changeset 2536:b66e00ca9ba9

Merge merge-requests '495' and '458' * github/merge-requests/495: Avoid c99 feature *phew* commented and simplified. Hopefully make it not crash It compiles! Remove all references to "IconData" update .gitignore Refactor: move view_file implementations to their own subdirectory. * github/merge-requests/458: Add the ability to use regular expressions for Pan View keyword filtering. Add pan filtering to all of the pan view modes Revamp pan view filtering to support different modes and grouping. Move filter code into pan-fiew-filter.{c,h} Adds a keyword filtering feature to Timeline PanView. Pull the search UI construction code out into a distinct function. Start moving pan view search code to its own module
author Klaus Ethgen <Klaus@Ethgen.de>
date Sat, 08 Jul 2017 10:24:19 +0100
parents 2f88376d1cf2 (current diff) 973e851c2ffa (diff)
children 85a4f55d2b33
files src/view_file_icon.c src/view_file_icon.h src/view_file_list.c src/view_file_list.h
diffstat 28 files changed, 5471 insertions(+), 4982 deletions(-) [+]
line wrap: on
line diff
--- a/.gitignore	Fri Jul 07 12:11:56 2017 +0100
+++ b/.gitignore	Sat Jul 08 10:24:19 2017 +0100
@@ -73,6 +73,10 @@
 /src/pan-view/.deps
 /src/pan-view/.dirstamp
 
+# /src/view_file/
+/src/view_file/.deps
+/src/view_file/.dirstamp
+
 /build-stamp
 /debian/geeqie*
 /debian/files
--- a/src/Makefile.am	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/Makefile.am	Sat Jul 08 10:24:19 2017 +0100
@@ -81,12 +81,14 @@
 	$(extra_ICONS)
 
 include $(srcdir)/pan-view/Makefile.am
+include $(srcdir)/view_file/Makefile.am
 
 bin_PROGRAMS = geeqie
 
 geeqie_SOURCES = \
 	$(module_SLIK)	\
 	$(module_pan_view)	\
+	$(module_view_file)	\
 	ClayRGB1998.icc \
 	ClayRGB1998_icc.h \
 	advanced_exif.c	\
@@ -251,10 +253,6 @@
 	view_dir_tree.h	\
 	view_file.c	\
 	view_file.h	\
-	view_file_list.c	\
-	view_file_list.h	\
-	view_file_icon.c	\
-	view_file_icon.h	\
 	window.c	\
 	window.h	\
 	lua.c		\
--- a/src/layout_image.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/layout_image.c	Sat Jul 08 10:24:19 2017 +0100
@@ -1070,7 +1070,6 @@
 	GtkTreePath *tpath;
 	FileData *fd_n;
 	GtkTreeIter iter;
-	IconData *id;
 
 	if (!lw || !lw->vf) return;
 
@@ -1089,8 +1088,7 @@
 		{
 		if (lw->vf->type == FILEVIEW_ICON)
 			{
-			id = work->data;
-			fd_n = id->fd;
+			fd_n = work->data;
 			work = work->next;
 			}
 		else
@@ -1120,7 +1118,6 @@
 	GtkTreePath *tpath;
 	FileData *fd_n;
 	GtkTreeIter iter;
-	IconData *id;
 
 	if (!lw || !lw->vf) return;
 
@@ -1139,8 +1136,7 @@
 		{
 		if (lw->vf->type == FILEVIEW_ICON)
 			{
-			id = work->data;
-			fd_n = id->fd;
+			fd_n = work->data;
 			work = work->next;
 			}
 		else
--- a/src/layout_util.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/layout_util.c	Sat Jul 08 10:24:19 2017 +0100
@@ -463,7 +463,6 @@
 	GtkTreePath *tpath;
 	FileData *fd_n;
 	GtkTreeIter iter;
-	IconData *id;
 	gchar *rotation;
 	gchar *command;
 	gint run_result;
@@ -489,8 +488,7 @@
 		{
 		if (lw->vf->type == FILEVIEW_ICON)
 			{
-			id = work->data;
-			fd_n = id->fd;
+			fd_n = work->data;
 			work = work->next;
 			}
 		else
--- a/src/pan-view/Makefile.am	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/Makefile.am	Sat Jul 08 10:24:19 2017 +0100
@@ -13,4 +13,9 @@
 	%D%/pan-util.c	\
 	%D%/pan-util.h	\
 	%D%/pan-view.c	\
-	%D%/pan-view.h
+	%D%/pan-view.h	\
+	%D%/pan-view-filter.c	\
+	%D%/pan-view-filter.h	\
+	%D%/pan-view-search.c	\
+	%D%/pan-view-search.h
+
--- a/src/pan-view/pan-calendar.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-calendar.c	Sat Jul 08 10:24:19 2017 +0100
@@ -26,6 +26,7 @@
 
 #include "pan-util.h"
 #include "pan-view.h"
+#include "pan-view-filter.h"
 #include "pixbuf_util.h"
 
 #define PAN_CAL_POPUP_COLOR 220, 220, 220
@@ -200,6 +201,7 @@
 	gint end_month = 0;
 
 	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+	pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
 	if (pw->cache_list && pw->exif_date_enable)
 		{
--- a/src/pan-view/pan-folder.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-folder.c	Sat Jul 08 10:24:19 2017 +0100
@@ -25,6 +25,7 @@
 
 #include "pan-item.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
 
 static void pan_flower_size(PanWindow *pw, gint *width, gint *height)
 {
@@ -242,6 +243,8 @@
 	f = filelist_sort(f, SORT_NAME, TRUE);
 	d = filelist_sort(d, SORT_NAME, TRUE);
 
+	pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
 	pi_box = pan_item_text_new(pw, x, y, dir_fd->path, PAN_TEXT_ATTR_NONE,
 				   PAN_TEXT_BORDER_SIZE,
 				   PAN_TEXT_COLOR, 255);
@@ -386,6 +389,8 @@
 	f = filelist_sort(f, SORT_NAME, TRUE);
 	d = filelist_sort(d, SORT_NAME, TRUE);
 
+	pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
 	*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,
--- a/src/pan-view/pan-grid.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-grid.c	Sat Jul 08 10:24:19 2017 +0100
@@ -25,6 +25,7 @@
 
 #include "pan-item.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
 
 void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
 {
@@ -35,6 +36,7 @@
 	gint next_y;
 
 	list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
+	pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
 	grid_size = (gint)sqrt((gdouble)g_list_length(list));
 	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
--- a/src/pan-view/pan-timeline.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-timeline.c	Sat Jul 08 10:24:19 2017 +0100
@@ -24,13 +24,14 @@
 #include "pan-item.h"
 #include "pan-util.h"
 #include "pan-view.h"
+#include "pan-view-filter.h"
 
 void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
 {
 	GList *list;
 	GList *work;
 	gint x, y;
-	time_t tc;
+	time_t group_start_date;
 	gint total;
 	gint count;
 	PanItem *pi_month = NULL;
@@ -41,6 +42,7 @@
 	gint y_height;
 
 	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+	pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
 	if (pw->cache_list && pw->exif_date_enable)
 		{
@@ -61,7 +63,8 @@
 	day_start = month_start;
 	x_width = 0;
 	y_height = 0;
-	tc = 0;
+	group_start_date = 0;
+	// total and count are used to enforce a stride of PAN_GROUP_MAX thumbs.
 	total = 0;
 	count = 0;
 	work = list;
@@ -73,13 +76,15 @@
 		fd = work->data;
 		work = work->next;
 
-		if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
+		if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_DAY))
 			{
+			// FD starts a new day group.
 			GList *needle;
 			gchar *buf;
 
-			if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_MONTH))
+			if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_MONTH))
 				{
+				// FD starts a new month group.
 				pi_day = NULL;
 
 				if (pi_month)
@@ -114,7 +119,7 @@
 
 			if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
 
-			tc = fd->date;
+			group_start_date = fd->date;
 			total = 1;
 			count = 0;
 
@@ -124,7 +129,7 @@
 				FileData *nfd;
 
 				nfd = needle->data;
-				if (pan_date_compare(nfd->date, tc, PAN_DATE_LENGTH_DAY))
+				if (pan_date_compare(nfd->date, group_start_date, PAN_DATE_LENGTH_DAY))
 					{
 					needle = needle->next;
 					total++;
--- a/src/pan-view/pan-types.h	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-types.h	Sat Jul 08 10:24:19 2017 +0100
@@ -169,6 +169,19 @@
 	gboolean queued;
 };
 
+typedef struct _PanViewSearchUi PanViewSearchUi;
+struct _PanViewSearchUi
+{
+	GtkWidget *search_box;
+	GtkWidget *search_entry;
+	GtkWidget *search_label;
+	GtkWidget *search_button;
+	GtkWidget *search_button_arrow;
+};
+
+// Defined in pan-view-filter.h
+typedef struct _PanViewFilterUi PanViewFilterUi;
+
 typedef struct _PanWindow PanWindow;
 struct _PanWindow
 {
@@ -182,11 +195,8 @@
 	GtkWidget *label_message;
 	GtkWidget *label_zoom;
 
-	GtkWidget *search_box;
-	GtkWidget *search_entry;
-	GtkWidget *search_label;
-	GtkWidget *search_button;
-	GtkWidget *search_button_arrow;
+	PanViewSearchUi *search_ui;
+	PanViewFilterUi *filter_ui;
 
 	GtkWidget *date_button;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-view-filter.c	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,353 @@
+/*
+ * 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-filter.h"
+
+#include "image.h"
+#include "metadata.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_fileops.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw)
+{
+	PanViewFilterUi *ui = g_new0(PanViewFilterUi, 1);
+	GtkWidget *combo;
+	GtkWidget *hbox;
+
+	/* Since we're using the GHashTable as a HashSet (in which key and value pointers
+	 * are always identical), specifying key _and_ value destructor callbacks will
+	 * cause a double-free.
+	 */
+	{
+		GtkTreeIter iter;
+		ui->filter_mode_model = gtk_list_store_new(3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
+		gtk_list_store_append(ui->filter_mode_model, &iter);
+		gtk_list_store_set(ui->filter_mode_model, &iter,
+				   0, PAN_VIEW_FILTER_REQUIRE, 1, _("Require"), 2, _("R"), -1);
+		gtk_list_store_append(ui->filter_mode_model, &iter);
+		gtk_list_store_set(ui->filter_mode_model, &iter,
+				   0, PAN_VIEW_FILTER_EXCLUDE, 1, _("Exclude"), 2, _("E"), -1);
+		gtk_list_store_append(ui->filter_mode_model, &iter);
+		gtk_list_store_set(ui->filter_mode_model, &iter,
+				   0, PAN_VIEW_FILTER_INCLUDE, 1, _("Include"), 2, _("I"), -1);
+		gtk_list_store_append(ui->filter_mode_model, &iter);
+		gtk_list_store_set(ui->filter_mode_model, &iter,
+				   0, PAN_VIEW_FILTER_GROUP, 1, _("Group"), 2, _("G"), -1);
+
+		ui->filter_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ui->filter_mode_model));
+		gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(ui->filter_mode_combo), FALSE);
+		gtk_combo_box_set_active(GTK_COMBO_BOX(ui->filter_mode_combo), 0);
+
+		GtkCellRenderer *render = gtk_cell_renderer_text_new();
+		gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, TRUE);
+		gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, "text", 1, NULL);
+	}
+
+	// Build the actual filter UI.
+	ui->filter_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+	pref_spacer(ui->filter_box, 0);
+	pref_label_new(ui->filter_box, _("Keyword Filter:"));
+
+	gtk_box_pack_start(GTK_BOX(ui->filter_box), ui->filter_mode_combo, TRUE, TRUE, 0);
+	gtk_widget_show(ui->filter_mode_combo);
+
+	hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+	gtk_box_pack_start(GTK_BOX(ui->filter_box), hbox, TRUE, TRUE, 0);
+	gtk_widget_show(hbox);
+
+	combo = tab_completion_new_with_history(&ui->filter_entry, "", "pan_view_filter", -1,
+						pan_filter_activate_cb, pw);
+	gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+	gtk_widget_show(combo);
+
+	// TODO(xsdg): Figure out whether it's useful to keep this label around.
+	ui->filter_label = gtk_label_new("");
+	//gtk_box_pack_start(GTK_BOX(hbox), ui->filter_label, FALSE, FALSE, 0);
+	//gtk_widget_show(ui->filter_label);
+
+	ui->filter_kw_hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+	gtk_box_pack_start(GTK_BOX(hbox), ui->filter_kw_hbox, TRUE, TRUE, 0);
+	gtk_widget_show(ui->filter_kw_hbox);
+
+	// Build the spin-button to show/hide the filter UI.
+	ui->filter_button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(ui->filter_button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click(GTK_BUTTON(ui->filter_button), FALSE);
+	hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+	gtk_container_add(GTK_CONTAINER(ui->filter_button), hbox);
+	gtk_widget_show(hbox);
+	ui->filter_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+	gtk_box_pack_start(GTK_BOX(hbox), ui->filter_button_arrow, FALSE, FALSE, 0);
+	gtk_widget_show(ui->filter_button_arrow);
+	pref_label_new(hbox, _("Filter"));
+
+	g_signal_connect(G_OBJECT(ui->filter_button), "clicked",
+			 G_CALLBACK(pan_filter_toggle_cb), pw);
+
+	return ui;
+}
+
+void pan_filter_ui_destroy(PanViewFilterUi **ui_ptr)
+{
+	if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+	// Note that g_clear_pointer handles already-NULL pointers.
+	//g_clear_pointer(&(*ui_ptr)->filter_kw_table, g_hash_table_destroy);
+
+	g_free(*ui_ptr);
+	*ui_ptr = NULL;
+}
+
+static void pan_filter_status(PanWindow *pw, const gchar *text)
+{
+	gtk_label_set_text(GTK_LABEL(pw->filter_ui->filter_label), (text) ? text : "");
+}
+
+static void pan_filter_kw_button_cb(GtkButton *widget, gpointer data)
+{
+	PanFilterCallbackState *cb_state = data;
+	PanWindow *pw = cb_state->pw;
+	PanViewFilterUi *ui = pw->filter_ui;
+
+	// TODO(xsdg): Fix filter element pointed object memory leak.
+	ui->filter_elements = g_list_delete_link(ui->filter_elements, cb_state->filter_element);
+	gtk_widget_destroy(GTK_WIDGET(widget));
+	g_free(cb_state);
+
+	pan_filter_status(pw, _("Removed keyword…"));
+	pan_layout_update(pw);
+}
+
+void pan_filter_activate_cb(const gchar *text, gpointer data)
+{
+	GtkWidget *kw_button;
+	PanWindow *pw = data;
+	PanViewFilterUi *ui = pw->filter_ui;
+	GtkTreeIter iter;
+
+	if (!text) return;
+
+	// Get all relevant state and reset UI.
+	gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ui->filter_mode_combo), &iter);
+	gtk_entry_set_text(GTK_ENTRY(ui->filter_entry), "");
+	tab_completion_append_to_history(ui->filter_entry, text);
+
+	// Add new filter element.
+	PanViewFilterElement *element = g_new0(PanViewFilterElement, 1);
+	gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 0, &element->mode, -1);
+	element->keyword = g_strdup(text);
+	if (g_strcmp0(text, g_regex_escape_string(text, -1)))
+		{
+		// It's an actual regex, so compile
+		element->kw_regex = g_regex_new(text, G_REGEX_ANCHORED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_ANCHORED, NULL);
+		}
+	ui->filter_elements = g_list_append(ui->filter_elements, element);
+
+	// Get the short version of the mode value.
+	gchar *short_mode;
+	gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 2, &short_mode, -1);
+
+	// Create the button.
+	// TODO(xsdg): Use MVC so that the button list is an actual representation of the GList
+	gchar *label = g_strdup_printf("(%s) %s", short_mode, text);
+	kw_button = gtk_button_new_with_label(label);
+	g_clear_pointer(&label, g_free);
+
+	gtk_box_pack_start(GTK_BOX(ui->filter_kw_hbox), kw_button, FALSE, FALSE, 0);
+	gtk_widget_show(kw_button);
+
+	PanFilterCallbackState *cb_state = g_new0(PanFilterCallbackState, 1);
+	cb_state->pw = pw;
+	cb_state->filter_element = g_list_last(ui->filter_elements);
+
+	g_signal_connect(G_OBJECT(kw_button), "clicked",
+			 G_CALLBACK(pan_filter_kw_button_cb), cb_state);
+
+	pan_layout_update(pw);
+}
+
+void pan_filter_activate(PanWindow *pw)
+{
+	gchar *text;
+
+	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->filter_ui->filter_entry)));
+	pan_filter_activate_cb(text, pw);
+	g_free(text);
+}
+
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data)
+{
+	PanWindow *pw = data;
+	PanViewFilterUi *ui = pw->filter_ui;
+	gboolean visible;
+
+	visible = gtk_widget_get_visible(ui->filter_box);
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+	if (visible)
+		{
+		gtk_widget_hide(ui->filter_box);
+		gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+		}
+	else
+		{
+		gtk_widget_show(ui->filter_box);
+		gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+		gtk_widget_grab_focus(ui->filter_entry);
+		}
+}
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable)
+{
+	PanViewFilterUi *ui = pw->filter_ui;
+	if (pw->fs) return;
+
+	if (enable)
+		{
+		if (gtk_widget_get_visible(ui->filter_box))
+			{
+			gtk_widget_grab_focus(ui->filter_entry);
+			}
+		else
+			{
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), TRUE);
+			}
+		}
+	else
+		{
+		if (gtk_widget_get_visible(ui->filter_entry))
+			{
+			if (gtk_widget_has_focus(ui->filter_entry))
+				{
+				gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+				}
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), FALSE);
+			}
+		}
+}
+
+static gboolean pan_view_list_contains_kw_pattern(GList *haystack, PanViewFilterElement *filter, gchar **found_kw)
+{
+	if (filter->kw_regex)
+		{
+		// regex compile succeeded; attempt regex match.
+		GList *work = g_list_first(haystack);
+		while (work)
+			{
+			gchar *keyword = work->data;
+			work = work->next;
+			if (g_regex_match(filter->kw_regex, keyword, 0x0, NULL))
+				{
+				if (found_kw) *found_kw = keyword;
+				return TRUE;
+				}
+			}
+		return FALSE;
+		}
+	else
+		{
+		// regex compile failed; fall back to exact string match.
+		GList *found_elem = g_list_find_custom(haystack, filter->keyword, (GCompareFunc)g_strcmp0);
+		if (found_elem && found_kw) *found_kw = found_elem->data;
+		return !!found_elem;
+		}
+}
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements)
+{
+	GList *work;
+	gboolean modified = FALSE;
+	GHashTable *seen_kw_table = NULL;
+
+	if (!fd_list || !*fd_list || !filter_elements) return modified;
+
+	// seen_kw_table is only valid in this scope, so don't take ownership of any strings.
+	seen_kw_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+	work = *fd_list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		GList *last_work = work;
+		work = work->next;
+
+		// TODO(xsdg): OPTIMIZATION Do the search inside of metadata.c to avoid a
+		// bunch of string list copies.
+		GList *img_keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
+
+		// TODO(xsdg): OPTIMIZATION Determine a heuristic for when to linear-search the
+		// keywords list, and when to build a hash table for the image's keywords.
+		gboolean should_reject = FALSE;
+		gchar *group_kw = NULL;
+		GList *filter_element = filter_elements;
+		while (filter_element)
+			{
+			PanViewFilterElement *filter = filter_element->data;
+			filter_element = filter_element->next;
+			gchar *found_kw = NULL;
+			gboolean has_kw = pan_view_list_contains_kw_pattern(img_keywords, filter, &found_kw);
+
+			switch (filter->mode)
+				{
+				case PAN_VIEW_FILTER_REQUIRE:
+					should_reject |= !has_kw;
+					break;
+				case PAN_VIEW_FILTER_EXCLUDE:
+					should_reject |= has_kw;
+					break;
+				case PAN_VIEW_FILTER_INCLUDE:
+					if (has_kw) should_reject = FALSE;
+					break;
+				case PAN_VIEW_FILTER_GROUP:
+					if (has_kw)
+						{
+						if (g_hash_table_contains(seen_kw_table, found_kw))
+							{
+							should_reject = TRUE;
+							}
+						else if (group_kw == NULL)
+							{
+							group_kw = found_kw;
+							}
+						}
+					break;
+				}
+			}
+
+		if (!should_reject && group_kw != NULL) g_hash_table_add(seen_kw_table, group_kw);
+
+		group_kw = NULL;  // group_kw references an item from img_keywords.
+		string_list_free(img_keywords);
+
+		if (should_reject)
+			{
+			*fd_list = g_list_delete_link(*fd_list, last_work);
+			modified = TRUE;
+			}
+		}
+
+	g_hash_table_destroy(seen_kw_table);
+	return modified;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-view-filter.h	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,77 @@
+/*
+ * 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_FILTER_H
+#define PAN_VIEW_PAN_VIEW_FILTER_H
+
+#include "main.h"
+#include "pan-types.h"
+
+typedef enum {
+	PAN_VIEW_FILTER_REQUIRE,
+	PAN_VIEW_FILTER_EXCLUDE,
+	PAN_VIEW_FILTER_INCLUDE,
+	PAN_VIEW_FILTER_GROUP
+} PanViewFilterMode;
+
+typedef struct _PanViewFilterElement PanViewFilterElement;
+struct _PanViewFilterElement
+{
+	PanViewFilterMode mode;
+	gchar *keyword;
+	GRegex *kw_regex;
+};
+
+typedef struct _PanFilterCallbackState PanFilterCallbackState;
+struct _PanFilterCallbackState
+{
+	PanWindow *pw;
+	GList *filter_element;
+};
+
+struct _PanViewFilterUi
+{
+	GtkWidget *filter_box;
+	GtkWidget *filter_entry;
+	GtkWidget *filter_label;
+	GtkWidget *filter_button;
+	GtkWidget *filter_button_arrow;
+	GtkWidget *filter_kw_hbox;
+	GtkListStore *filter_mode_model;
+	GtkWidget *filter_mode_combo;
+	GList *filter_elements;  // List of PanViewFilterElement.
+};
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_filter_activate(PanWindow *pw);
+void pan_filter_activate_cb(const gchar *text, gpointer data);
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewFilterUi instance and returns it.
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewFilterUi and sets the pointer to NULL.
+void pan_filter_ui_destroy(PanViewFilterUi **ui);
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements);
+
+#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-view-search.c	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,468 @@
+/*
+ * 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-search.h"
+
+#include "image.h"
+#include "pan-calendar.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw)
+{
+	PanViewSearchUi *ui = g_new0(PanViewSearchUi, 1);
+	GtkWidget *combo;
+	GtkWidget *hbox;
+
+	// Build the actual search UI.
+	ui->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+	pref_spacer(ui->search_box, 0);
+	pref_label_new(ui->search_box, _("Find:"));
+
+	hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+	gtk_box_pack_start(GTK_BOX(ui->search_box), hbox, TRUE, TRUE, 0);
+	gtk_widget_show(hbox);
+
+	combo = tab_completion_new_with_history(&ui->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);
+
+	ui->search_label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(hbox), ui->search_label, TRUE, TRUE, 0);
+	gtk_widget_show(ui->search_label);
+
+	// Build the spin-button to show/hide the search UI.
+	ui->search_button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(ui->search_button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click(GTK_BUTTON(ui->search_button), FALSE);
+	hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+	gtk_container_add(GTK_CONTAINER(ui->search_button), hbox);
+	gtk_widget_show(hbox);
+	ui->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+	gtk_box_pack_start(GTK_BOX(hbox), ui->search_button_arrow, FALSE, FALSE, 0);
+	gtk_widget_show(ui->search_button_arrow);
+	pref_label_new(hbox, _("Find"));
+
+	g_signal_connect(G_OBJECT(ui->search_button), "clicked",
+			 G_CALLBACK(pan_search_toggle_cb), pw);
+
+	return ui;
+}
+
+void pan_search_ui_destroy(PanViewSearchUi **ui_ptr)
+{
+	if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+	g_free(*ui_ptr);
+	*ui_ptr = NULL;
+}
+
+static void pan_search_status(PanWindow *pw, const gchar *text)
+{
+	gtk_label_set_text(GTK_LABEL(pw->search_ui->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;
+}
+
+void pan_search_activate_cb(const gchar *text, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (!text) return;
+
+	tab_completion_append_to_history(pw->search_ui->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"));
+}
+
+void pan_search_activate(PanWindow *pw)
+{
+	gchar *text;
+
+	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_ui->search_entry)));
+	pan_search_activate_cb(text, pw);
+	g_free(text);
+}
+
+void pan_search_toggle_cb(GtkWidget *button, gpointer data)
+{
+	PanWindow *pw = data;
+	PanViewSearchUi *ui = pw->search_ui;
+	gboolean visible;
+
+	visible = gtk_widget_get_visible(ui->search_box);
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+	if (visible)
+		{
+		gtk_widget_hide(ui->search_box);
+		gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+		}
+	else
+		{
+		gtk_widget_show(ui->search_box);
+		gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+		gtk_widget_grab_focus(ui->search_entry);
+		}
+}
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
+{
+	PanViewSearchUi *ui = pw->search_ui;
+	if (pw->fs) return;
+
+	if (enable)
+		{
+		if (gtk_widget_get_visible(ui->search_box))
+			{
+			gtk_widget_grab_focus(ui->search_entry);
+			}
+		else
+			{
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), TRUE);
+			}
+		}
+	else
+		{
+		if (gtk_widget_get_visible(ui->search_entry))
+			{
+			if (gtk_widget_has_focus(ui->search_entry))
+				{
+				gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+				}
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), FALSE);
+			}
+		}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-view-search.h	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,40 @@
+/*
+ * 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_SEARCH_H
+#define PAN_VIEW_PAN_VIEW_SEARCH_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_search_activate(PanWindow *pw);
+void pan_search_activate_cb(const gchar *text, gpointer data);
+void pan_search_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewSearchUi instance and returns it.
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewSearchUi and sets the pointer to NULL.
+void pan_search_ui_destroy(PanViewSearchUi **ui);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-view/pan-view.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-view.c	Sat Jul 08 10:24:19 2017 +0100
@@ -38,6 +38,8 @@
 #include "pan-item.h"
 #include "pan-timeline.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
+#include "pan-view-search.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
 #include "thumb.h"
@@ -78,9 +80,6 @@
 
 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);
@@ -1072,7 +1071,7 @@
 		}
 }
 
-static void pan_layout_update(PanWindow *pw)
+void pan_layout_update(PanWindow *pw)
 {
 	pan_window_message(pw, _("Sorting images..."));
 	pan_layout_update_idle(pw);
@@ -1134,7 +1133,8 @@
 	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));
+		    gtk_widget_has_focus(pw->search_ui->search_entry) ||
+		    gtk_widget_has_focus(pw->filter_ui->filter_entry));
 
 	if (focused)
 		{
@@ -1248,6 +1248,7 @@
 
 		if (stop_signal) return stop_signal;
 
+		// Don't steal characters from entry boxes.
 		if (!on_entry)
 			{
 			stop_signal = TRUE;
@@ -1326,7 +1327,7 @@
 }
 
 
-static void pan_info_update(PanWindow *pw, PanItem *pi)
+void pan_info_update(PanWindow *pw, PanItem *pi)
 {
 	PanTextAlignment *ta;
 	PanItem *pbox;
@@ -1452,399 +1453,6 @@
 
 /*
  *-----------------------------------------------------------------------------
- * 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
  *-----------------------------------------------------------------------------
  */
@@ -2129,6 +1737,8 @@
 		}
 
 	pan_fullscreen_toggle(pw, TRUE);
+	pan_search_ui_destroy(&pw->search_ui);
+	pan_filter_ui_destroy(&pw->filter_ui);
 	gtk_widget_destroy(pw->window);
 
 	pan_window_items_free(pw);
@@ -2274,24 +1884,12 @@
 
 	/* 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);
+	pw->search_ui = pan_search_ui_new(pw);
+	gtk_box_pack_start(GTK_BOX(vbox), pw->search_ui->search_box, FALSE, FALSE, 2);
+
+    /* filter bar */
+    pw->filter_ui = pan_filter_ui_new(pw);
+    gtk_box_pack_start(GTK_BOX(vbox), pw->filter_ui->filter_box, FALSE, FALSE, 2);
 
 	/* status bar */
 
@@ -2320,21 +1918,13 @@
 	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);
+	// Add the "Find" button to the status bar area.
+	gtk_box_pack_end(GTK_BOX(box), pw->search_ui->search_button, FALSE, FALSE, 0);
+	gtk_widget_show(pw->search_ui->search_button);
+
+	// Add the "Filter" button to the status bar area.
+	gtk_box_pack_end(GTK_BOX(box), pw->filter_ui->filter_button, FALSE, FALSE, 0);
+	gtk_widget_show(pw->filter_ui->filter_button);
 
 	g_signal_connect(G_OBJECT(pw->window), "delete_event",
 			 G_CALLBACK(pan_window_delete_cb), pw);
--- a/src/pan-view/pan-view.h	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/pan-view/pan-view.h	Sat Jul 08 10:24:19 2017 +0100
@@ -25,6 +25,7 @@
 #include "main.h"
 #include "pan-types.h"
 
+void pan_layout_update(PanWindow *pw);
 GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
 void pan_layout_resize(PanWindow *pw);
 
@@ -32,6 +33,7 @@
 
 GList *pan_cache_sort(GList *list, SortType method, gboolean ascend);
 
+void pan_info_update(PanWindow *pw, PanItem *pi);
 
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/typedefs.h	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/typedefs.h	Sat Jul 08 10:24:19 2017 +0100
@@ -252,6 +252,13 @@
 
 #define MAX_SPLIT_IMAGES 4
 
+typedef enum {
+	SELECTION_NONE		= 0,
+	SELECTION_SELECTED	= 1 << 0,
+	SELECTION_PRELIGHT	= 1 << 1,
+	SELECTION_FOCUS		= 1 << 2
+} SelectionType;
+
 typedef struct _ImageLoader ImageLoader;
 typedef struct _ThumbLoader ThumbLoader;
 
@@ -575,6 +582,8 @@
 	GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values
 	GList *cached_metadata;
 	gint rating;
+
+	SelectionType selected;  // Used by view_file_icon.
 };
 
 struct _LayoutOptions
@@ -823,6 +832,7 @@
 struct _ViewFile
 {
 	FileViewType type;
+	// TODO(xsdg): Turn this into a union (see VFLIST and VFICON from view_file.h).
 	gpointer info;
 
 	GtkWidget *widget;
@@ -876,20 +886,6 @@
 	guint select_idle_id; /* event source id */
 };
 
-typedef enum {
-	SELECTION_NONE		= 0,
-	SELECTION_SELECTED	= 1 << 0,
-	SELECTION_PRELIGHT	= 1 << 1,
-	SELECTION_FOCUS		= 1 << 2
-} SelectionType;
-
-typedef struct _IconData IconData;
-struct _IconData
-{
-	SelectionType selected;
-	FileData *fd;
-};
-
 struct _ViewFileInfoIcon
 {
 	/* table stuff */
@@ -897,15 +893,15 @@
 	gint rows;
 
 	GList *selection;
-	struct _IconData *prev_selection;
+	FileData *prev_selection;
 
 	GtkWidget *tip_window;
 	guint tip_delay_id; /* event source id */
-	struct _IconData *tip_id;
+	FileData *tip_fd;
 
-	struct _IconData *click_id;
+	FileData *click_fd;
 
-	struct _IconData *focus_id;
+	FileData *focus_fd;
 	gint focus_row;
 	gint focus_column;
 
--- a/src/view_file.c	Fri Jul 07 12:11:56 2017 +0100
+++ b/src/view_file.c	Sat Jul 08 10:24:19 2017 +0100
@@ -31,8 +31,8 @@
 #include "ui_menu.h"
 #include "ui_fileops.h"
 #include "utilops.h"
-#include "view_file_list.h"
-#include "view_file_icon.h"
+#include "view_file/view_file_list.h"
+#include "view_file/view_file_icon.h"
 
 /*
  *-----------------------------------------------------------------------------
@@ -555,7 +555,7 @@
 		active = (VFLIST(vf)->click_fd != NULL);
 		break;
 	case FILEVIEW_ICON:
-		active = (VFICON(vf)->click_id != NULL);
+		active = (VFICON(vf)->click_fd != NULL);
 		break;
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/Makefile.am	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,5 @@
+module_view_file = \
+	%D%/view_file_icon.c	\
+	%D%/view_file_icon.h	\
+	%D%/view_file_list.c	\
+	%D%/view_file_list.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/view_file_common.c	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,37 @@
+/*
+ *  This file is a part of Geeqie project (http://www.geeqie.org/).
+ *  Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ *  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.
+ */
+
+guint vfcommon_count(ViewFile *vf, gint64 *bytes)
+{
+	if (bytes)
+		{
+		gint64 b = 0;
+		GList *work;
+
+		work = vf->list;
+		while (work)
+			{
+			FileData *fd = work->data;
+			work = work->next;
+
+			b += fd->size;
+			}
+
+		*bytes = b;
+		}
+
+	return g_list_length(vf->list);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/view_file_icon.c	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,2194 @@
+/*
+ * 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 "view_file_icon.h"
+
+#include "bar.h"
+#include "cellrenderericon.h"
+#include "collect.h"
+#include "collect-io.h"
+#include "collect-table.h"
+#include "dnd.h"
+#include "editors.h"
+#include "img-view.h"
+#include "filedata.h"
+#include "layout.h"
+#include "layout_image.h"
+#include "menu.h"
+#include "metadata.h"
+#include "thumb.h"
+#include "utilops.h"
+#include "ui_fileops.h"
+#include "ui_menu.h"
+#include "ui_tree_edit.h"
+#include "uri_utils.h"
+#include "view_file.h"
+
+#include <gdk/gdkkeysyms.h> /* for keyboard values */
+
+
+/* between these, the icon width is increased by thumb_max_width / 2 */
+#define THUMB_MIN_ICON_WIDTH 128
+#define THUMB_MAX_ICON_WIDTH 150
+
+#define VFICON_MAX_COLUMNS 32
+#define THUMB_BORDER_PADDING 2
+
+#define VFICON_TIP_DELAY 500
+
+enum {
+	FILE_COLUMN_POINTER = 0,
+	FILE_COLUMN_COUNT
+};
+
+static void vficon_toggle_filenames(ViewFile *vf);
+static void vficon_selection_remove(ViewFile *vf, FileData *id, SelectionType mask, GtkTreeIter *iter);
+static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative);
+static void vficon_set_focus(ViewFile *vf, FileData *fd);
+static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force);
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * pop-up menu
+ *-----------------------------------------------------------------------------
+ */
+
+GList *vficon_selection_get_one(ViewFile *vf, FileData *fd)
+{
+	return g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
+}
+
+GList *vficon_pop_menu_file_list(ViewFile *vf)
+{
+	if (!VFICON(vf)->click_fd) return NULL;
+
+	if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
+		{
+		return vf_selection_get_list(vf);
+		}
+
+	return vficon_selection_get_one(vf, VFICON(vf)->click_fd);
+}
+
+void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	if (!VFICON(vf)->click_fd) return;
+
+	if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
+		{
+		GList *list;
+
+		list = vf_selection_get_list(vf);
+		view_window_new_from_list(list);
+		filelist_free(list);
+		}
+	else
+		{
+		view_window_new(VFICON(vf)->click_fd);
+		}
+}
+
+void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview);
+}
+
+void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vficon_toggle_filenames(vf);
+}
+
+void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vf_refresh(vf);
+}
+
+void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+	vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
+	VFICON(vf)->click_fd = NULL;
+	vf->popup = NULL;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * signals
+ *-------------------------------------------------------------------
+ */
+
+static void vficon_send_layout_select(ViewFile *vf, FileData *fd)
+{
+	FileData *read_ahead_fd = NULL;
+	FileData *sel_fd;
+	FileData *cur_fd;
+
+	if (!vf->layout || !fd) return;
+
+	sel_fd = fd;
+
+	cur_fd = layout_image_get_fd(vf->layout);
+	if (sel_fd == cur_fd) return; /* no change */
+
+	if (options->image.enable_read_ahead)
+		{
+		gint row;
+
+		row = g_list_index(vf->list, fd);
+		if (row > vficon_index_by_fd(vf, cur_fd) &&
+		    (guint) (row + 1) < vf_count(vf, NULL))
+			{
+			read_ahead_fd = vf_index_get_data(vf, row + 1);
+			}
+		else if (row > 0)
+			{
+			read_ahead_fd = vf_index_get_data(vf, row - 1);
+			}
+		}
+
+	layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
+}
+
+static void vficon_toggle_filenames(ViewFile *vf)
+{
+	GtkAllocation allocation;
+	VFICON(vf)->show_text = !VFICON(vf)->show_text;
+	options->show_icon_names = VFICON(vf)->show_text;
+
+	gtk_widget_get_allocation(vf->listview, &allocation);
+	vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
+}
+
+static gint vficon_get_icon_width(ViewFile *vf)
+{
+	gint width;
+
+	if (!VFICON(vf)->show_text) return options->thumbnails.max_width;
+
+	width = options->thumbnails.max_width + options->thumbnails.max_width / 2;
+	if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
+	if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width;
+
+	return width;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * misc utils
+ *-------------------------------------------------------------------
+ */
+
+static gboolean vficon_find_position(ViewFile *vf, FileData *fd, gint *row, gint *col)
+{
+	gint n;
+
+	n = g_list_index(vf->list, fd);
+
+	if (n < 0) return FALSE;
+
+	*row = n / VFICON(vf)->columns;
+	*col = n - (*row * VFICON(vf)->columns);
+
+	return TRUE;
+}
+
+static gboolean vficon_find_iter(ViewFile *vf, FileData *fd, GtkTreeIter *iter, gint *column)
+{
+	GtkTreeModel *store;
+	gint row, col;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	if (!vficon_find_position(vf, fd, &row, &col)) return FALSE;
+	if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
+	if (column) *column = col;
+
+	return TRUE;
+}
+
+static FileData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
+{
+	GtkTreeModel *store;
+	GtkTreeIter p;
+
+	if (row < 0 || col < 0) return NULL;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	if (gtk_tree_model_iter_nth_child(store, &p, NULL, row))
+		{
+		GList *list;
+
+		gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1);
+		if (!list) return NULL;
+
+		if (iter) *iter = p;
+
+		return g_list_nth_data(list, col);
+		}
+
+	return NULL;
+}
+
+static FileData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
+{
+	GtkTreePath *tpath;
+	GtkTreeViewColumn *column;
+
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
+					  &tpath, &column, NULL, NULL))
+		{
+		GtkTreeModel *store;
+		GtkTreeIter row;
+		GList *list;
+		gint n;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		gtk_tree_model_get_iter(store, &row, tpath);
+		gtk_tree_path_free(tpath);
+
+		gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
+
+		n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number"));
+		if (list)
+			{
+			if (iter) *iter = row;
+			return g_list_nth_data(list, n);
+			}
+		}
+
+	return NULL;
+}
+
+static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeModel *store;
+	GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+	GtkTreeIter row;
+	gint column;
+	GList *list;
+	guint toggled_mark;
+	FileData *fd;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	if (!path || !gtk_tree_model_get_iter(store, &row, path)) return;
+
+	gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
+
+	column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number"));
+	g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL);
+
+	fd = g_list_nth_data(list, column);
+	if (fd)
+		{
+		file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark));
+		}
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * tooltip type window
+ *-------------------------------------------------------------------
+ */
+
+static void tip_show(ViewFile *vf)
+{
+	GtkWidget *label;
+	gint x, y;
+#if GTK_CHECK_VERSION(3,0,0)
+	GdkDisplay *display;
+	GdkDeviceManager *device_manager;
+	GdkDevice *device;
+#endif
+
+	if (VFICON(vf)->tip_window) return;
+
+#if GTK_CHECK_VERSION(3,0,0)
+	device_manager = gdk_display_get_device_manager(gdk_window_get_display(
+						gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview))));
+	device = gdk_device_manager_get_client_pointer(device_manager);
+	gdk_window_get_device_position(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)),
+						device, &x, &y, NULL);
+#else
+	gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL);
+#endif
+
+	VFICON(vf)->tip_fd = vficon_find_data_by_coord(vf, x, y, NULL);
+	if (!VFICON(vf)->tip_fd) return;
+
+	VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
+	gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE);
+	gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2);
+
+	label = gtk_label_new(VFICON(vf)->tip_fd->name);
+
+	g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label);
+	gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label);
+	gtk_widget_show(label);
+
+#if GTK_CHECK_VERSION(3,0,0)
+	display = gdk_display_get_default();
+	device_manager = gdk_display_get_device_manager(display);
+	device = gdk_device_manager_get_client_pointer(device_manager);
+	gdk_device_get_position(device, NULL, &x, &y);
+#else
+	gdk_window_get_pointer(NULL, &x, &y, NULL);
+#endif
+
+	if (!gtk_widget_get_realized(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window);
+	gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
+	gtk_widget_show(VFICON(vf)->tip_window);
+}
+
+static void tip_hide(ViewFile *vf)
+{
+	if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window);
+	VFICON(vf)->tip_window = NULL;
+}
+
+static gboolean tip_schedule_cb(gpointer data)
+{
+	ViewFile *vf = data;
+	GtkWidget *window;
+
+	if (!VFICON(vf)->tip_delay_id) return FALSE;
+
+	window = gtk_widget_get_toplevel(vf->listview);
+
+	if (gtk_widget_get_sensitive(window) &&
+	    gtk_window_has_toplevel_focus(GTK_WINDOW(window)))
+		{
+		tip_show(vf);
+		}
+
+	VFICON(vf)->tip_delay_id = 0;
+	return FALSE;
+}
+
+static void tip_schedule(ViewFile *vf)
+{
+	tip_hide(vf);
+
+	if (VFICON(vf)->tip_delay_id)
+		{
+		g_source_remove(VFICON(vf)->tip_delay_id);
+		VFICON(vf)->tip_delay_id = 0;
+		}
+
+	if (!VFICON(vf)->show_text)
+		{
+		VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf);
+		}
+}
+
+static void tip_unschedule(ViewFile *vf)
+{
+	tip_hide(vf);
+
+	if (VFICON(vf)->tip_delay_id)
+		{
+		g_source_remove(VFICON(vf)->tip_delay_id);
+		VFICON(vf)->tip_delay_id = 0;
+		}
+}
+
+static void tip_update(ViewFile *vf, FileData *fd)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+	GdkDisplay *display = gdk_display_get_default();
+	GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
+	GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager);
+#endif
+
+	if (VFICON(vf)->tip_window)
+		{
+		gint x, y;
+
+#if GTK_CHECK_VERSION(3,0,0)
+		gdk_device_get_position(device, NULL, &x, &y);
+#else
+		gdk_window_get_pointer(NULL, &x, &y, NULL);
+#endif
+		gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
+
+		if (fd != VFICON(vf)->tip_fd)
+			{
+			GtkWidget *label;
+
+			VFICON(vf)->tip_fd = fd;
+
+			if (!VFICON(vf)->tip_fd)
+				{
+				tip_hide(vf);
+				tip_schedule(vf);
+				return;
+				}
+
+			label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label");
+			gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_fd->name);
+			}
+		}
+	else
+		{
+		tip_schedule(vf);
+		}
+}
+
+/*
+ *-------------------------------------------------------------------
+ * dnd
+ *-------------------------------------------------------------------
+ */
+
+static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
+			   GtkSelectionData *selection_data, guint info,
+			   guint time, gpointer data)
+{
+	ViewFile *vf = data;
+	GList *list = NULL;
+
+	if (!VFICON(vf)->click_fd) return;
+
+	if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
+		{
+		list = vf_selection_get_list(vf);
+		}
+	else
+		{
+		list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_fd));
+		}
+
+	if (!list) return;
+	uri_selection_data_set_uris_from_filelist(selection_data, list);
+	filelist_free(list);
+}
+
+static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
+				      int x, int y, GtkSelectionData *selection,
+				      guint info, guint time, gpointer data)
+{
+	ViewFile *vf = data;
+
+	if (info == TARGET_TEXT_PLAIN) {
+		FileData *fd = vficon_find_data_by_coord(vf, x, y, NULL);
+
+		if (fd) {
+			/* Add keywords to file */
+			gchar *str = (gchar *) gtk_selection_data_get_text(selection);
+			GList *kw_list = string_to_keywords_list(str);
+
+			metadata_append_list(fd, KEYWORD_KEY, kw_list);
+			string_list_free(kw_list);
+			g_free(str);
+		}
+	}
+}
+
+static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+	ViewFile *vf = data;
+
+	tip_unschedule(vf);
+
+	if (VFICON(vf)->click_fd && VFICON(vf)->click_fd->thumb_pixbuf)
+		{
+		gint items;
+
+		if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
+			items = g_list_length(VFICON(vf)->selection);
+		else
+			items = 1;
+
+		dnd_set_drag_icon(widget, context, VFICON(vf)->click_fd->thumb_pixbuf, items);
+		}
+}
+
+static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
+
+	if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
+		{
+		vf_refresh(vf);
+		}
+
+	tip_unschedule(vf);
+}
+
+void vficon_dnd_init(ViewFile *vf)
+{
+	gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+
+	g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
+			 G_CALLBACK(vficon_dnd_get), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
+			 G_CALLBACK(vficon_dnd_begin), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_end",
+			 G_CALLBACK(vficon_dnd_end), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
+			 G_CALLBACK(vficon_drag_data_received), vf);
+}
+
+/*
+ *-------------------------------------------------------------------
+ * cell updates
+ *-------------------------------------------------------------------
+ */
+
+static void vficon_selection_set(ViewFile *vf, FileData *fd, SelectionType value, GtkTreeIter *iter)
+{
+	GtkTreeModel *store;
+	GList *list;
+
+	if (!fd) return;
+
+	if (fd->selected == value) return;
+	fd->selected = value;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	if (iter)
+		{
+		gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
+		if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1);
+		}
+	else
+		{
+		GtkTreeIter row;
+
+		if (vficon_find_iter(vf, fd, &row, NULL))
+			{
+			gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
+			if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
+			}
+		}
+}
+
+static void vficon_selection_add(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
+{
+	if (!fd) return;
+
+	vficon_selection_set(vf, fd, fd->selected | mask, iter);
+}
+
+static void vficon_selection_remove(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
+{
+	if (!fd) return;
+
+	vficon_selection_set(vf, fd, fd->selected & ~mask, iter);
+}
+
+void vficon_marks_set(ViewFile *vf, gint enable)
+{
+	GtkAllocation allocation;
+	gtk_widget_get_allocation(vf->listview, &allocation);
+	vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
+}
+
+/*
+ *-------------------------------------------------------------------
+ * selections
+ *-------------------------------------------------------------------
+ */
+
+static void vficon_verify_selections(ViewFile *vf)
+{
+	GList *work;
+
+	work = VFICON(vf)->selection;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		if (vficon_index_by_fd(vf, fd) >= 0) continue;
+
+		VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
+		}
+}
+
+void vficon_select_all(ViewFile *vf)
+{
+	GList *work;
+
+	g_list_free(VFICON(vf)->selection);
+	VFICON(vf)->selection = NULL;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+		vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
+		}
+
+	vf_send_update(vf);
+}
+
+void vficon_select_none(ViewFile *vf)
+{
+	GList *work;
+
+	work = VFICON(vf)->selection;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
+		}
+
+	g_list_free(VFICON(vf)->selection);
+	VFICON(vf)->selection = NULL;
+
+	vf_send_update(vf);
+}
+
+void vficon_select_invert(ViewFile *vf)
+{
+	GList *work;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		if (fd->selected & SELECTION_SELECTED)
+			{
+			VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
+			vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
+			}
+		else
+			{
+			VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+			vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
+			}
+		}
+
+	vf_send_update(vf);
+}
+
+static void vficon_select(ViewFile *vf, FileData *fd)
+{
+	VFICON(vf)->prev_selection = fd;
+
+	if (!fd || fd->selected & SELECTION_SELECTED) return;
+
+	VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+	vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
+
+	vf_send_update(vf);
+}
+
+static void vficon_unselect(ViewFile *vf, FileData *fd)
+{
+	VFICON(vf)->prev_selection = fd;
+
+	if (!fd || !(fd->selected & SELECTION_SELECTED) ) return;
+
+	VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
+	vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
+
+	vf_send_update(vf);
+}
+
+static void vficon_select_util(ViewFile *vf, FileData *fd, gboolean select)
+{
+	if (select)
+		{
+		vficon_select(vf, fd);
+		}
+	else
+		{
+		vficon_unselect(vf, fd);
+		}
+}
+
+static void vficon_select_region_util(ViewFile *vf, FileData *start, FileData *end, gboolean select)
+{
+	gint row1, col1;
+	gint row2, col2;
+	gint t;
+	gint i, j;
+
+	if (!vficon_find_position(vf, start, &row1, &col1) ||
+	    !vficon_find_position(vf, end, &row2, &col2) ) return;
+
+	VFICON(vf)->prev_selection = end;
+
+	if (!options->collections.rectangular_selection)
+		{
+		GList *work;
+
+		if (g_list_index(vf->list, start) > g_list_index(vf->list, end))
+			{
+			FileData *tmp = start;
+			start = end;
+			end = tmp;
+			}
+
+		work = g_list_find(vf->list, start);
+		while (work)
+			{
+			FileData *fd = work->data;
+			vficon_select_util(vf, fd, select);
+
+			if (work->data != end)
+				work = work->next;
+			else
+				work = NULL;
+			}
+		return;
+		}
+
+	// rectangular_selection==true.
+	if (row2 < row1)
+		{
+		t = row1;
+		row1 = row2;
+		row2 = t;
+		}
+	if (col2 < col1)
+		{
+		t = col1;
+		col1 = col2;
+		col2 = t;
+		}
+
+	DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2);
+
+	for (i = row1; i <= row2; i++)
+		{
+		for (j = col1; j <= col2; j++)
+			{
+			FileData *fd = vficon_find_data(vf, i, j, NULL);
+			if (fd) vficon_select_util(vf, fd, select);
+			}
+		}
+}
+
+gboolean vficon_index_is_selected(ViewFile *vf, gint row)
+{
+	FileData *fd = g_list_nth_data(vf->list, row);
+
+	if (!fd) return FALSE;
+
+	return (fd->selected & SELECTION_SELECTED);
+}
+
+guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
+{
+	if (bytes)
+		{
+		gint64 b = 0;
+		GList *work;
+
+		work = VFICON(vf)->selection;
+		while (work)
+			{
+			FileData *fd = work->data;
+			g_assert(fd->magick == FD_MAGICK);
+			b += fd->size;
+
+			work = work->next;
+			}
+
+		*bytes = b;
+		}
+
+	return g_list_length(VFICON(vf)->selection);
+}
+
+GList *vficon_selection_get_list(ViewFile *vf)
+{
+	GList *list = NULL;
+	GList *work, *work2;
+
+	work = VFICON(vf)->selection;
+	while (work)
+		{
+		FileData *fd = work->data;
+		g_assert(fd->magick == FD_MAGICK);
+
+		list = g_list_prepend(list, file_data_ref(fd));
+
+		work2 = fd->sidecar_files;
+		while (work2)
+			{
+			fd = work2->data;
+			list = g_list_prepend(list, file_data_ref(fd));
+			work2 = work2->next;
+			}
+
+		work = work->next;
+		}
+
+	list = g_list_reverse(list);
+
+	return list;
+}
+
+GList *vficon_selection_get_list_by_index(ViewFile *vf)
+{
+	GList *list = NULL;
+	GList *work;
+
+	work = VFICON(vf)->selection;
+	while (work)
+		{
+		list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data)));
+		work = work->next;
+		}
+
+	return g_list_reverse(list);
+}
+
+void vficon_select_by_fd(ViewFile *vf, FileData *fd)
+{
+	if (!fd) return;
+	if (!g_list_find(vf->list, fd)) return;
+
+	if (!(fd->selected & SELECTION_SELECTED))
+		{
+		vf_select_none(vf);
+		vficon_select(vf, fd);
+		}
+
+	vficon_set_focus(vf, fd);
+}
+
+void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
+{
+	GList *work;
+	gint n = mark - 1;
+
+	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		gboolean mark_val, selected;
+
+		g_assert(fd->magick == FD_MAGICK);
+
+		mark_val = file_data_get_mark(fd, n);
+		selected = fd->selected & SELECTION_SELECTED;
+
+		switch (mode)
+			{
+			case MTS_MODE_SET: selected = mark_val;
+				break;
+			case MTS_MODE_OR: selected = mark_val || selected;
+				break;
+			case MTS_MODE_AND: selected = mark_val && selected;
+				break;
+			case MTS_MODE_MINUS: selected = !mark_val && selected;
+				break;
+			}
+
+		vficon_select_util(vf, fd, selected);
+
+		work = work->next;
+		}
+}
+
+void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
+{
+	GList *slist;
+	GList *work;
+	gint n = mark -1;
+
+	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
+
+	slist = vf_selection_get_list(vf);
+	work = slist;
+	while (work)
+		{
+		FileData *fd = work->data;
+
+		switch (mode)
+			{
+			case STM_MODE_SET: file_data_set_mark(fd, n, 1);
+				break;
+			case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
+				break;
+			case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
+				break;
+			}
+		work = work->next;
+		}
+	filelist_free(slist);
+}
+
+static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
+{
+	GList *work;
+	FileData *fd = NULL;
+
+	if (sel_fd->parent) sel_fd = sel_fd->parent;
+	work = vf->list;
+
+	while (work)
+		{
+		gint match;
+
+		fd = work->data;
+		work = work->next;
+
+		match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
+
+		if (match >= 0) break;
+		}
+
+	if (fd)
+		{
+		vficon_select(vf, fd);
+		vficon_send_layout_select(vf, fd);
+		}
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * focus
+ *-------------------------------------------------------------------
+ */
+
+static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative)
+{
+	gint new_row;
+	gint new_col;
+
+	if (relative)
+		{
+		new_row = VFICON(vf)->focus_row;
+		new_col = VFICON(vf)->focus_column;
+
+		new_row += row;
+		if (new_row < 0) new_row = 0;
+		if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1;
+
+		while (col != 0)
+			{
+			if (col < 0)
+				{
+				new_col--;
+				col++;
+				}
+			else
+				{
+				new_col++;
+				col--;
+				}
+
+			if (new_col < 0)
+				{
+				if (new_row > 0)
+					{
+					new_row--;
+					new_col = VFICON(vf)->columns - 1;
+					}
+				else
+					{
+					new_col = 0;
+					}
+				}
+			if (new_col >= VFICON(vf)->columns)
+				{
+				if (new_row < VFICON(vf)->rows - 1)
+					{
+					new_row++;
+					new_col = 0;
+					}
+				else
+					{
+					new_col = VFICON(vf)->columns - 1;
+					}
+				}
+			}
+		}
+	else
+		{
+		new_row = row;
+		new_col = col;
+
+		if (new_row >= VFICON(vf)->rows)
+			{
+			if (VFICON(vf)->rows > 0)
+				new_row = VFICON(vf)->rows - 1;
+			else
+				new_row = 0;
+			new_col = VFICON(vf)->columns - 1;
+			}
+		if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1;
+		}
+
+	if (new_row == VFICON(vf)->rows - 1)
+		{
+		gint l;
+
+		/* if we moved beyond the last image, go to the last image */
+
+		l = g_list_length(vf->list);
+		if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns;
+		if (new_col >= l) new_col = l - 1;
+		}
+
+	vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
+}
+
+static void vficon_set_focus(ViewFile *vf, FileData *fd)
+{
+	GtkTreeIter iter;
+	gint row, col;
+
+	if (g_list_find(vf->list, VFICON(vf)->focus_fd))
+		{
+		if (fd == VFICON(vf)->focus_fd)
+			{
+			/* ensure focus row col are correct */
+			vficon_find_position(vf, VFICON(vf)->focus_fd, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
+#if GTK_CHECK_VERSION(3,0,0)
+/* FIXME: Refer to issue #467 on Github. The thumbnail position is not
+ * preserved when the icon view is refreshed. Caused by an unknown call from
+ * the idle loop. This patch hides the problem.
+ */
+			if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
+				{
+				tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
+				}
+#endif
+			return;
+			}
+		vficon_selection_remove(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
+		}
+
+	if (!vficon_find_position(vf, fd, &row, &col))
+		{
+		VFICON(vf)->focus_fd = NULL;
+		VFICON(vf)->focus_row = -1;
+		VFICON(vf)->focus_column = -1;
+		return;
+		}
+
+	VFICON(vf)->focus_fd = fd;
+	VFICON(vf)->focus_row = row;
+	VFICON(vf)->focus_column = col;
+	vficon_selection_add(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
+
+	if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
+		{
+		GtkTreePath *tpath;
+		GtkTreeViewColumn *column;
+		GtkTreeModel *store;
+
+		tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		tpath = gtk_tree_model_get_path(store, &iter);
+		/* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
+		column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS);
+		gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE);
+		gtk_tree_path_free(tpath);
+		}
+}
+
+/* used to figure the page up/down distances */
+static gint page_height(ViewFile *vf)
+{
+	GtkAdjustment *adj;
+	gint page_size;
+	gint row_height;
+	gint ret;
+
+	adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview));
+	page_size = (gint)gtk_adjustment_get_page_increment(adj);
+
+	row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2;
+	if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3;
+
+	ret = page_size / row_height;
+	if (ret < 1) ret = 1;
+
+	return ret;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * keyboard
+ *-------------------------------------------------------------------
+ */
+
+static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+	gint column;
+	GtkTreePath *tpath;
+	gint cw, ch;
+
+	if (!vficon_find_iter(vf, VFICON(vf)->click_fd, &iter, &column)) return;
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	tpath = gtk_tree_model_get_path(store, &iter);
+	tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
+	gtk_tree_path_free(tpath);
+	*y += ch;
+	popup_menu_position_clamp(menu, x, y, 0);
+}
+
+gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+	ViewFile *vf = data;
+	gint focus_row = 0;
+	gint focus_col = 0;
+	FileData *fd;
+	gboolean stop_signal;
+
+	stop_signal = TRUE;
+	switch (event->keyval)
+		{
+		case GDK_KEY_Left: case GDK_KEY_KP_Left:
+			focus_col = -1;
+			break;
+		case GDK_KEY_Right: case GDK_KEY_KP_Right:
+			focus_col = 1;
+			break;
+		case GDK_KEY_Up: case GDK_KEY_KP_Up:
+			focus_row = -1;
+			break;
+		case GDK_KEY_Down: case GDK_KEY_KP_Down:
+			focus_row = 1;
+			break;
+		case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
+			focus_row = -page_height(vf);
+			break;
+		case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
+			focus_row = page_height(vf);
+			break;
+		case GDK_KEY_Home: case GDK_KEY_KP_Home:
+			focus_row = -VFICON(vf)->focus_row;
+			focus_col = -VFICON(vf)->focus_column;
+			break;
+		case GDK_KEY_End: case GDK_KEY_KP_End:
+			focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row;
+			focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
+			break;
+		case GDK_KEY_space:
+			fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+			if (fd)
+				{
+				VFICON(vf)->click_fd = fd;
+				if (event->state & GDK_CONTROL_MASK)
+					{
+					gint selected;
+
+					selected = fd->selected & SELECTION_SELECTED;
+					if (selected)
+						{
+						vficon_unselect(vf, fd);
+						}
+					else
+						{
+						vficon_select(vf, fd);
+						vficon_send_layout_select(vf, fd);
+						}
+					}
+				else
+					{
+					vf_select_none(vf);
+					vficon_select(vf, fd);
+					vficon_send_layout_select(vf, fd);
+					}
+				}
+			break;
+		case GDK_KEY_Menu:
+			fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+			VFICON(vf)->click_fd = fd;
+
+			vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
+			tip_unschedule(vf);
+
+			vf->popup = vf_pop_menu(vf);
+			gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
+			break;
+		default:
+			stop_signal = FALSE;
+			break;
+		}
+
+	if (focus_row != 0 || focus_col != 0)
+		{
+		FileData *new_fd;
+		FileData *old_fd;
+
+		old_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+		vficon_move_focus(vf, focus_row, focus_col, TRUE);
+		new_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+
+		if (new_fd != old_fd)
+			{
+			if (event->state & GDK_SHIFT_MASK)
+				{
+				if (!options->collections.rectangular_selection)
+					{
+					vficon_select_region_util(vf, old_fd, new_fd, FALSE);
+					}
+				else
+					{
+					vficon_select_region_util(vf, VFICON(vf)->click_fd, old_fd, FALSE);
+					}
+				vficon_select_region_util(vf, VFICON(vf)->click_fd, new_fd, TRUE);
+				vficon_send_layout_select(vf, new_fd);
+				}
+			else if (event->state & GDK_CONTROL_MASK)
+				{
+				VFICON(vf)->click_fd = new_fd;
+				}
+			else
+				{
+				VFICON(vf)->click_fd = new_fd;
+				vf_select_none(vf);
+				vficon_select(vf, new_fd);
+				vficon_send_layout_select(vf, new_fd);
+				}
+			}
+		}
+
+	if (stop_signal)
+		{
+		tip_unschedule(vf);
+		}
+
+	return stop_signal;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * mouse
+ *-------------------------------------------------------------------
+ */
+
+static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+	ViewFile *vf = data;
+	FileData *fd;
+
+	fd = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL);
+	tip_update(vf, fd);
+
+	return FALSE;
+}
+
+gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeIter iter;
+	FileData *fd;
+
+	tip_unschedule(vf);
+
+	fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
+
+	VFICON(vf)->click_fd = fd;
+	vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
+
+	switch (bevent->button)
+		{
+		case MOUSE_BUTTON_LEFT:
+			if (!gtk_widget_has_focus(vf->listview))
+				{
+				gtk_widget_grab_focus(vf->listview);
+				}
+
+			if (bevent->type == GDK_2BUTTON_PRESS && vf->layout)
+				{
+				vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
+				layout_image_full_screen_start(vf->layout);
+				}
+			break;
+		case MOUSE_BUTTON_RIGHT:
+			vf->popup = vf_pop_menu(vf);
+			gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
+			break;
+		default:
+			break;
+		}
+
+	return FALSE;
+}
+
+gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeIter iter;
+	FileData *fd = NULL;
+	gboolean was_selected;
+
+	tip_schedule(vf);
+
+	if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
+		{
+		fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
+		}
+
+	if (VFICON(vf)->click_fd)
+		{
+		vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
+		}
+
+	if (!fd || VFICON(vf)->click_fd != fd) return TRUE;
+
+	was_selected = !!(fd->selected & SELECTION_SELECTED);
+
+	switch (bevent->button)
+		{
+		case MOUSE_BUTTON_LEFT:
+			{
+			vficon_set_focus(vf, fd);
+
+			if (bevent->state & GDK_CONTROL_MASK)
+				{
+				gboolean select;
+
+				select = !(fd->selected & SELECTION_SELECTED);
+				if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
+					{
+					vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, select);
+					}
+				else
+					{
+					vficon_select_util(vf, fd, select);
+					}
+				}
+			else
+				{
+				vf_select_none(vf);
+
+				if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
+					{
+					vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, TRUE);
+					}
+				else
+					{
+					vficon_select_util(vf, fd, TRUE);
+					was_selected = FALSE;
+					}
+				}
+			}
+			break;
+		case MOUSE_BUTTON_MIDDLE:
+			{
+			vficon_select_util(vf, fd, !(fd->selected & SELECTION_SELECTED));
+			}
+			break;
+		default:
+			break;
+		}
+
+	if (!was_selected && (fd->selected & SELECTION_SELECTED))
+		{
+		vficon_send_layout_select(vf, fd);
+		}
+
+	return TRUE;
+}
+
+static gboolean vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
+{
+	ViewFile *vf = data;
+
+	tip_unschedule(vf);
+	return FALSE;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * population
+ *-------------------------------------------------------------------
+ */
+
+static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
+{
+	GList *list;
+
+	gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
+
+	/* it seems that gtk_list_store_clear may call some callbacks
+	   that use the column. Set the pointer to NULL to be safe. */
+	gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, NULL, -1);
+	g_list_free(list);
+
+	return FALSE;
+}
+
+static void vficon_clear_store(ViewFile *vf)
+{
+	GtkTreeModel *store;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
+
+	gtk_list_store_clear(GTK_LIST_STORE(store));
+}
+
+static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter)
+{
+	GtkListStore *store;
+	GList *list = NULL;
+	gint i;
+
+	for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL);
+
+	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+	gtk_list_store_append(store, iter);
+	gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
+
+	return list;
+}
+
+static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_position)
+{
+	GtkTreeModel *store;
+	GtkTreePath *tpath;
+	GList *work;
+	FileData *visible_fd = NULL;
+	gint r, c;
+	gboolean valid;
+	GtkTreeIter iter;
+
+	vficon_verify_selections(vf);
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+
+	if (keep_position && gtk_widget_get_realized(vf->listview) &&
+	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+		{
+		GtkTreeIter iter;
+		GList *list;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_path_free(tpath);
+
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+		if (list) visible_fd = list->data;
+		}
+
+
+	if (resize)
+		{
+		gint i;
+		gint thumb_width;
+
+		vficon_clear_store(vf);
+
+		thumb_width = vficon_get_icon_width(vf);
+
+		for (i = 0; i < VFICON_MAX_COLUMNS; i++)
+			{
+			GtkTreeViewColumn *column;
+			GtkCellRenderer *cell;
+			GList *list;
+
+			column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i);
+			gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns));
+			gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
+
+			list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
+			cell = (list) ? list->data : NULL;
+			g_list_free(list);
+
+			if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
+				{
+				g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
+							     "fixed_height", options->thumbnails.max_height,
+							     "show_text", VFICON(vf)->show_text,
+							     "show_marks", vf->marks_enabled,
+							     "num_marks", FILEDATA_MARKS_SIZE,
+							     NULL);
+				}
+			}
+		if (gtk_widget_get_realized(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
+		}
+
+	r = -1;
+	c = 0;
+
+	valid = gtk_tree_model_iter_children(store, &iter, NULL);
+
+	work = vf->list;
+	while (work)
+		{
+		GList *list;
+		r++;
+		c = 0;
+		if (valid)
+			{
+			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+			gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
+			}
+		else
+			{
+			list = vficon_add_row(vf, &iter);
+			}
+
+		while (list)
+			{
+			FileData *fd;
+
+			if (work)
+				{
+				fd = work->data;
+				work = work->next;
+				c++;
+				}
+			else
+				{
+				fd = NULL;
+				}
+
+			list->data = fd;
+			list = list->next;
+			}
+		if (valid) valid = gtk_tree_model_iter_next(store, &iter);
+		}
+
+	r++;
+	while (valid)
+		{
+		GList *list;
+
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+		valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
+		g_list_free(list);
+		}
+
+	VFICON(vf)->rows = r;
+
+	if (visible_fd &&
+	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+		{
+		GtkTreeIter iter;
+		GList *list;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_path_free(tpath);
+
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+		if (g_list_find(list, visible_fd) == NULL &&
+		    vficon_find_iter(vf, visible_fd, &iter, NULL))
+			{
+			tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
+			}
+		}
+
+
+	vf_send_update(vf);
+	vf_thumb_update(vf);
+}
+
+static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force)
+{
+	gint new_cols;
+	gint thumb_width;
+
+	thumb_width = vficon_get_icon_width(vf);
+
+	new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
+	if (new_cols < 1) new_cols = 1;
+
+	if (!force && new_cols == VFICON(vf)->columns) return;
+
+	VFICON(vf)->columns = new_cols;
+
+	vficon_populate(vf, TRUE, TRUE);
+
+	DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows);
+}
+
+static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * misc
+ *-----------------------------------------------------------------------------
+ */
+
+void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend)
+{
+	if (vf->sort_method == type && vf->sort_ascend == ascend) return;
+
+	vf->sort_method = type;
+	vf->sort_ascend = ascend;
+
+	if (!vf->list) return;
+
+	vf_refresh(vf);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * thumb updates
+ *-----------------------------------------------------------------------------
+ */
+
+void vficon_thumb_progress_count(GList *list, gint *count, gint *done)
+{
+	GList *work = list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		if (fd->thumb_pixbuf) (*done)++;
+		(*count)++;
+		}
+}
+
+void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
+{
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+	GList *list;
+
+	if (!g_list_find(vf->list, fd)) return;
+	if (!vficon_find_iter(vf, fd, &iter, NULL)) return;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+
+	gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+	gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
+}
+
+/* Returns the next fd without a loaded pixbuf, so the thumb-loader can load the pixbuf for it. */
+FileData *vficon_thumb_next_fd(ViewFile *vf)
+{
+	GtkTreePath *tpath;
+
+	/* First see if there are visible files that don't have a loaded thumb... */
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+		{
+		GtkTreeModel *store;
+		GtkTreeIter iter;
+		gboolean valid = TRUE;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_path_free(tpath);
+		tpath = NULL;
+
+		while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
+			{
+			GList *list;
+			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+
+			// TODO(xsdg): for loop here.
+			for (; list; list = list->next)
+				{
+				FileData *fd = list->data;
+				if (fd && !fd->thumb_pixbuf) return fd;
+				}
+
+			valid = gtk_tree_model_iter_next(store, &iter);
+			}
+		}
+
+	/* Then iterate through the entire list to load all of them. */
+	GList *work;
+	for (work = vf->list; work; work = work->next)
+		{
+		FileData *fd = work->data;
+
+		// Note: This implementation differs from view_file_list.c because sidecar files are not
+		// distinct list elements here, as they are in the list view.
+		if (!fd->thumb_pixbuf) return fd;
+		}
+
+	return NULL;
+}
+
+void vficon_thumb_reset_all(ViewFile *vf)
+{
+	GList *work = vf->list;
+
+	while (work)
+		{
+		FileData *fd = work->data;
+		if (fd->thumb_pixbuf)
+			{
+			g_object_unref(fd->thumb_pixbuf);
+			fd->thumb_pixbuf = NULL;
+			}
+		work = work->next;
+		}
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * row stuff
+ *-----------------------------------------------------------------------------
+ */
+
+FileData *vficon_index_get_data(ViewFile *vf, gint row)
+{
+	FileData *fd;
+
+	fd = g_list_nth_data(vf->list, row);
+	return fd ? fd : NULL;
+}
+
+
+gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
+{
+	gint p = 0;
+	GList *work;
+
+	if (!in_fd) return -1;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		if (fd == in_fd) return p;
+		work = work->next;
+		p++;
+		}
+
+	return -1;
+}
+
+guint vficon_count(ViewFile *vf, gint64 *bytes)
+{
+	if (bytes)
+		{
+		gint64 b = 0;
+		GList *work;
+
+		work = vf->list;
+		while (work)
+			{
+			FileData *fd = work->data;
+			work = work->next;
+
+			b += fd->size;
+			}
+
+		*bytes = b;
+		}
+
+	return g_list_length(vf->list);
+}
+
+GList *vficon_get_list(ViewFile *vf)
+{
+	GList *list = NULL;
+	GList *work;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		list = g_list_prepend(list, file_data_ref(fd));
+		}
+
+	return g_list_reverse(list);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
+{
+	gboolean ret = TRUE;
+	GList *work, *new_work;
+	FileData *focus_fd;
+	FileData *first_selected = NULL;
+	GList *new_filelist = NULL;
+	GList *new_fd_list = NULL;
+
+	focus_fd = VFICON(vf)->focus_fd;
+
+	if (vf->dir_fd)
+		{
+		ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
+		new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
+		}
+
+	vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
+	new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
+
+	if (VFICON(vf)->selection)
+		{
+		first_selected = VFICON(vf)->selection->data;
+		file_data_ref(first_selected);
+		g_list_free(VFICON(vf)->selection);
+		VFICON(vf)->selection = NULL;
+		}
+
+	/* iterate old list and new list, looking for differences */
+	work = vf->list;
+	new_work = new_filelist;
+	while (work || new_work)
+		{
+		FileData *fd = NULL;
+		FileData *new_fd = NULL;
+		gint match;
+
+		if (work && new_work)
+			{
+			fd = work->data;
+			new_fd = new_work->data;
+
+			if (fd == new_fd)
+				{
+				/* not changed, go to next */
+				work = work->next;
+				new_work = new_work->next;
+				if (fd->selected & SELECTION_SELECTED)
+					{
+					VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, fd);
+					}
+				continue;
+				}
+
+			match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
+			if (match == 0) g_warning("multiple fd for the same path");
+			}
+		else if (work)
+			{
+			/* old item was deleted */
+			fd = work->data;
+			match = -1;
+			}
+		else
+			{
+			/* new item was added */
+			new_fd = new_work->data;
+			match = 1;
+			}
+
+		if (match < 0)
+			{
+			/* file no longer exists, delete from vf->list */
+			GList *to_delete = work;
+			work = work->next;
+			if (fd == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
+			if (fd == VFICON(vf)->click_fd) VFICON(vf)->click_fd = NULL;
+			file_data_unref(fd);
+			vf->list = g_list_delete_link(vf->list, to_delete);
+			}
+		else
+			{
+			/* new file, add to vf->list */
+			file_data_ref(new_fd);
+			new_fd->selected = SELECTION_NONE;
+			if (work)
+				{
+				vf->list = g_list_insert_before(vf->list, work, new_fd);
+				}
+			else
+				{
+				/* it is faster to append all new entries together later */
+				new_fd_list = g_list_prepend(new_fd_list, new_fd);
+				}
+
+			new_work = new_work->next;
+			}
+		}
+
+	if (new_fd_list)
+		{
+		vf->list = g_list_concat(vf->list, g_list_reverse(new_fd_list));
+		}
+
+	VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
+
+	filelist_free(new_filelist);
+
+	vficon_populate(vf, TRUE, keep_position);
+
+	if (first_selected && !VFICON(vf)->selection)
+		{
+		/* all selected files disappeared */
+		vficon_select_closest(vf, first_selected);
+		}
+	file_data_unref(first_selected);
+
+	/* attempt to keep focus on same icon when refreshing */
+	if (focus_fd && g_list_find(vf->list, focus_fd))
+		{
+		vficon_set_focus(vf, focus_fd);
+		}
+
+	return ret;
+}
+
+gboolean vficon_refresh(ViewFile *vf)
+{
+	return vficon_refresh_real(vf, TRUE);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * draw, etc.
+ *-----------------------------------------------------------------------------
+ */
+
+typedef struct _ColumnData ColumnData;
+struct _ColumnData
+{
+	ViewFile *vf;
+	gint number;
+};
+
+static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+				GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+	GList *list;
+	FileData *fd;
+	ColumnData *cd = data;
+	ViewFile *vf = cd->vf;
+
+	if (!GQV_IS_CELL_RENDERER_ICON(cell)) return;
+
+	gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
+
+	fd = g_list_nth_data(list, cd->number);
+
+	if (fd)
+		{
+		GdkColor color_fg;
+		GdkColor color_bg;
+		GtkStyle *style;
+		gchar *name_sidecars;
+		gchar *link;
+		GtkStateType state = GTK_STATE_NORMAL;
+
+		g_assert(fd->magick == FD_MAGICK);
+
+		link = islink(fd->path) ? GQ_LINK_STR : "";
+		if (fd->sidecar_files)
+			{
+			gchar *sidecars = file_data_sc_list_to_string(fd);
+			name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
+			g_free(sidecars);
+			}
+		else
+			{
+			gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
+			name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
+			}
+
+		style = gtk_widget_get_style(vf->listview);
+		if (fd->selected & SELECTION_SELECTED)
+			{
+			state = GTK_STATE_SELECTED;
+			}
+
+		memcpy(&color_fg, &style->text[state], sizeof(color_fg));
+		memcpy(&color_bg, &style->base[state], sizeof(color_bg));
+
+		if (fd->selected & SELECTION_PRELIGHT)
+			{
+			shift_color(&color_bg, -1, 0);
+			}
+
+		g_object_set(cell,	"pixbuf", fd->thumb_pixbuf,
+					"text", name_sidecars,
+					"marks", file_data_get_marks(fd),
+					"show_marks", vf->marks_enabled,
+					"cell-background-gdk", &color_bg,
+					"cell-background-set", TRUE,
+					"foreground-gdk", &color_fg,
+					"foreground-set", TRUE,
+					"has-focus", (VFICON(vf)->focus_fd == fd), NULL);
+		g_free(name_sidecars);
+		}
+	else
+		{
+		g_object_set(cell,	"pixbuf", NULL,
+					"text", NULL,
+					"show_marks", FALSE,
+					"cell-background-set", FALSE,
+					"foreground-set", FALSE,
+					"has-focus", FALSE, NULL);
+		}
+}
+
+static void vficon_append_column(ViewFile *vf, gint n)
+{
+	ColumnData *cd;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_min_width(column, 0);
+
+	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+	gtk_tree_view_column_set_alignment(column, 0.5);
+
+	renderer = gqv_cell_renderer_icon_new();
+	gtk_tree_view_column_pack_start(column, renderer, FALSE);
+	g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
+					 "ypad", THUMB_BORDER_PADDING,
+					 "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+
+	g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
+	g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
+
+	cd = g_new0(ColumnData, 1);
+	cd->vf = vf;
+	cd->number = n;
+	gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
+
+	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
+
+	g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * base
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd)
+{
+	gboolean ret;
+
+	if (!dir_fd) return FALSE;
+	if (vf->dir_fd == dir_fd) return TRUE;
+
+	file_data_unref(vf->dir_fd);
+	vf->dir_fd = file_data_ref(dir_fd);
+
+	g_list_free(VFICON(vf)->selection);
+	VFICON(vf)->selection = NULL;
+
+	g_list_free(vf->list);
+	vf->list = NULL;
+
+	/* NOTE: populate will clear the store for us */
+	ret = vficon_refresh_real(vf, FALSE);
+
+	VFICON(vf)->focus_fd = NULL;
+	vficon_move_focus(vf, 0, 0, FALSE);
+
+	return ret;
+}
+
+void vficon_destroy_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vf_refresh_idle_cancel(vf);
+
+	file_data_unregister_notify_func(vf_notify_cb, vf);
+
+	tip_unschedule(vf);
+
+	vf_thumb_cleanup(vf);
+
+	g_list_free(vf->list);
+	g_list_free(VFICON(vf)->selection);
+}
+
+ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
+{
+	GtkListStore *store;
+	GtkTreeSelection *selection;
+	gint i;
+
+	vf->info = g_new0(ViewFileInfoIcon, 1);
+
+	VFICON(vf)->show_text = options->show_icon_names;
+
+	store = gtk_list_store_new(1, G_TYPE_POINTER);
+	vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+	g_object_unref(store);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
+
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
+	gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
+
+	for (i = 0; i < VFICON_MAX_COLUMNS; i++)
+		{
+		vficon_append_column(vf, i);
+		}
+
+	/* zero width column to hide tree view focus, we draw it ourselves */
+	vficon_append_column(vf, i);
+	/* end column to fill white space */
+	vficon_append_column(vf, i);
+
+	g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
+			 G_CALLBACK(vficon_sized_cb), vf);
+
+	gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
+			      GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
+
+	g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
+			 G_CALLBACK(vficon_motion_cb), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
+			 G_CALLBACK(vficon_leave_cb), vf);
+
+	/* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
+	vficon_populate_at_new_size(vf, 1, 1, FALSE);
+
+	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
+
+	return vf;
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/view_file_icon.h	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2004 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 VIEW_FILE_VIEW_FILE_ICON_H
+#define VIEW_FILE_VIEW_FILE_ICON_H
+
+#include "filedata.h"
+
+gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
+gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
+gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
+
+void vficon_dnd_init(ViewFile *vf);
+
+void vficon_destroy_cb(GtkWidget *widget, gpointer data);
+ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd);
+
+gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd);
+gboolean vficon_refresh(ViewFile *vf);
+
+void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend);
+
+void vficon_marks_set(ViewFile *vf, gboolean enable);
+
+GList *vficon_selection_get_one(ViewFile *vf, FileData *fd);
+GList *vficon_pop_menu_file_list(ViewFile *vf);
+void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data);
+void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data);
+void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data);
+void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data);
+void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data);
+
+FileData *vficon_index_get_data(ViewFile *vf, gint row);
+gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd);
+guint vficon_count(ViewFile *vf, gint64 *bytes);
+GList *vficon_get_list(ViewFile *vf);
+
+gboolean vficon_index_is_selected(ViewFile *vf, gint row);
+guint vficon_selection_count(ViewFile *vf, gint64 *bytes);
+GList *vficon_selection_get_list(ViewFile *vf);
+GList *vficon_selection_get_list_by_index(ViewFile *vf);
+
+void vficon_select_all(ViewFile *vf);
+void vficon_select_none(ViewFile *vf);
+void vficon_select_invert(ViewFile *vf);
+void vficon_select_by_fd(ViewFile *vf, FileData *fd);
+
+void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode);
+void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode);
+
+
+void vficon_thumb_progress_count(GList *list, gint *count, gint *done);
+void vficon_set_thumb_fd(ViewFile *vf, FileData *fd);
+FileData *vficon_thumb_next_fd(ViewFile *vf);
+void vficon_thumb_reset_all(ViewFile *vf);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/view_file_list.c	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,2053 @@
+/*
+ * Copyright (C) 2004 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 "view_file_list.h"
+
+#include "bar.h"
+#include "cache_maint.h"
+#include "dnd.h"
+#include "editors.h"
+#include "img-view.h"
+#include "layout.h"
+#include "layout_image.h"
+#include "menu.h"
+#include "metadata.h"
+#include "thumb.h"
+#include "utilops.h"
+#include "ui_fileops.h"
+#include "ui_menu.h"
+#include "ui_tree_edit.h"
+#include "uri_utils.h"
+#include "view_file.h"
+
+#include <gdk/gdkkeysyms.h> /* for keyboard values */
+
+/* Index to tree store */
+enum {
+	FILE_COLUMN_POINTER = 0,
+	FILE_COLUMN_VERSION,
+	FILE_COLUMN_THUMB,
+	FILE_COLUMN_FORMATTED,
+	FILE_COLUMN_NAME,
+	FILE_COLUMN_SIDECARS,
+	FILE_COLUMN_SIZE,
+	FILE_COLUMN_DATE,
+	FILE_COLUMN_EXPANDED,
+	FILE_COLUMN_COLOR,
+	FILE_COLUMN_MARKS,
+	FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
+	FILE_COLUMN_COUNT
+};
+
+
+/* Index to tree view */
+enum {
+	FILE_VIEW_COLUMN_MARKS = 0,
+	FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
+	FILE_VIEW_COLUMN_THUMB,
+	FILE_VIEW_COLUMN_FORMATTED,
+	FILE_VIEW_COLUMN_SIZE,
+	FILE_VIEW_COLUMN_DATE,
+	FILE_VIEW_COLUMN_COUNT
+};
+
+
+
+static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
+static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
+static void vflist_populate_view(ViewFile *vf, gboolean force);
+static gboolean vflist_is_multiline(ViewFile *vf);
+static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * misc
+ *-----------------------------------------------------------------------------
+ */
+typedef struct {
+	FileData *fd;
+	GtkTreeIter *iter;
+	gboolean found;
+	gint row;
+} ViewFileFindRowData;
+
+static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+	ViewFileFindRowData *find = data;
+	FileData *fd;
+	gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
+	if (fd == find->fd)
+		{
+		*find->iter = *iter;
+		find->found = TRUE;
+		return TRUE;
+		}
+	find->row++;
+	return FALSE;
+}
+
+static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
+{
+	GtkTreeModel *store;
+	ViewFileFindRowData data = {fd, iter, FALSE, 0};
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
+
+	if (data.found)
+		{
+		return data.row;
+		}
+
+	return -1;
+}
+
+static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
+{
+	GtkTreePath *tpath;
+	GtkTreeViewColumn *column;
+
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
+					  &tpath, &column, NULL, NULL))
+		{
+		GtkTreeModel *store;
+		GtkTreeIter row;
+		FileData *fd;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		gtk_tree_model_get_iter(store, &row, tpath);
+		gtk_tree_path_free(tpath);
+		gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
+
+		return fd;
+		}
+
+	return NULL;
+}
+
+static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+	FileData *fd;
+	gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
+
+	/* it seems that gtk_tree_store_clear may call some callbacks
+	   that use the column. Set the pointer to NULL to be safe. */
+	gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
+	file_data_unref(fd);
+	return FALSE;
+}
+
+static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
+{
+	GtkTreeModel *store;
+	GList *files = NULL;
+
+	if (unlock_files && vf->marks_enabled)
+		{
+		// unlock locked files in this directory
+		filelist_read(vf->dir_fd, &files, NULL);
+		while (files)
+			{
+			FileData *fd = files->data;
+			files = files->next;
+			file_data_unlock(fd);
+			file_data_unref(fd);  // undo the ref that got added in filelist_read
+			}
+		}
+
+	g_list_free(files);
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
+	gtk_tree_store_clear(GTK_TREE_STORE(store));
+}
+
+void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
+{
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+
+	if (vflist_find_row(vf, fd, &iter) < 0) return;
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
+}
+
+static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
+{
+	GtkTreeModel *store;
+	GtkTreePath *tpath;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+
+	tpath = gtk_tree_model_get_path(store, iter);
+	gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
+	gtk_tree_path_free(tpath);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * dnd
+ *-----------------------------------------------------------------------------
+ */
+
+static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
+			   GtkSelectionData *selection_data, guint info,
+			   guint time, gpointer data)
+{
+	ViewFile *vf = data;
+	GList *list = NULL;
+
+	if (!VFLIST(vf)->click_fd) return;
+
+	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
+		{
+		list = vf_selection_get_list(vf);
+		}
+	else
+		{
+		list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
+		}
+
+	if (!list) return;
+	uri_selection_data_set_uris_from_filelist(selection_data, list);
+	filelist_free(list);
+}
+
+static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
+
+	if (VFLIST(vf)->thumbs_enabled &&
+	    VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
+		{
+		guint items;
+
+		if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
+			items = vf_selection_count(vf, NULL);
+		else
+			items = 1;
+
+		dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
+		}
+}
+
+static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+
+	if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
+		{
+		vf_refresh(vf);
+		}
+}
+
+static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
+				      int x, int y, GtkSelectionData *selection,
+				      guint info, guint time, gpointer data)
+{
+	ViewFile *vf = data;
+
+	if (info == TARGET_TEXT_PLAIN) {
+		FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
+
+		if (fd) {
+			/* Add keywords to file */
+			gchar *str = (gchar *) gtk_selection_data_get_text(selection);
+			GList *kw_list = string_to_keywords_list(str);
+
+			metadata_append_list(fd, KEYWORD_KEY, kw_list);
+			string_list_free(kw_list);
+			g_free(str);
+		}
+	}
+}
+
+void vflist_dnd_init(ViewFile *vf)
+{
+	gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+
+	g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
+			 G_CALLBACK(vflist_dnd_get), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
+			 G_CALLBACK(vflist_dnd_begin), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_end",
+			 G_CALLBACK(vflist_dnd_end), vf);
+	g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
+			 G_CALLBACK(vflist_drag_data_received), vf);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * pop-up menu
+ *-----------------------------------------------------------------------------
+ */
+
+GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
+{
+	GList *list = g_list_append(NULL, file_data_ref(fd));
+
+	if (fd->sidecar_files)
+		{
+		/* check if the row is expanded */
+		GtkTreeModel *store;
+		GtkTreeIter iter;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		if (vflist_find_row(vf, fd, &iter) >= 0)
+			{
+			GtkTreePath *tpath;
+
+			tpath = gtk_tree_model_get_path(store, &iter);
+			if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
+				{
+				/* unexpanded - add whole group */
+				GList *work = fd->sidecar_files;
+				while (work)
+					{
+					FileData *sfd = work->data;
+					list = g_list_prepend(list, file_data_ref(sfd));
+					work = work->next;
+					}
+				}
+			gtk_tree_path_free(tpath);
+			}
+		list = g_list_reverse(list);
+		}
+
+	return list;
+}
+
+GList *vflist_pop_menu_file_list(ViewFile *vf)
+{
+	if (!VFLIST(vf)->click_fd) return NULL;
+
+	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
+		{
+		return vf_selection_get_list(vf);
+		}
+	return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
+}
+
+
+void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
+		{
+		GList *list;
+
+		list = vf_selection_get_list(vf);
+		view_window_new_from_list(list);
+		filelist_free(list);
+		}
+	else
+		{
+		view_window_new(VFLIST(vf)->click_fd);
+		}
+}
+
+void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+	GList *list;
+
+	list = vf_pop_menu_file_list(vf);
+	if (options->file_ops.enable_in_place_rename &&
+	    list && !list->next && VFLIST(vf)->click_fd)
+		{
+		GtkTreeModel *store;
+		GtkTreeIter iter;
+
+		filelist_free(list);
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
+			{
+			GtkTreePath *tpath;
+
+			tpath = gtk_tree_model_get_path(store, &iter);
+			tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
+					  FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
+					  vflist_row_rename_cb, vf);
+			gtk_tree_path_free(tpath);
+			}
+		return;
+		}
+
+	file_util_rename(NULL, list, vf->listview);
+}
+
+void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+	if (vf->layout)
+		{
+		layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
+		}
+	else
+		{
+		vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
+		}
+}
+
+void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+	vf_refresh(vf);
+	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
+}
+
+void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+	VFLIST(vf)->click_fd = NULL;
+	vf->popup = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * callbacks
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
+{
+	ViewFile *vf = data;
+	gchar *new_path;
+
+	if (!new || !new[0]) return FALSE;
+
+	new_path = g_build_filename(vf->dir_fd->path, new, NULL);
+
+	if (strchr(new, G_DIR_SEPARATOR) != NULL)
+		{
+		gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
+		file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
+		g_free(text);
+		}
+	else
+		{
+		gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
+		FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
+		file_util_rename_simple(fd, new_path, vf->listview);
+		file_data_unref(fd);
+		g_free(old_path);
+		}
+
+	g_free(new_path);
+
+	return FALSE;
+}
+
+static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+	GtkTreePath *tpath;
+	gint cw, ch;
+
+	if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	tpath = gtk_tree_model_get_path(store, &iter);
+	tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
+	gtk_tree_path_free(tpath);
+	*y += ch;
+	popup_menu_position_clamp(menu, x, y, 0);
+}
+
+gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreePath *tpath;
+
+	if (event->keyval != GDK_KEY_Menu) return FALSE;
+
+	gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
+	if (tpath)
+		{
+		GtkTreeModel *store;
+		GtkTreeIter iter;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
+		gtk_tree_path_free(tpath);
+		}
+	else
+		{
+		VFLIST(vf)->click_fd = NULL;
+		}
+
+	vf->popup = vf_pop_menu(vf);
+	gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
+
+	return TRUE;
+}
+
+gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreePath *tpath;
+	GtkTreeIter iter;
+	FileData *fd = NULL;
+	GtkTreeViewColumn *column;
+
+	vf->clicked_mark = 0;
+
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
+					  &tpath, &column, NULL, NULL))
+		{
+		GtkTreeModel *store;
+		gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
+
+		if (bevent->button == MOUSE_BUTTON_LEFT &&
+		    col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
+			return FALSE;
+
+		if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
+			vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+		gtk_tree_path_free(tpath);
+		}
+
+	VFLIST(vf)->click_fd = fd;
+
+	if (bevent->button == MOUSE_BUTTON_RIGHT)
+		{
+		vf->popup = vf_pop_menu(vf);
+		gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
+				bevent->button, bevent->time);
+		return TRUE;
+		}
+
+	if (!fd) return FALSE;
+
+	if (bevent->button == MOUSE_BUTTON_MIDDLE)
+		{
+		if (!vflist_row_is_selected(vf, fd))
+			{
+			vflist_color_set(vf, fd, TRUE);
+			}
+		return TRUE;
+		}
+
+
+	if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
+	    !(bevent->state & GDK_SHIFT_MASK ) &&
+	    !(bevent->state & GDK_CONTROL_MASK ) &&
+	    vflist_row_is_selected(vf, fd))
+		{
+		GtkTreeSelection *selection;
+
+		gtk_widget_grab_focus(widget);
+
+
+		/* returning FALSE and further processing of the event is needed for
+		   correct operation of the expander, to show the sidecar files.
+		   It however resets the selection of multiple files. With this condition
+		   it should work for both cases */
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+		return (gtk_tree_selection_count_selected_rows(selection) > 1);
+		}
+
+	if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
+		{
+		if (vf->layout) layout_image_full_screen_start(vf->layout);
+		}
+
+	return FALSE;
+}
+
+gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreePath *tpath;
+	GtkTreeIter iter;
+	FileData *fd = NULL;
+
+	if (bevent->button == MOUSE_BUTTON_MIDDLE)
+		{
+		vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+		}
+
+	if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
+		{
+		return TRUE;
+		}
+
+	if ((bevent->x != 0 || bevent->y != 0) &&
+	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
+					  &tpath, NULL, NULL, NULL))
+		{
+		GtkTreeModel *store;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+		gtk_tree_path_free(tpath);
+		}
+
+	if (bevent->button == MOUSE_BUTTON_MIDDLE)
+		{
+		if (fd && VFLIST(vf)->click_fd == fd)
+			{
+			GtkTreeSelection *selection;
+
+			selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+			if (vflist_row_is_selected(vf, fd))
+				{
+				gtk_tree_selection_unselect_iter(selection, &iter);
+				}
+			else
+				{
+				gtk_tree_selection_select_iter(selection, &iter);
+				}
+			}
+		return TRUE;
+		}
+
+	if (fd && VFLIST(vf)->click_fd == fd &&
+	    !(bevent->state & GDK_SHIFT_MASK ) &&
+	    !(bevent->state & GDK_CONTROL_MASK ) &&
+	    vflist_row_is_selected(vf, fd))
+		{
+		GtkTreeSelection *selection;
+
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+		gtk_tree_selection_unselect_all(selection);
+		gtk_tree_selection_select_iter(selection, &iter);
+		vflist_move_cursor(vf, &iter);
+		}
+
+	return FALSE;
+}
+
+static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
+{
+	FileData *read_ahead_fd = NULL;
+	gint row;
+	FileData *cur_fd;
+
+	if (!sel_fd) return;
+
+	cur_fd = layout_image_get_fd(vf->layout);
+	if (sel_fd == cur_fd) return; /* no change */
+
+	row = g_list_index(vf->list, sel_fd);
+	// FIXME sidecar data
+
+	if (sel_fd && options->image.enable_read_ahead && row >= 0)
+		{
+		if (row > g_list_index(vf->list, cur_fd) &&
+		    (guint) (row + 1) < vf_count(vf, NULL))
+			{
+			read_ahead_fd = vf_index_get_data(vf, row + 1);
+			}
+		else if (row > 0)
+			{
+			read_ahead_fd = vf_index_get_data(vf, row - 1);
+			}
+		}
+
+	layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
+}
+
+static gboolean vflist_select_idle_cb(gpointer data)
+{
+	ViewFile *vf = data;
+
+	if (!vf->layout)
+		{
+		VFLIST(vf)->select_idle_id = 0;
+		return FALSE;
+		}
+
+	vf_send_update(vf);
+
+	if (VFLIST(vf)->select_fd)
+		{
+		vflist_select_image(vf, VFLIST(vf)->select_fd);
+		VFLIST(vf)->select_fd = NULL;
+		}
+
+	VFLIST(vf)->select_idle_id = 0;
+	return FALSE;
+}
+
+static void vflist_select_idle_cancel(ViewFile *vf)
+{
+	if (VFLIST(vf)->select_idle_id)
+		{
+		g_source_remove(VFLIST(vf)->select_idle_id);
+		VFLIST(vf)->select_idle_id = 0;
+		}
+}
+
+static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
+				 gboolean path_currently_selected, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeIter iter;
+
+	if (!path_currently_selected &&
+	    gtk_tree_model_get_iter(store, &iter, tpath))
+		{
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
+		}
+	else
+		{
+		VFLIST(vf)->select_fd = NULL;
+		}
+
+	if (vf->layout &&
+	    !VFLIST(vf)->select_idle_id)
+		{
+		VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
+		}
+
+	return TRUE;
+}
+
+static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
+{
+	ViewFile *vf = data;
+	vflist_set_expanded(vf, iter, TRUE);
+}
+
+static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
+{
+	ViewFile *vf = data;
+	vflist_set_expanded(vf, iter, FALSE);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * misc
+ *-----------------------------------------------------------------------------
+ */
+
+
+static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
+ {
+	gboolean multiline = vflist_is_multiline(vf);
+	gchar *text;
+
+	if (multiline)
+		{
+		text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
+		}
+	else
+		{
+		text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
+		}
+	return text;
+}
+
+static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
+{
+	GtkTreeStore *store;
+	gchar *name;
+	gchar *sidecars;
+	gchar *size;
+	gchar *time;
+	gchar *formatted;
+
+	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+
+	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
+					FILE_COLUMN_NAME, &name,
+					FILE_COLUMN_SIDECARS, &sidecars,
+					FILE_COLUMN_SIZE, &size,
+					FILE_COLUMN_DATE, &time,
+					-1);
+	formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
+
+	gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
+					FILE_COLUMN_EXPANDED, expanded,
+					-1);
+	g_free(time);
+	g_free(size);
+	g_free(sidecars);
+	g_free(name);
+	g_free(formatted);
+}
+
+static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
+{
+	gchar *size;
+	gchar *sidecars = NULL;
+	gchar *name;
+	const gchar *time = text_from_time(fd->date);
+	gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
+	const gchar *disabled_grouping;
+	gchar *formatted;
+	gboolean expanded = FALSE;
+
+	if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
+		{
+		gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
+		}
+
+	sidecars = file_data_sc_list_to_string(fd);
+
+	disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
+	name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
+	size = text_from_size(fd->size);
+
+	formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
+
+	gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
+					FILE_COLUMN_VERSION, fd->version,
+					FILE_COLUMN_THUMB, fd->thumb_pixbuf,
+					FILE_COLUMN_FORMATTED, formatted,
+					FILE_COLUMN_SIDECARS, sidecars,
+					FILE_COLUMN_NAME, name,
+					FILE_COLUMN_SIZE, size,
+					FILE_COLUMN_DATE, time,
+#define STORE_SET_IS_SLOW 1
+#if STORE_SET_IS_SLOW
+/* this is 3x faster on a directory with 20000 files */
+					FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
+					FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
+					FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
+					FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
+					FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
+					FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
+#if FILEDATA_MARKS_SIZE != 6
+#error this needs to be updated
+#endif
+#endif
+					FILE_COLUMN_COLOR, FALSE, -1);
+
+#if !STORE_SET_IS_SLOW
+	{
+	gint i;
+	for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
+		gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
+	}
+#endif
+	g_free(size);
+	g_free(sidecars);
+	g_free(name);
+	g_free(formatted);
+}
+
+static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
+{
+	GList *work;
+	GtkTreeIter iter;
+	gboolean valid;
+	gint num_ordered = 0;
+	gint num_prepended = 0;
+
+	valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
+
+	work = list;
+	while (work)
+		{
+		gint match;
+		FileData *fd = work->data;
+		gboolean done = FALSE;
+
+		while (!done)
+			{
+			FileData *old_fd = NULL;
+			gint old_version = 0;
+
+			if (valid)
+				{
+				gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+						   FILE_COLUMN_POINTER, &old_fd,
+						   FILE_COLUMN_VERSION, &old_version,
+						   -1);
+
+				if (fd == old_fd)
+					{
+					match = 0;
+					}
+				else
+					{
+					if (parent_iter)
+						match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
+					else
+						match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
+
+					if (match == 0) g_warning("multiple fd for the same path");
+					}
+
+				}
+			else
+				{
+				match = -1;
+				}
+
+			if (match < 0)
+				{
+				GtkTreeIter new;
+
+				if (valid)
+					{
+					num_ordered++;
+					gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
+					}
+				else
+					{
+					/*
+					    here should be used gtk_tree_store_append, but this function seems to be O(n)
+					    and it seems to be much faster to add new entries to the beginning and reorder later
+					*/
+					num_prepended++;
+					gtk_tree_store_prepend(store, &new, parent_iter);
+					}
+
+				vflist_setup_iter(vf, store, &new, file_data_ref(fd));
+				vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
+
+				if (g_list_find(selected, fd))
+					{
+					/* renamed files - the same fd appears at different position - select it again*/
+					GtkTreeSelection *selection;
+					selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+					gtk_tree_selection_select_iter(selection, &new);
+					}
+
+				done = TRUE;
+				}
+			else if (match > 0)
+				{
+				file_data_unref(old_fd);
+				valid = gtk_tree_store_remove(store, &iter);
+				}
+			else
+				{
+				num_ordered++;
+				if (fd->version != old_version || force)
+					{
+					vflist_setup_iter(vf, store, &iter, fd);
+					vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
+					}
+
+				if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
+
+				done = TRUE;
+				}
+			}
+		work = work->next;
+		}
+
+	while (valid)
+		{
+		FileData *old_fd;
+		gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
+		file_data_unref(old_fd);
+
+		valid = gtk_tree_store_remove(store, &iter);
+		}
+
+	/* move the prepended entries to the correct position */
+	if (num_prepended)
+		{
+		gint i;
+		gint num_total = num_prepended + num_ordered;
+		gint *new_order = g_malloc(num_total * sizeof(gint));
+
+		for (i = 0; i < num_total; i++)
+			{
+			if (i < num_ordered)
+				new_order[i] = num_prepended + i;
+			else
+				new_order[i] = num_total - 1 - i;
+			}
+		gtk_tree_store_reorder(store, parent_iter, new_order);
+
+		g_free(new_order);
+		}
+}
+
+void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
+{
+	gint i;
+	GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
+	gint *new_order;
+	GtkTreeStore *store;
+	GList *work;
+
+	if (vf->sort_method == type && vf->sort_ascend == ascend) return;
+	if (!vf->list) return;
+
+	work = vf->list;
+	i = 0;
+	while (work)
+		{
+		FileData *fd = work->data;
+		g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
+		i++;
+		work = work->next;
+		}
+
+	vf->sort_method = type;
+	vf->sort_ascend = ascend;
+
+	vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
+
+	new_order = g_malloc(i * sizeof(gint));
+
+	work = vf->list;
+	i = 0;
+	while (work)
+		{
+		FileData *fd = work->data;
+		new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
+		i++;
+		work = work->next;
+		}
+
+	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+	gtk_tree_store_reorder(store, NULL, new_order);
+
+	g_free(new_order);
+	g_hash_table_destroy(fd_idx_hash);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * thumb updates
+ *-----------------------------------------------------------------------------
+ */
+
+
+void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
+{
+	GList *work = list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		if (fd->thumb_pixbuf) (*done)++;
+
+		if (fd->sidecar_files)
+			{
+			vflist_thumb_progress_count(fd->sidecar_files, count, done);
+			}
+		(*count)++;
+		}
+}
+
+void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
+{
+	GtkTreeStore *store;
+	GtkTreeIter iter;
+
+	if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
+
+	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+	gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
+}
+
+FileData *vflist_thumb_next_fd(ViewFile *vf)
+{
+	GtkTreePath *tpath;
+	FileData *fd = NULL;
+
+	/* first check the visible files */
+
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+		{
+		GtkTreeModel *store;
+		GtkTreeIter iter;
+		gboolean valid = TRUE;
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_path_free(tpath);
+		tpath = NULL;
+
+		while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
+			{
+			FileData *nfd;
+
+			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
+
+			if (!nfd->thumb_pixbuf) fd = nfd;
+
+			valid = gtk_tree_model_iter_next(store, &iter);
+			}
+		}
+
+	/* then find first undone */
+
+	if (!fd)
+		{
+		GList *work = vf->list;
+		while (work && !fd)
+			{
+			FileData *fd_p = work->data;
+			if (!fd_p->thumb_pixbuf)
+				fd = fd_p;
+			else
+				{
+				GList *work2 = fd_p->sidecar_files;
+
+				while (work2 && !fd)
+					{
+					fd_p = work2->data;
+					if (!fd_p->thumb_pixbuf) fd = fd_p;
+					work2 = work2->next;
+					}
+				}
+			work = work->next;
+			}
+		}
+
+	return fd;
+}
+
+
+void vflist_thumb_reset_all(ViewFile *vf)
+{
+	GList *work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		if (fd->thumb_pixbuf)
+			{
+			g_object_unref(fd->thumb_pixbuf);
+			fd->thumb_pixbuf = NULL;
+			}
+		work = work->next;
+		}
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * row stuff
+ *-----------------------------------------------------------------------------
+ */
+
+FileData *vflist_index_get_data(ViewFile *vf, gint row)
+{
+	return g_list_nth_data(vf->list, row);
+}
+
+gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
+{
+	gint p = 0;
+	GList *work, *work2;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *list_fd = work->data;
+		if (list_fd == fd) return p;
+
+		work2 = list_fd->sidecar_files;
+		while (work2)
+			{
+			/* FIXME: return the same index also for sidecars
+			   it is sufficient for next/prev navigation but it should be rewritten
+			   without using indexes at all
+			*/
+			FileData *sidecar_fd = work2->data;
+			if (sidecar_fd == fd) return p;
+			work2 = work2->next;
+			}
+
+		work = work->next;
+		p++;
+		}
+
+	return -1;
+}
+
+guint vflist_count(ViewFile *vf, gint64 *bytes)
+{
+	if (bytes)
+		{
+		gint64 b = 0;
+		GList *work;
+
+		work = vf->list;
+		while (work)
+			{
+			FileData *fd = work->data;
+			work = work->next;
+			b += fd->size;
+			}
+
+		*bytes = b;
+		}
+
+	return g_list_length(vf->list);
+}
+
+GList *vflist_get_list(ViewFile *vf)
+{
+	GList *list = NULL;
+	GList *work;
+
+	work = vf->list;
+	while (work)
+		{
+		FileData *fd = work->data;
+		work = work->next;
+
+		list = g_list_prepend(list, file_data_ref(fd));
+		}
+
+	return g_list_reverse(list);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * selections
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
+{
+	GtkTreeModel *store;
+	GtkTreeSelection *selection;
+	GList *slist;
+	GList *work;
+	gboolean found = FALSE;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	slist = gtk_tree_selection_get_selected_rows(selection, &store);
+	work = slist;
+	while (!found && work)
+		{
+		GtkTreePath *tpath = work->data;
+		FileData *fd_n;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
+		if (fd_n == fd) found = TRUE;
+		work = work->next;
+		}
+	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+	g_list_free(slist);
+
+	return found;
+}
+
+gboolean vflist_index_is_selected(ViewFile *vf, gint row)
+{
+	FileData *fd;
+
+	fd = vf_index_get_data(vf, row);
+	return vflist_row_is_selected(vf, fd);
+}
+
+guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
+{
+	GtkTreeModel *store;
+	GtkTreeSelection *selection;
+	GList *slist;
+	guint count;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	slist = gtk_tree_selection_get_selected_rows(selection, &store);
+
+	if (bytes)
+		{
+		gint64 b = 0;
+		GList *work;
+
+		work = slist;
+		while (work)
+			{
+			GtkTreePath *tpath = work->data;
+			GtkTreeIter iter;
+			FileData *fd;
+
+			gtk_tree_model_get_iter(store, &iter, tpath);
+			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+			b += fd->size;
+
+			work = work->next;
+			}
+
+		*bytes = b;
+		}
+
+	count = g_list_length(slist);
+	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+	g_list_free(slist);
+
+	return count;
+}
+
+GList *vflist_selection_get_list(ViewFile *vf)
+{
+	GtkTreeModel *store;
+	GtkTreeSelection *selection;
+	GList *slist;
+	GList *list = NULL;
+	GList *work;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	slist = gtk_tree_selection_get_selected_rows(selection, &store);
+	work = slist;
+	while (work)
+		{
+		GtkTreePath *tpath = work->data;
+		FileData *fd;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+
+		list = g_list_prepend(list, file_data_ref(fd));
+
+		if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
+			{
+			/* unexpanded - add whole group */
+			GList *work2 = fd->sidecar_files;
+			while (work2)
+				{
+				FileData *sfd = work2->data;
+				list = g_list_prepend(list, file_data_ref(sfd));
+				work2 = work2->next;
+				}
+			}
+
+		work = work->next;
+		}
+	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+	g_list_free(slist);
+
+	return g_list_reverse(list);
+}
+
+GList *vflist_selection_get_list_by_index(ViewFile *vf)
+{
+	GtkTreeModel *store;
+	GtkTreeSelection *selection;
+	GList *slist;
+	GList *list = NULL;
+	GList *work;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	slist = gtk_tree_selection_get_selected_rows(selection, &store);
+	work = slist;
+	while (work)
+		{
+		GtkTreePath *tpath = work->data;
+		FileData *fd;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+
+		list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
+
+		work = work->next;
+		}
+	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+	g_list_free(slist);
+
+	return g_list_reverse(list);
+}
+
+void vflist_select_all(ViewFile *vf)
+{
+	GtkTreeSelection *selection;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_selection_select_all(selection);
+
+	VFLIST(vf)->select_fd = NULL;
+}
+
+void vflist_select_none(ViewFile *vf)
+{
+	GtkTreeSelection *selection;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_selection_unselect_all(selection);
+}
+
+static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
+{
+	GtkTreePath *tpath;
+	gboolean result;
+
+	tpath = gtk_tree_model_get_path(store, iter);
+	result = gtk_tree_path_prev(tpath);
+	if (result)
+		gtk_tree_model_get_iter(store, iter, tpath);
+
+	gtk_tree_path_free(tpath);
+
+	return result;
+}
+
+static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
+{
+	if (!gtk_tree_model_get_iter_first(store, iter))
+		return FALSE;
+
+	while (TRUE)
+		{
+		GtkTreeIter next = *iter;
+
+		if (gtk_tree_model_iter_next(store, &next))
+			*iter = next;
+		else
+			break;
+		}
+
+	return TRUE;
+}
+
+void vflist_select_invert(ViewFile *vf)
+{
+	GtkTreeIter iter;
+	GtkTreeSelection *selection;
+	GtkTreeModel *store;
+	gboolean valid;
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+
+	/* Backward iteration prevents scrolling to the end of the list,
+	 * it scrolls to the first selected row instead. */
+	valid = tree_model_get_iter_last(store, &iter);
+
+	while (valid)
+		{
+		gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
+
+		if (selected)
+			gtk_tree_selection_unselect_iter(selection, &iter);
+		else
+			gtk_tree_selection_select_iter(selection, &iter);
+
+		valid = tree_model_iter_prev(store, &iter);
+		}
+}
+
+void vflist_select_by_fd(ViewFile *vf, FileData *fd)
+{
+	GtkTreeIter iter;
+
+	if (vflist_find_row(vf, fd, &iter) < 0) return;
+
+	tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
+
+	if (!vflist_row_is_selected(vf, fd))
+		{
+		GtkTreeSelection *selection;
+		GtkTreeModel *store;
+		GtkTreePath *tpath;
+
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+		gtk_tree_selection_unselect_all(selection);
+		gtk_tree_selection_select_iter(selection, &iter);
+		vflist_move_cursor(vf, &iter);
+
+		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+		tpath = gtk_tree_model_get_path(store, &iter);
+		gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
+		gtk_tree_path_free(tpath);
+		}
+}
+
+static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
+{
+	GList *work;
+	FileData *fd = NULL;
+
+	if (sel_fd->parent) sel_fd = sel_fd->parent;
+	work = vf->list;
+
+	while (work)
+		{
+		gint match;
+		fd = work->data;
+		work = work->next;
+
+		match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
+
+		if (match >= 0) break;
+		}
+
+	if (fd) vflist_select_by_fd(vf, fd);
+
+}
+
+void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
+{
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+	GtkTreeSelection *selection;
+	gboolean valid;
+	gint n = mark - 1;
+
+	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
+
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+
+	valid = gtk_tree_model_get_iter_first(store, &iter);
+	while (valid)
+		{
+		FileData *fd;
+		gboolean mark_val, selected;
+		gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
+
+		mark_val = file_data_get_mark(fd, n);
+		selected = gtk_tree_selection_iter_is_selected(selection, &iter);
+
+		switch (mode)
+			{
+			case MTS_MODE_SET: selected = mark_val;
+				break;
+			case MTS_MODE_OR: selected = mark_val || selected;
+				break;
+			case MTS_MODE_AND: selected = mark_val && selected;
+				break;
+			case MTS_MODE_MINUS: selected = !mark_val && selected;
+				break;
+			}
+
+		if (selected)
+			gtk_tree_selection_select_iter(selection, &iter);
+		else
+			gtk_tree_selection_unselect_iter(selection, &iter);
+
+		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
+		}
+}
+
+void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
+{
+	GtkTreeModel *store;
+	GtkTreeSelection *selection;
+	GList *slist;
+	GList *work;
+	gint n = mark - 1;
+
+	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	slist = gtk_tree_selection_get_selected_rows(selection, &store);
+	work = slist;
+	while (work)
+		{
+		GtkTreePath *tpath = work->data;
+		FileData *fd;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter(store, &iter, tpath);
+		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
+
+		/* the change has a very limited range and the standard notification would trigger
+		   complete re-read of the directory - try to do only minimal update instead */
+		file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
+
+		switch (mode)
+			{
+			case STM_MODE_SET: file_data_set_mark(fd, n, 1);
+				break;
+			case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
+				break;
+			case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
+				break;
+			}
+
+		if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
+			{
+			vf_refresh_idle(vf);
+			}
+		else
+			{
+			/* mark functions can have various side effects - update all columns to be sure */
+			vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
+			/* mark functions can change sidecars too */
+			vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
+			}
+
+
+		file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
+
+		work = work->next;
+		}
+	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+	g_list_free(slist);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * core (population)
+ *-----------------------------------------------------------------------------
+ */
+
+static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
+{
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *cell;
+	GList *list;
+
+	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
+	if (!column) return;
+
+	gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
+
+	list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
+	if (!list) return;
+	cell = list->data;
+	g_list_free(list);
+
+	g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
+	gtk_tree_view_column_set_visible(column, thumb);
+
+	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
+	if (!column) return;
+	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
+
+	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
+	if (!column) return;
+	gtk_tree_view_column_set_visible(column, !multiline);
+
+	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
+	if (!column) return;
+	gtk_tree_view_column_set_visible(column, !multiline);
+}
+
+static gboolean vflist_is_multiline(ViewFile *vf)
+{
+	return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
+}
+
+
+static void vflist_populate_view(ViewFile *vf, gboolean force)
+{
+	GtkTreeStore *store;
+	GList *selected;
+
+	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+
+	vf_thumb_stop(vf);
+
+	if (!vf->list)
+		{
+		vflist_store_clear(vf, FALSE);
+		vf_send_update(vf);
+		return;
+		}
+
+	vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
+
+	selected = vflist_selection_get_list(vf);
+
+	vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
+
+	if (selected && vflist_selection_count(vf, NULL) == 0)
+		{
+		/* all selected files disappeared */
+		vflist_select_closest(vf, selected->data);
+		}
+
+	filelist_free(selected);
+
+	vf_send_update(vf);
+	vf_thumb_update(vf);
+}
+
+gboolean vflist_refresh(ViewFile *vf)
+{
+	GList *old_list;
+	gboolean ret = TRUE;
+
+	old_list = vf->list;
+	vf->list = NULL;
+
+	DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
+	if (vf->dir_fd)
+		{
+		file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
+
+		ret = filelist_read(vf->dir_fd, &vf->list, NULL);
+
+		if (vf->marks_enabled)
+		        {
+		        // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
+		        // each time a mark is changed.
+		        file_data_lock_list(vf->list);
+		        }
+	        else
+			{
+			// FIXME: only do this when needed (aka when we just switched from
+			// FIXME: marks-enabled to marks-disabled)
+			file_data_unlock_list(vf->list);
+			}
+
+		vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
+		file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
+
+		DEBUG_1("%s vflist_refresh: sort", get_exec_time());
+		vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
+		}
+
+	DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
+
+	vflist_populate_view(vf, FALSE);
+
+	DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
+
+	filelist_free(old_list);
+	DEBUG_1("%s vflist_refresh: done", get_exec_time());
+
+	return ret;
+}
+
+
+
+/* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
+
+#define CELL_HEIGHT_OVERRIDE 512
+
+static void cell_renderer_height_override(GtkCellRenderer *renderer)
+{
+	GParamSpec *spec;
+
+	spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
+	if (spec && G_IS_PARAM_SPEC_INT(spec))
+		{
+		GParamSpecInt *spec_int;
+
+		spec_int = G_PARAM_SPEC_INT(spec);
+		if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
+		}
+}
+
+static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
+{
+	static GdkColor color;
+	static GtkWidget *done = NULL;
+
+	if (done != widget)
+		{
+		GtkStyle *style;
+
+		style = gtk_widget_get_style(widget);
+		memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
+		shift_color(&color, -1, 0);
+		done = widget;
+		}
+
+	return &color;
+}
+
+static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+				     GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+	ViewFile *vf = data;
+	gboolean set;
+
+	gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
+	g_object_set(G_OBJECT(cell),
+		     "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
+		     "cell-background-set", set, NULL);
+}
+
+static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
+{
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_title(column, title);
+	gtk_tree_view_column_set_min_width(column, 4);
+
+	if (!image)
+		{
+		gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+		renderer = gtk_cell_renderer_text_new();
+		if (right_justify)
+			{
+			g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+			}
+		gtk_tree_view_column_pack_start(column, renderer, TRUE);
+		gtk_tree_view_column_add_attribute(column, renderer, "text", n);
+		if (expand)
+			gtk_tree_view_column_set_expand(column, TRUE);
+		}
+	else
+		{
+		gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+		renderer = gtk_cell_renderer_pixbuf_new();
+		cell_renderer_height_override(renderer);
+		gtk_tree_view_column_pack_start(column, renderer, TRUE);
+		gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
+		}
+
+	gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
+	g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
+	g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
+
+	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
+}
+
+static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
+{
+	ViewFile *vf = data;
+	GtkTreeStore *store;
+	GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+	GtkTreeIter iter;
+	FileData *fd;
+	gboolean marked;
+	guint col_idx;
+
+	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
+	if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
+		return;
+
+	col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
+
+	g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
+
+	gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
+	marked = !marked;
+
+	/* the change has a very limited range and the standard notification would trigger
+	   complete re-read of the directory - try to do only minimal update instead */
+	file_data_unregister_notify_func(vf_notify_cb, vf);
+	file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
+	if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
+		{
+		vf_refresh_idle(vf);
+		}
+	else
+		{
+		/* mark functions can have various side effects - update all columns to be sure */
+		vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
+		/* mark functions can change sidecars too */
+		vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
+		}
+	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
+
+	gtk_tree_path_free(path);
+}
+
+static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
+{
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	renderer = gtk_cell_renderer_toggle_new();
+	column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
+
+	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+	g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
+	g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
+
+	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
+	gtk_tree_view_column_set_fixed_width(column, 22);
+	gtk_tree_view_column_set_visible(column, vf->marks_enabled);
+
+
+	g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * base
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
+{
+	gboolean ret;
+	if (!dir_fd) return FALSE;
+	if (vf->dir_fd == dir_fd) return TRUE;
+
+	file_data_unref(vf->dir_fd);
+	vf->dir_fd = file_data_ref(dir_fd);
+
+	/* force complete reload */
+	vflist_store_clear(vf, TRUE);
+
+	filelist_free(vf->list);
+	vf->list = NULL;
+
+	ret = vf_refresh(vf);
+	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
+	return ret;
+}
+
+void vflist_destroy_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	file_data_unregister_notify_func(vf_notify_cb, vf);
+
+	vflist_select_idle_cancel(vf);
+	vf_refresh_idle_cancel(vf);
+	vf_thumb_stop(vf);
+
+	filelist_free(vf->list);
+}
+
+ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
+{
+	GtkTreeStore *store;
+	GtkTreeSelection *selection;
+	GType flist_types[FILE_COLUMN_COUNT];
+	gint i;
+	gint column;
+
+	vf->info = g_new0(ViewFileInfoList, 1);
+
+	flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
+	flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
+	flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
+	flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
+	flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
+	flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
+	flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
+	flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
+	flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
+	flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
+	for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
+		flist_types[i] = G_TYPE_BOOLEAN;
+
+	store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
+
+	vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+	g_object_unref(store);
+
+	g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
+			 G_CALLBACK(vflist_expand_cb), vf);
+
+	g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
+			 G_CALLBACK(vflist_collapse_cb), vf);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
+	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
+	gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
+
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
+	gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
+
+	column = 0;
+
+	for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
+		{
+		vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
+		g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
+		column++;
+		}
+
+	vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
+	g_assert(column == FILE_VIEW_COLUMN_THUMB);
+	column++;
+
+	vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
+	g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
+	column++;
+
+	vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
+	g_assert(column == FILE_VIEW_COLUMN_SIZE);
+	column++;
+
+	vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
+	g_assert(column == FILE_VIEW_COLUMN_DATE);
+	column++;
+
+	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
+	return vf;
+}
+
+void vflist_thumb_set(ViewFile *vf, gboolean enable)
+{
+	if (VFLIST(vf)->thumbs_enabled == enable) return;
+
+	VFLIST(vf)->thumbs_enabled = enable;
+
+	/* vflist_populate_view is better than vf_refresh:
+	   - no need to re-read the directory
+	   - force update because the formatted string has changed
+	*/
+	if (vf->layout)
+		{
+		vflist_populate_view(vf, TRUE);
+		gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
+		}
+}
+
+void vflist_marks_set(ViewFile *vf, gboolean enable)
+{
+	GList *columns, *work;
+
+	columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
+
+	work = columns;
+	while (work)
+		{
+		GtkTreeViewColumn *column = work->data;
+		gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
+		work = work->next;
+
+		if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
+			gtk_tree_view_column_set_visible(column, enable);
+		}
+
+	if (enable)
+		{
+		// Previously disabled, which means that vf->list is complete
+		file_data_lock_list(vf->list);
+		}
+	else
+		{
+		// Previously enabled, which means that vf->list is incomplete
+		}
+
+	g_list_free(columns);
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/view_file/view_file_list.h	Sat Jul 08 10:24:19 2017 +0100
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2004 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 VIEW_FILE_VIEW_FILE_LIST_H
+#define VIEW_FILE_VIEW_FILE_LIST_H
+
+#include "filedata.h"
+
+gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
+gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
+gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
+
+void vflist_dnd_init(ViewFile *vf);
+
+void vflist_destroy_cb(GtkWidget *widget, gpointer data);
+ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd);
+
+gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd);
+gboolean vflist_refresh(ViewFile *vf);
+
+void vflist_thumb_set(ViewFile *vf, gboolean enable);
+void vflist_marks_set(ViewFile *vf, gboolean enable);
+void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend);
+
+GList *vflist_selection_get_one(ViewFile *vf, FileData *fd);
+GList *vflist_pop_menu_file_list(ViewFile *vf);
+void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data);
+void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data);
+void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data);
+void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data);
+void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data);
+
+FileData *vflist_index_get_data(ViewFile *vf, gint row);
+gint vflist_index_by_fd(ViewFile *vf, FileData *fd);
+guint vflist_count(ViewFile *vf, gint64 *bytes);
+GList *vflist_get_list(ViewFile *vf);
+
+gboolean vflist_index_is_selected(ViewFile *vf, gint row);
+guint vflist_selection_count(ViewFile *vf, gint64 *bytes);
+GList *vflist_selection_get_list(ViewFile *vf);
+GList *vflist_selection_get_list_by_index(ViewFile *vf);
+
+void vflist_select_all(ViewFile *vf);
+void vflist_select_none(ViewFile *vf);
+void vflist_select_invert(ViewFile *vf);
+void vflist_select_by_fd(ViewFile *vf, FileData *fd);
+
+void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode);
+void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode);
+
+void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set);
+
+void vflist_thumb_progress_count(GList *list, gint *count, gint *done);
+void vflist_set_thumb_fd(ViewFile *vf, FileData *fd);
+FileData *vflist_thumb_next_fd(ViewFile *vf);
+void vflist_thumb_reset_all(ViewFile *vf);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_icon.c	Fri Jul 07 12:11:56 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2300 +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 "view_file_icon.h"
-
-#include "bar.h"
-#include "cellrenderericon.h"
-#include "collect.h"
-#include "collect-io.h"
-#include "collect-table.h"
-#include "dnd.h"
-#include "editors.h"
-#include "img-view.h"
-#include "filedata.h"
-#include "layout.h"
-#include "layout_image.h"
-#include "menu.h"
-#include "metadata.h"
-#include "thumb.h"
-#include "utilops.h"
-#include "ui_fileops.h"
-#include "ui_menu.h"
-#include "ui_tree_edit.h"
-#include "uri_utils.h"
-#include "view_file.h"
-
-#include <gdk/gdkkeysyms.h> /* for keyboard values */
-
-
-/* between these, the icon width is increased by thumb_max_width / 2 */
-#define THUMB_MIN_ICON_WIDTH 128
-#define THUMB_MAX_ICON_WIDTH 150
-
-#define VFICON_MAX_COLUMNS 32
-#define THUMB_BORDER_PADDING 2
-
-#define VFICON_TIP_DELAY 500
-
-enum {
-	FILE_COLUMN_POINTER = 0,
-	FILE_COLUMN_COUNT
-};
-
-static gint vficon_index_by_id(ViewFile *vf, IconData *in_id);
-
-static IconData *vficon_icon_data(ViewFile *vf, FileData *fd)
-{
-	IconData *id = NULL;
-	GList *work;
-
-	if (!fd) return NULL;
-	work = vf->list;
-	while (work && !id)
-		{
-		IconData *chk = work->data;
-		work = work->next;
-		if (chk->fd == fd) id = chk;
-		}
-	return id;
-}
-
-static void iconlist_free(GList *list)
-{
-	GList *work = list;
-	while (work)
-		{
-		IconData *id = work->data;
-		file_data_unref(id->fd);
-		g_free(id);
-		work = work->next;
-		}
-
-	g_list_free(list);
-
-}
-
-gint iconlist_sort_file_cb(gpointer a, gpointer b)
-{
-	IconData *ida = a;
-	IconData *idb = b;
-	return filelist_sort_compare_filedata(ida->fd, idb->fd);
-}
-
-GList *iconlist_sort(GList *list, SortType method, gboolean ascend)
-{
-	return filelist_sort_full(list, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
-}
-
-GList *iconlist_insert_sort(GList *list, IconData *id, SortType method, gboolean ascend)
-{
-	return filelist_insert_sort_full(list, id, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
-}
-
-
-static void vficon_toggle_filenames(ViewFile *vf);
-static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter);
-static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative);
-static void vficon_set_focus(ViewFile *vf, IconData *id);
-static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force);
-
-
-/*
- *-----------------------------------------------------------------------------
- * pop-up menu
- *-----------------------------------------------------------------------------
- */
-
-GList *vficon_selection_get_one(ViewFile *vf, FileData *fd)
-{
-	return g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
-}
-
-GList *vficon_pop_menu_file_list(ViewFile *vf)
-{
-	if (!VFICON(vf)->click_id) return NULL;
-
-	if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
-		{
-		return vf_selection_get_list(vf);
-		}
-
-	return vficon_selection_get_one(vf, VFICON(vf)->click_id->fd);
-}
-
-void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	if (!VFICON(vf)->click_id) return;
-
-	if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
-		{
-		GList *list;
-
-		list = vf_selection_get_list(vf);
-		view_window_new_from_list(list);
-		filelist_free(list);
-		}
-	else
-		{
-		view_window_new(VFICON(vf)->click_id->fd);
-		}
-}
-
-void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview);
-}
-
-void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vficon_toggle_filenames(vf);
-}
-
-void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vf_refresh(vf);
-}
-
-void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-	vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
-	VFICON(vf)->click_id = NULL;
-	vf->popup = NULL;
-}
-
-/*
- *-------------------------------------------------------------------
- * signals
- *-------------------------------------------------------------------
- */
-
-static void vficon_send_layout_select(ViewFile *vf, IconData *id)
-{
-	FileData *read_ahead_fd = NULL;
-	FileData *sel_fd;
-	FileData *cur_fd;
-
-	if (!vf->layout || !id || !id->fd) return;
-
-	sel_fd = id->fd;
-
-	cur_fd = layout_image_get_fd(vf->layout);
-	if (sel_fd == cur_fd) return; /* no change */
-
-	if (options->image.enable_read_ahead)
-		{
-		gint row;
-
-		row = g_list_index(vf->list, id);
-		if (row > vficon_index_by_fd(vf, cur_fd) &&
-		    (guint) (row + 1) < vf_count(vf, NULL))
-			{
-			read_ahead_fd = vf_index_get_data(vf, row + 1);
-			}
-		else if (row > 0)
-			{
-			read_ahead_fd = vf_index_get_data(vf, row - 1);
-			}
-		}
-
-	layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
-}
-
-static void vficon_toggle_filenames(ViewFile *vf)
-{
-	GtkAllocation allocation;
-	VFICON(vf)->show_text = !VFICON(vf)->show_text;
-	options->show_icon_names = VFICON(vf)->show_text;
-
-	gtk_widget_get_allocation(vf->listview, &allocation);
-	vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
-}
-
-static gint vficon_get_icon_width(ViewFile *vf)
-{
-	gint width;
-
-	if (!VFICON(vf)->show_text) return options->thumbnails.max_width;
-
-	width = options->thumbnails.max_width + options->thumbnails.max_width / 2;
-	if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
-	if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width;
-
-	return width;
-}
-
-/*
- *-------------------------------------------------------------------
- * misc utils
- *-------------------------------------------------------------------
- */
-
-static gboolean vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint *col)
-{
-	gint n;
-
-	n = g_list_index(vf->list, id);
-
-	if (n < 0) return FALSE;
-
-	*row = n / VFICON(vf)->columns;
-	*col = n - (*row * VFICON(vf)->columns);
-
-	return TRUE;
-}
-
-static gboolean vficon_find_iter(ViewFile *vf, IconData *id, GtkTreeIter *iter, gint *column)
-{
-	GtkTreeModel *store;
-	gint row, col;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	if (!vficon_find_position(vf, id, &row, &col)) return FALSE;
-	if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
-	if (column) *column = col;
-
-	return TRUE;
-}
-
-static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
-{
-	GtkTreeModel *store;
-	GtkTreeIter p;
-
-	if (row < 0 || col < 0) return NULL;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	if (gtk_tree_model_iter_nth_child(store, &p, NULL, row))
-		{
-		GList *list;
-
-		gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1);
-		if (!list) return NULL;
-
-		if (iter) *iter = p;
-
-		return g_list_nth_data(list, col);
-		}
-
-	return NULL;
-}
-
-static IconData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
-{
-	GtkTreePath *tpath;
-	GtkTreeViewColumn *column;
-
-	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
-					  &tpath, &column, NULL, NULL))
-		{
-		GtkTreeModel *store;
-		GtkTreeIter row;
-		GList *list;
-		gint n;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		gtk_tree_model_get_iter(store, &row, tpath);
-		gtk_tree_path_free(tpath);
-
-		gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
-
-		n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number"));
-		if (list)
-			{
-			if (iter) *iter = row;
-			return g_list_nth_data(list, n);
-			}
-		}
-
-	return NULL;
-}
-
-static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeModel *store;
-	GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
-	GtkTreeIter row;
-	gint column;
-	GList *list;
-	guint toggled_mark;
-	IconData *id;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	if (!path || !gtk_tree_model_get_iter(store, &row, path))
-		return;
-
-	gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
-
-	column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number"));
-	g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL);
-
-	id = g_list_nth_data(list, column);
-	if (id)
-		{
-		FileData *fd = id->fd;
-		file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark));
-		}
-}
-
-
-/*
- *-------------------------------------------------------------------
- * tooltip type window
- *-------------------------------------------------------------------
- */
-
-static void tip_show(ViewFile *vf)
-{
-	GtkWidget *label;
-	gint x, y;
-#if GTK_CHECK_VERSION(3,0,0)
-	GdkDisplay *display;
-	GdkDeviceManager *device_manager;
-	GdkDevice *device;
-#endif
-
-	if (VFICON(vf)->tip_window) return;
-
-#if GTK_CHECK_VERSION(3,0,0)
-	device_manager = gdk_display_get_device_manager(gdk_window_get_display(
-						gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview))));
-	device = gdk_device_manager_get_client_pointer(device_manager);
-	gdk_window_get_device_position(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)),
-						device, &x, &y, NULL);
-#else
-	gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL);
-#endif
-
-	VFICON(vf)->tip_id = vficon_find_data_by_coord(vf, x, y, NULL);
-	if (!VFICON(vf)->tip_id) return;
-
-	VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
-	gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE);
-	gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2);
-
-	label = gtk_label_new(VFICON(vf)->tip_id->fd->name);
-
-	g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label);
-	gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label);
-	gtk_widget_show(label);
-
-#if GTK_CHECK_VERSION(3,0,0)
-	display = gdk_display_get_default();
-	device_manager = gdk_display_get_device_manager(display);
-	device = gdk_device_manager_get_client_pointer(device_manager);
-	gdk_device_get_position(device, NULL, &x, &y);
-#else
-	gdk_window_get_pointer(NULL, &x, &y, NULL);
-#endif
-
-	if (!gtk_widget_get_realized(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window);
-	gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
-	gtk_widget_show(VFICON(vf)->tip_window);
-}
-
-static void tip_hide(ViewFile *vf)
-{
-	if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window);
-	VFICON(vf)->tip_window = NULL;
-}
-
-static gboolean tip_schedule_cb(gpointer data)
-{
-	ViewFile *vf = data;
-	GtkWidget *window;
-
-	if (!VFICON(vf)->tip_delay_id) return FALSE;
-
-	window = gtk_widget_get_toplevel(vf->listview);
-
-	if (gtk_widget_get_sensitive(window) &&
-	    gtk_window_has_toplevel_focus(GTK_WINDOW(window)))
-		{
-		tip_show(vf);
-		}
-
-	VFICON(vf)->tip_delay_id = 0;
-	return FALSE;
-}
-
-static void tip_schedule(ViewFile *vf)
-{
-	tip_hide(vf);
-
-	if (VFICON(vf)->tip_delay_id)
-		{
-		g_source_remove(VFICON(vf)->tip_delay_id);
-		VFICON(vf)->tip_delay_id = 0;
-		}
-
-	if (!VFICON(vf)->show_text)
-		{
-		VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf);
-		}
-}
-
-static void tip_unschedule(ViewFile *vf)
-{
-	tip_hide(vf);
-
-	if (VFICON(vf)->tip_delay_id)
-		{
-		g_source_remove(VFICON(vf)->tip_delay_id);
-		VFICON(vf)->tip_delay_id = 0;
-		}
-}
-
-static void tip_update(ViewFile *vf, IconData *id)
-{
-#if GTK_CHECK_VERSION(3,0,0)
-	GdkDisplay *display = gdk_display_get_default();
-	GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
-	GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager);
-#endif
-
-	if (VFICON(vf)->tip_window)
-		{
-		gint x, y;
-
-#if GTK_CHECK_VERSION(3,0,0)
-		gdk_device_get_position(device, NULL, &x, &y);
-#else
-		gdk_window_get_pointer(NULL, &x, &y, NULL);
-#endif
-		gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
-
-		if (id != VFICON(vf)->tip_id)
-			{
-			GtkWidget *label;
-
-			VFICON(vf)->tip_id = id;
-
-			if (!VFICON(vf)->tip_id)
-				{
-				tip_hide(vf);
-				tip_schedule(vf);
-				return;
-				}
-
-			label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label");
-			gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_id->fd->name);
-			}
-		}
-	else
-		{
-		tip_schedule(vf);
-		}
-}
-
-/*
- *-------------------------------------------------------------------
- * dnd
- *-------------------------------------------------------------------
- */
-
-static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
-			   GtkSelectionData *selection_data, guint info,
-			   guint time, gpointer data)
-{
-	ViewFile *vf = data;
-	GList *list = NULL;
-
-	if (!VFICON(vf)->click_id) return;
-
-	if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
-		{
-		list = vf_selection_get_list(vf);
-		}
-	else
-		{
-		list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd));
-		}
-
-	if (!list) return;
-	uri_selection_data_set_uris_from_filelist(selection_data, list);
-	filelist_free(list);
-}
-
-static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
-				      int x, int y, GtkSelectionData *selection,
-				      guint info, guint time, gpointer data)
-{
-	ViewFile *vf = data;
-
-	if (info == TARGET_TEXT_PLAIN) {
-		IconData *id = vficon_find_data_by_coord(vf, x, y, NULL);
-
-		if (id && id->fd) {
-			/* Add keywords to file */
-			FileData *fd = id->fd;
-			gchar *str = (gchar *) gtk_selection_data_get_text(selection);
-			GList *kw_list = string_to_keywords_list(str);
-
-			metadata_append_list(fd, KEYWORD_KEY, kw_list);
-			string_list_free(kw_list);
-			g_free(str);
-		}
-	}
-}
-
-static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
-{
-	ViewFile *vf = data;
-
-	tip_unschedule(vf);
-
-	if (VFICON(vf)->click_id && VFICON(vf)->click_id->fd->thumb_pixbuf)
-		{
-		gint items;
-
-		if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
-			items = g_list_length(VFICON(vf)->selection);
-		else
-			items = 1;
-
-		dnd_set_drag_icon(widget, context, VFICON(vf)->click_id->fd->thumb_pixbuf, items);
-		}
-}
-
-static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
-
-	if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
-		{
-		vf_refresh(vf);
-		}
-
-	tip_unschedule(vf);
-}
-
-void vficon_dnd_init(ViewFile *vf)
-{
-	gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
-			    dnd_file_drag_types, dnd_file_drag_types_count,
-			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-	gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
-			    dnd_file_drag_types, dnd_file_drag_types_count,
-			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-
-	g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
-			 G_CALLBACK(vficon_dnd_get), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
-			 G_CALLBACK(vficon_dnd_begin), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_end",
-			 G_CALLBACK(vficon_dnd_end), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
-			 G_CALLBACK(vficon_drag_data_received), vf);
-}
-
-/*
- *-------------------------------------------------------------------
- * cell updates
- *-------------------------------------------------------------------
- */
-
-static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value, GtkTreeIter *iter)
-{
-	GtkTreeModel *store;
-	GList *list;
-
-	if (!id) return;
-
-
-	if (id->selected == value) return;
-	id->selected = value;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	if (iter)
-		{
-		gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
-		if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1);
-		}
-	else
-		{
-		GtkTreeIter row;
-
-		if (vficon_find_iter(vf, id, &row, NULL))
-			{
-			gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
-			if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
-			}
-		}
-}
-
-static void vficon_selection_add(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
-{
-	if (!id) return;
-
-	vficon_selection_set(vf, id, id->selected | mask, iter);
-}
-
-static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
-{
-	if (!id) return;
-
-	vficon_selection_set(vf, id, id->selected & ~mask, iter);
-}
-
-void vficon_marks_set(ViewFile *vf, gint enable)
-{
-	GtkAllocation allocation;
-	gtk_widget_get_allocation(vf->listview, &allocation);
-	vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
-}
-
-/*
- *-------------------------------------------------------------------
- * selections
- *-------------------------------------------------------------------
- */
-
-static void vficon_verify_selections(ViewFile *vf)
-{
-	GList *work;
-
-	work = VFICON(vf)->selection;
-	while (work)
-		{
-		IconData *id = work->data;
-		work = work->next;
-
-		if (vficon_index_by_id(vf, id) >= 0) continue;
-
-		VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
-		}
-}
-
-void vficon_select_all(ViewFile *vf)
-{
-	GList *work;
-
-	g_list_free(VFICON(vf)->selection);
-	VFICON(vf)->selection = NULL;
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		work = work->next;
-
-		VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-		vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
-		}
-
-	vf_send_update(vf);
-}
-
-void vficon_select_none(ViewFile *vf)
-{
-	GList *work;
-
-	work = VFICON(vf)->selection;
-	while (work)
-		{
-		IconData *id = work->data;
-		work = work->next;
-
-		vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
-		}
-
-	g_list_free(VFICON(vf)->selection);
-	VFICON(vf)->selection = NULL;
-
-	vf_send_update(vf);
-}
-
-void vficon_select_invert(ViewFile *vf)
-{
-	GList *work;
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		work = work->next;
-
-		if (id->selected & SELECTION_SELECTED)
-			{
-			VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
-			vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
-			}
-		else
-			{
-			VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-			vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
-			}
-		}
-
-	vf_send_update(vf);
-}
-
-static void vficon_select(ViewFile *vf, IconData *id)
-{
-	VFICON(vf)->prev_selection = id;
-
-	if (!id || id->selected & SELECTION_SELECTED) return;
-
-	VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-	vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
-
-	vf_send_update(vf);
-}
-
-static void vficon_unselect(ViewFile *vf, IconData *id)
-{
-	VFICON(vf)->prev_selection = id;
-
-	if (!id || !(id->selected & SELECTION_SELECTED) ) return;
-
-	VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
-	vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
-
-	vf_send_update(vf);
-}
-
-static void vficon_select_util(ViewFile *vf, IconData *id, gboolean select)
-{
-	if (select)
-		{
-		vficon_select(vf, id);
-		}
-	else
-		{
-		vficon_unselect(vf, id);
-		}
-}
-
-static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *end, gboolean select)
-{
-	gint row1, col1;
-	gint row2, col2;
-	gint t;
-	gint i, j;
-
-	if (!vficon_find_position(vf, start, &row1, &col1) ||
-	    !vficon_find_position(vf, end, &row2, &col2) ) return;
-
-	VFICON(vf)->prev_selection = end;
-
-	if (!options->collections.rectangular_selection)
-		{
-		GList *work;
-		IconData *id;
-
-		if (g_list_index(vf->list, start) > g_list_index(vf->list, end))
-			{
-			id = start;
-			start = end;
-			end = id;
-			}
-
-		work = g_list_find(vf->list, start);
-		while (work)
-			{
-			id = work->data;
-			vficon_select_util(vf, id, select);
-
-			if (work->data != end)
-				work = work->next;
-			else
-				work = NULL;
-			}
-		return;
-		}
-
-	if (row2 < row1)
-		{
-		t = row1;
-		row1 = row2;
-		row2 = t;
-		}
-	if (col2 < col1)
-		{
-		t = col1;
-		col1 = col2;
-		col2 = t;
-		}
-
-	DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2);
-
-	for (i = row1; i <= row2; i++)
-		{
-		for (j = col1; j <= col2; j++)
-			{
-			IconData *id = vficon_find_data(vf, i, j, NULL);
-			if (id) vficon_select_util(vf, id, select);
-			}
-		}
-}
-
-gboolean vficon_index_is_selected(ViewFile *vf, gint row)
-{
-	IconData *id = g_list_nth_data(vf->list, row);
-
-	if (!id) return FALSE;
-
-	return (id->selected & SELECTION_SELECTED);
-}
-
-guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
-{
-	if (bytes)
-		{
-		gint64 b = 0;
-		GList *work;
-
-		work = VFICON(vf)->selection;
-		while (work)
-			{
-			IconData *id = work->data;
-			FileData *fd = id->fd;
-			g_assert(fd->magick == FD_MAGICK);
-			b += fd->size;
-
-			work = work->next;
-			}
-
-		*bytes = b;
-		}
-
-	return g_list_length(VFICON(vf)->selection);
-}
-
-GList *vficon_selection_get_list(ViewFile *vf)
-{
-	GList *list = NULL;
-	GList *work, *work2;
-
-	work = VFICON(vf)->selection;
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		g_assert(fd->magick == FD_MAGICK);
-
-		list = g_list_prepend(list, file_data_ref(fd));
-
-		work2 = fd->sidecar_files;
-		while (work2)
-			{
-			fd = work2->data;
-			list = g_list_prepend(list, file_data_ref(fd));
-			work2 = work2->next;
-			}
-
-		work = work->next;
-		}
-
-	list = g_list_reverse(list);
-
-	return list;
-}
-
-GList *vficon_selection_get_list_by_index(ViewFile *vf)
-{
-	GList *list = NULL;
-	GList *work;
-
-	work = VFICON(vf)->selection;
-	while (work)
-		{
-		list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data)));
-		work = work->next;
-		}
-
-	return g_list_reverse(list);
-}
-
-static void vficon_select_by_id(ViewFile *vf, IconData *id)
-{
-	if (!id) return;
-
-	if (!(id->selected & SELECTION_SELECTED))
-		{
-		vf_select_none(vf);
-		vficon_select(vf, id);
-		}
-
-	vficon_set_focus(vf, id);
-}
-
-void vficon_select_by_fd(ViewFile *vf, FileData *fd)
-{
-	IconData *id = NULL;
-	GList *work;
-
-	if (!fd) return;
-	work = vf->list;
-	while (work && !id)
-		{
-		IconData *chk = work->data;
-		work = work->next;
-		if (chk->fd == fd) id = chk;
-		}
-	vficon_select_by_id(vf, id);
-}
-
-void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
-{
-	GList *work;
-	gint n = mark - 1;
-
-	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		gboolean mark_val, selected;
-
-		g_assert(fd->magick == FD_MAGICK);
-
-		mark_val = file_data_get_mark(fd, n);
-		selected = (id->selected & SELECTION_SELECTED);
-
-		switch (mode)
-			{
-			case MTS_MODE_SET: selected = mark_val;
-				break;
-			case MTS_MODE_OR: selected = mark_val || selected;
-				break;
-			case MTS_MODE_AND: selected = mark_val && selected;
-				break;
-			case MTS_MODE_MINUS: selected = !mark_val && selected;
-				break;
-			}
-
-		vficon_select_util(vf, id, selected);
-
-		work = work->next;
-		}
-}
-
-void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
-{
-	GList *slist;
-	GList *work;
-	gint n = mark -1;
-
-	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
-
-	slist = vf_selection_get_list(vf);
-	work = slist;
-	while (work)
-		{
-		FileData *fd = work->data;
-
-		switch (mode)
-			{
-			case STM_MODE_SET: file_data_set_mark(fd, n, 1);
-				break;
-			case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
-				break;
-			case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
-				break;
-			}
-		work = work->next;
-		}
-	filelist_free(slist);
-}
-
-static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
-{
-	GList *work;
-	IconData *id = NULL;
-
-	if (sel_fd->parent) sel_fd = sel_fd->parent;
-	work = vf->list;
-
-	while (work)
-		{
-		gint match;
-		FileData *fd;
-
-		id = work->data;
-		fd = id->fd;
-		work = work->next;
-
-		match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
-
-		if (match >= 0) break;
-		}
-
-	if (id)
-		{
-		vficon_select(vf, id);
-		vficon_send_layout_select(vf, id);
-		}
-}
-
-
-/*
- *-------------------------------------------------------------------
- * focus
- *-------------------------------------------------------------------
- */
-
-static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative)
-{
-	gint new_row;
-	gint new_col;
-
-	if (relative)
-		{
-		new_row = VFICON(vf)->focus_row;
-		new_col = VFICON(vf)->focus_column;
-
-		new_row += row;
-		if (new_row < 0) new_row = 0;
-		if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1;
-
-		while (col != 0)
-			{
-			if (col < 0)
-				{
-				new_col--;
-				col++;
-				}
-			else
-				{
-				new_col++;
-				col--;
-				}
-
-			if (new_col < 0)
-				{
-				if (new_row > 0)
-					{
-					new_row--;
-					new_col = VFICON(vf)->columns - 1;
-					}
-				else
-					{
-					new_col = 0;
-					}
-				}
-			if (new_col >= VFICON(vf)->columns)
-				{
-				if (new_row < VFICON(vf)->rows - 1)
-					{
-					new_row++;
-					new_col = 0;
-					}
-				else
-					{
-					new_col = VFICON(vf)->columns - 1;
-					}
-				}
-			}
-		}
-	else
-		{
-		new_row = row;
-		new_col = col;
-
-		if (new_row >= VFICON(vf)->rows)
-			{
-			if (VFICON(vf)->rows > 0)
-				new_row = VFICON(vf)->rows - 1;
-			else
-				new_row = 0;
-			new_col = VFICON(vf)->columns - 1;
-			}
-		if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1;
-		}
-
-	if (new_row == VFICON(vf)->rows - 1)
-		{
-		gint l;
-
-		/* if we moved beyond the last image, go to the last image */
-
-		l = g_list_length(vf->list);
-		if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns;
-		if (new_col >= l) new_col = l - 1;
-		}
-
-	vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
-}
-
-static void vficon_set_focus(ViewFile *vf, IconData *id)
-{
-	GtkTreeIter iter;
-	gint row, col;
-
-	if (g_list_find(vf->list, VFICON(vf)->focus_id))
-		{
-		if (id == VFICON(vf)->focus_id)
-			{
-			/* ensure focus row col are correct */
-			vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
-#if GTK_CHECK_VERSION(3,0,0)
-/* FIXME: Refer to issue #467 on Github. The thumbnail position is not
- * preserved when the icon view is refreshed. Caused by an unknown call from
- * the idle loop. This patch hides the problem.
- */
-			if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL))
-				{
-				tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
-				}
-#endif
-			return;
-			}
-		vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
-		}
-
-	if (!vficon_find_position(vf, id, &row, &col))
-		{
-		VFICON(vf)->focus_id = NULL;
-		VFICON(vf)->focus_row = -1;
-		VFICON(vf)->focus_column = -1;
-		return;
-		}
-
-	VFICON(vf)->focus_id = id;
-	VFICON(vf)->focus_row = row;
-	VFICON(vf)->focus_column = col;
-	vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
-
-	if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL))
-		{
-		GtkTreePath *tpath;
-		GtkTreeViewColumn *column;
-		GtkTreeModel *store;
-
-		tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		tpath = gtk_tree_model_get_path(store, &iter);
-		/* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
-		column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS);
-		gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE);
-		gtk_tree_path_free(tpath);
-		}
-}
-
-/* used to figure the page up/down distances */
-static gint page_height(ViewFile *vf)
-{
-	GtkAdjustment *adj;
-	gint page_size;
-	gint row_height;
-	gint ret;
-
-	adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview));
-	page_size = (gint)gtk_adjustment_get_page_increment(adj);
-
-	row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2;
-	if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3;
-
-	ret = page_size / row_height;
-	if (ret < 1) ret = 1;
-
-	return ret;
-}
-
-/*
- *-------------------------------------------------------------------
- * keyboard
- *-------------------------------------------------------------------
- */
-
-static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-	gint column;
-	GtkTreePath *tpath;
-	gint cw, ch;
-
-	if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return;
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	tpath = gtk_tree_model_get_path(store, &iter);
-	tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
-	gtk_tree_path_free(tpath);
-	*y += ch;
-	popup_menu_position_clamp(menu, x, y, 0);
-}
-
-gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
-{
-	ViewFile *vf = data;
-	gint focus_row = 0;
-	gint focus_col = 0;
-	IconData *id;
-	gboolean stop_signal;
-
-	stop_signal = TRUE;
-	switch (event->keyval)
-		{
-		case GDK_KEY_Left: case GDK_KEY_KP_Left:
-			focus_col = -1;
-			break;
-		case GDK_KEY_Right: case GDK_KEY_KP_Right:
-			focus_col = 1;
-			break;
-		case GDK_KEY_Up: case GDK_KEY_KP_Up:
-			focus_row = -1;
-			break;
-		case GDK_KEY_Down: case GDK_KEY_KP_Down:
-			focus_row = 1;
-			break;
-		case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
-			focus_row = -page_height(vf);
-			break;
-		case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
-			focus_row = page_height(vf);
-			break;
-		case GDK_KEY_Home: case GDK_KEY_KP_Home:
-			focus_row = -VFICON(vf)->focus_row;
-			focus_col = -VFICON(vf)->focus_column;
-			break;
-		case GDK_KEY_End: case GDK_KEY_KP_End:
-			focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row;
-			focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
-			break;
-		case GDK_KEY_space:
-			id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-			if (id)
-				{
-				VFICON(vf)->click_id = id;
-				if (event->state & GDK_CONTROL_MASK)
-					{
-					gint selected;
-
-					selected = id->selected & SELECTION_SELECTED;
-					if (selected)
-						{
-						vficon_unselect(vf, id);
-						}
-					else
-						{
-						vficon_select(vf, id);
-						vficon_send_layout_select(vf, id);
-						}
-					}
-				else
-					{
-					vf_select_none(vf);
-					vficon_select(vf, id);
-					vficon_send_layout_select(vf, id);
-					}
-				}
-			break;
-		case GDK_KEY_Menu:
-			id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-			VFICON(vf)->click_id = id;
-
-			vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
-			tip_unschedule(vf);
-
-			vf->popup = vf_pop_menu(vf);
-			gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
-			break;
-		default:
-			stop_signal = FALSE;
-			break;
-		}
-
-	if (focus_row != 0 || focus_col != 0)
-		{
-		IconData *new_id;
-		IconData *old_id;
-
-		old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-		vficon_move_focus(vf, focus_row, focus_col, TRUE);
-		new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-
-		if (new_id != old_id)
-			{
-			if (event->state & GDK_SHIFT_MASK)
-				{
-				if (!options->collections.rectangular_selection)
-					{
-					vficon_select_region_util(vf, old_id, new_id, FALSE);
-					}
-				else
-					{
-					vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE);
-					}
-				vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE);
-				vficon_send_layout_select(vf, new_id);
-				}
-			else if (event->state & GDK_CONTROL_MASK)
-				{
-				VFICON(vf)->click_id = new_id;
-				}
-			else
-				{
-				VFICON(vf)->click_id = new_id;
-				vf_select_none(vf);
-				vficon_select(vf, new_id);
-				vficon_send_layout_select(vf, new_id);
-				}
-			}
-		}
-
-	if (stop_signal)
-		{
-		tip_unschedule(vf);
-		}
-
-	return stop_signal;
-}
-
-/*
- *-------------------------------------------------------------------
- * mouse
- *-------------------------------------------------------------------
- */
-
-static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
-{
-	ViewFile *vf = data;
-	IconData *id;
-
-	id = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL);
-	tip_update(vf, id);
-
-	return FALSE;
-}
-
-gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeIter iter;
-	IconData *id;
-
-	tip_unschedule(vf);
-
-	id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
-
-	VFICON(vf)->click_id = id;
-	vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
-
-	switch (bevent->button)
-		{
-		case MOUSE_BUTTON_LEFT:
-			if (!gtk_widget_has_focus(vf->listview))
-				{
-				gtk_widget_grab_focus(vf->listview);
-				}
-
-			if (bevent->type == GDK_2BUTTON_PRESS &&
-			    vf->layout)
-				{
-				vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
-				layout_image_full_screen_start(vf->layout);
-				}
-			break;
-		case MOUSE_BUTTON_RIGHT:
-			vf->popup = vf_pop_menu(vf);
-			gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
-			break;
-		default:
-			break;
-		}
-
-	return FALSE;
-}
-
-gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeIter iter;
-	IconData *id = NULL;
-	gboolean was_selected;
-
-	tip_schedule(vf);
-
-	if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
-		{
-		id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
-		}
-
-	if (VFICON(vf)->click_id)
-		{
-		vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
-		}
-
-	if (!id || VFICON(vf)->click_id != id) return TRUE;
-
-	was_selected = !!(id->selected & SELECTION_SELECTED);
-
-	switch (bevent->button)
-		{
-		case MOUSE_BUTTON_LEFT:
-			{
-			vficon_set_focus(vf, id);
-
-			if (bevent->state & GDK_CONTROL_MASK)
-				{
-				gboolean select;
-
-				select = !(id->selected & SELECTION_SELECTED);
-				if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
-					{
-					vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select);
-					}
-				else
-					{
-					vficon_select_util(vf, id, select);
-					}
-				}
-			else
-				{
-				vf_select_none(vf);
-
-				if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
-					{
-					vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE);
-					}
-				else
-					{
-					vficon_select_util(vf, id, TRUE);
-					was_selected = FALSE;
-					}
-				}
-			}
-			break;
-		case MOUSE_BUTTON_MIDDLE:
-			{
-			vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED));
-			}
-			break;
-		default:
-			break;
-		}
-
-	if (!was_selected && (id->selected & SELECTION_SELECTED))
-		{
-		vficon_send_layout_select(vf, id);
-		}
-
-	return TRUE;
-}
-
-static gboolean vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
-{
-	ViewFile *vf = data;
-
-	tip_unschedule(vf);
-	return FALSE;
-}
-
-/*
- *-------------------------------------------------------------------
- * population
- *-------------------------------------------------------------------
- */
-
-static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
-{
-	GList *list;
-
-	gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
-
-	/* it seems that gtk_list_store_clear may call some callbacks
-	   that use the column. Set the pointer to NULL to be safe. */
-	gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, NULL, -1);
-	g_list_free(list);
-
-	return FALSE;
-}
-
-static void vficon_clear_store(ViewFile *vf)
-{
-	GtkTreeModel *store;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
-
-	gtk_list_store_clear(GTK_LIST_STORE(store));
-}
-
-static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter)
-{
-	GtkListStore *store;
-	GList *list = NULL;
-	gint i;
-
-	for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL);
-
-	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-	gtk_list_store_append(store, iter);
-	gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
-
-	return list;
-}
-
-static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_position)
-{
-	GtkTreeModel *store;
-	GtkTreePath *tpath;
-	GList *work;
-	IconData *visible_id = NULL;
-	gint r, c;
-	gboolean valid;
-	GtkTreeIter iter;
-
-	vficon_verify_selections(vf);
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-
-	if (keep_position && gtk_widget_get_realized(vf->listview) &&
-	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
-		{
-		GtkTreeIter iter;
-		GList *list;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_path_free(tpath);
-
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-		if (list) visible_id = list->data;
-		}
-
-
-	if (resize)
-		{
-		gint i;
-		gint thumb_width;
-
-		vficon_clear_store(vf);
-
-		thumb_width = vficon_get_icon_width(vf);
-
-		for (i = 0; i < VFICON_MAX_COLUMNS; i++)
-			{
-			GtkTreeViewColumn *column;
-			GtkCellRenderer *cell;
-			GList *list;
-
-			column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i);
-			gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns));
-			gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
-
-			list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
-			cell = (list) ? list->data : NULL;
-			g_list_free(list);
-
-			if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
-				{
-				g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
-							     "fixed_height", options->thumbnails.max_height,
-							     "show_text", VFICON(vf)->show_text,
-							     "show_marks", vf->marks_enabled,
-							     "num_marks", FILEDATA_MARKS_SIZE,
-							     NULL);
-				}
-			}
-		if (gtk_widget_get_realized(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
-		}
-
-	r = -1;
-	c = 0;
-
-	valid = gtk_tree_model_iter_children(store, &iter, NULL);
-
-	work = vf->list;
-	while (work)
-		{
-		GList *list;
-		r++;
-		c = 0;
-		if (valid)
-			{
-			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-			gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
-			}
-		else
-			{
-			list = vficon_add_row(vf, &iter);
-			}
-
-		while (list)
-			{
-			IconData *id;
-
-			if (work)
-				{
-				id = work->data;
-				work = work->next;
-				c++;
-				}
-			else
-				{
-				id = NULL;
-				}
-
-			list->data = id;
-			list = list->next;
-			}
-		if (valid) valid = gtk_tree_model_iter_next(store, &iter);
-		}
-
-	r++;
-	while (valid)
-		{
-		GList *list;
-
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-		valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
-		g_list_free(list);
-		}
-
-	VFICON(vf)->rows = r;
-
-	if (visible_id &&
-	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
-		{
-		GtkTreeIter iter;
-		GList *list;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_path_free(tpath);
-
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-		if (g_list_find(list, visible_id) == NULL &&
-		    vficon_find_iter(vf, visible_id, &iter, NULL))
-			{
-			tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
-			}
-		}
-
-
-	vf_send_update(vf);
-	vf_thumb_update(vf);
-}
-
-static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force)
-{
-	gint new_cols;
-	gint thumb_width;
-
-	thumb_width = vficon_get_icon_width(vf);
-
-	new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
-	if (new_cols < 1) new_cols = 1;
-
-	if (!force && new_cols == VFICON(vf)->columns) return;
-
-	VFICON(vf)->columns = new_cols;
-
-	vficon_populate(vf, TRUE, TRUE);
-
-	DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows);
-}
-
-static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * misc
- *-----------------------------------------------------------------------------
- */
-
-void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend)
-{
-	if (vf->sort_method == type && vf->sort_ascend == ascend) return;
-
-	vf->sort_method = type;
-	vf->sort_ascend = ascend;
-
-	if (!vf->list) return;
-
-	vf_refresh(vf);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * thumb updates
- *-----------------------------------------------------------------------------
- */
-
-void vficon_thumb_progress_count(GList *list, gint *count, gint *done)
-{
-	GList *work = list;
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		work = work->next;
-
-		if (fd->thumb_pixbuf) (*done)++;
-		(*count)++;
-		}
-}
-
-void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
-{
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-	GList *list;
-
-	if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-
-	gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-	gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
-}
-
-
-FileData *vficon_thumb_next_fd(ViewFile *vf)
-{
-	GtkTreePath *tpath;
-	FileData *fd = NULL;
-
-	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
-		{
-		GtkTreeModel *store;
-		GtkTreeIter iter;
-		gboolean valid = TRUE;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_path_free(tpath);
-
-		while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
-			{
-			GList *list;
-
-			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-
-			while (!fd && list)
-				{
-				IconData *id = list->data;
-				if (id && !id->fd->thumb_pixbuf) fd = id->fd;
-				list = list->next;
-				}
-
-			valid = gtk_tree_model_iter_next(store, &iter);
-			}
-		}
-
-	/* then find first undone */
-
-	if (!fd)
-		{
-		GList *work = vf->list;
-		while (work && !fd)
-			{
-			IconData *id = work->data;
-			FileData *fd_p = id->fd;
-			work = work->next;
-
-			if (!fd_p->thumb_pixbuf) fd = fd_p;
-			}
-		}
-
-	return fd;
-}
-
-void vficon_thumb_reset_all(ViewFile *vf)
-{
-	GList *work = vf->list;
-
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		if (fd->thumb_pixbuf)
-			{
-			g_object_unref(fd->thumb_pixbuf);
-			fd->thumb_pixbuf = NULL;
-			}
-		work = work->next;
-		}
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * row stuff
- *-----------------------------------------------------------------------------
- */
-
-FileData *vficon_index_get_data(ViewFile *vf, gint row)
-{
-	IconData *id;
-
-	id = g_list_nth_data(vf->list, row);
-	return id ? id->fd : NULL;
-}
-
-
-gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
-{
-	gint p = 0;
-	GList *work;
-
-	if (!in_fd) return -1;
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		if (fd == in_fd) return p;
-		work = work->next;
-		p++;
-		}
-
-	return -1;
-}
-
-static gint vficon_index_by_id(ViewFile *vf, IconData *in_id)
-{
-	gint p = 0;
-	GList *work;
-
-	if (!in_id) return -1;
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		if (id == in_id) return p;
-		work = work->next;
-		p++;
-		}
-
-	return -1;
-}
-
-guint vficon_count(ViewFile *vf, gint64 *bytes)
-{
-	if (bytes)
-		{
-		gint64 b = 0;
-		GList *work;
-
-		work = vf->list;
-		while (work)
-			{
-			IconData *id = work->data;
-			FileData *fd = id->fd;
-			work = work->next;
-
-			b += fd->size;
-			}
-
-		*bytes = b;
-		}
-
-	return g_list_length(vf->list);
-}
-
-GList *vficon_get_list(ViewFile *vf)
-{
-	GList *list = NULL;
-	GList *work;
-
-	work = vf->list;
-	while (work)
-		{
-		IconData *id = work->data;
-		FileData *fd = id->fd;
-		work = work->next;
-
-		list = g_list_prepend(list, file_data_ref(fd));
-		}
-
-	return g_list_reverse(list);
-}
-
-/*
- *-----------------------------------------------------------------------------
- *
- *-----------------------------------------------------------------------------
- */
-
-static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
-{
-	gboolean ret = TRUE;
-	GList *work, *work_fd;
-	IconData *focus_id;
-	GList *new_filelist = NULL;
-	FileData *first_selected = NULL;
-	GList *new_iconlist = NULL;
-
-	focus_id = VFICON(vf)->focus_id;
-
-	if (vf->dir_fd)
-		{
-		ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
-		new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
-		}
-
-	vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
-	new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
-
-	if (VFICON(vf)->selection)
-		{
-		first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd;
-		file_data_ref(first_selected);
-		g_list_free(VFICON(vf)->selection);
-		VFICON(vf)->selection = NULL;
-
-
-		}
-
-	/* check for same files from old_list */
-	work = vf->list;
-	work_fd = new_filelist;
-	while (work || work_fd)
-		{
-		IconData *id = NULL;
-		FileData *fd = NULL;
-		FileData *new_fd = NULL;
-		gint match;
-
-		if (work && work_fd)
-			{
-			id = work->data;
-			fd = id->fd;
-
-			new_fd = work_fd->data;
-
-			if (fd == new_fd)
-				{
-				/* not changed, go to next */
-				work = work->next;
-				work_fd = work_fd->next;
-				if (id->selected & SELECTION_SELECTED)
-					{
-					VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id);
-					}
-				continue;
-				}
-
-			match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
-			if (match == 0) g_warning("multiple fd for the same path");
-			}
-		else if (work)
-			{
-			id = work->data;
-			fd = id->fd;
-			match = -1;
-			}
-		else /* work_fd */
-			{
-			new_fd = work_fd->data;
-			match = 1;
-			}
-
-		if (match < 0)
-			{
-			/* file no longer exists, delete from vf->list */
-			GList *to_delete = work;
-			work = work->next;
-			if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
-			if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL;
-			file_data_unref(fd);
-			g_free(id);
-			vf->list = g_list_delete_link(vf->list, to_delete);
-			}
-		else
-			{
-			/* new file, add to vf->list */
-			id = g_new0(IconData, 1);
-
-			id->selected = SELECTION_NONE;
-			id->fd = file_data_ref(new_fd);
-			if (work)
-				vf->list = g_list_insert_before(vf->list, work, id);
-			else
-				new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */
-
-			work_fd = work_fd->next;
-			}
-
-		}
-
-	if (new_iconlist)
-		{
-		vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist));
-		}
-
-	VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
-
-	filelist_free(new_filelist);
-
-	vficon_populate(vf, TRUE, keep_position);
-
-	if (first_selected && !VFICON(vf)->selection)
-		{
-		/* all selected files disappeared */
-		vficon_select_closest(vf, first_selected);
-		}
-	file_data_unref(first_selected);
-
-	/* attempt to keep focus on same icon when refreshing */
-	if (focus_id && g_list_find(vf->list, focus_id))
-		{
-		vficon_set_focus(vf, focus_id);
-		}
-
-	return ret;
-}
-
-gboolean vficon_refresh(ViewFile *vf)
-{
-	return vficon_refresh_real(vf, TRUE);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * draw, etc.
- *-----------------------------------------------------------------------------
- */
-
-typedef struct _ColumnData ColumnData;
-struct _ColumnData
-{
-	ViewFile *vf;
-	gint number;
-};
-
-static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
-				GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
-{
-	GList *list;
-	IconData *id;
-	ColumnData *cd = data;
-	ViewFile *vf = cd->vf;
-
-	if (!GQV_IS_CELL_RENDERER_ICON(cell)) return;
-
-	gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
-
-	id = g_list_nth_data(list, cd->number);
-
-	if (id)
-		{
-		GdkColor color_fg;
-		GdkColor color_bg;
-		GtkStyle *style;
-		gchar *name_sidecars;
-		gchar *link;
-		GtkStateType state = GTK_STATE_NORMAL;
-
-		g_assert(id->fd->magick == FD_MAGICK);
-
-		link = islink(id->fd->path) ? GQ_LINK_STR : "";
-		if (id->fd->sidecar_files)
-			{
-			gchar *sidecars = file_data_sc_list_to_string(id->fd);
-			name_sidecars = g_strdup_printf("%s%s %s", link, id->fd->name, sidecars);
-			g_free(sidecars);
-			}
-		else
-			{
-			gchar *disabled_grouping = id->fd->disable_grouping ? _(" [NO GROUPING]") : "";
-			name_sidecars = g_strdup_printf("%s%s%s", link, id->fd->name, disabled_grouping);
-			}
-
-		style = gtk_widget_get_style(vf->listview);
-		if (id->selected & SELECTION_SELECTED)
-			{
-			state = GTK_STATE_SELECTED;
-			}
-
-		memcpy(&color_fg, &style->text[state], sizeof(color_fg));
-		memcpy(&color_bg, &style->base[state], sizeof(color_bg));
-
-		if (id->selected & SELECTION_PRELIGHT)
-			{
-			shift_color(&color_bg, -1, 0);
-			}
-
-		g_object_set(cell,	"pixbuf", id->fd->thumb_pixbuf,
-					"text", name_sidecars,
-					"marks", file_data_get_marks(id->fd),
-					"show_marks", vf->marks_enabled,
-					"cell-background-gdk", &color_bg,
-					"cell-background-set", TRUE,
-					"foreground-gdk", &color_fg,
-					"foreground-set", TRUE,
-					"has-focus", (VFICON(vf)->focus_id == id), NULL);
-		g_free(name_sidecars);
-		}
-	else
-		{
-		g_object_set(cell,	"pixbuf", NULL,
-					"text", NULL,
-					"show_marks", FALSE,
-					"cell-background-set", FALSE,
-					"foreground-set", FALSE,
-					"has-focus", FALSE, NULL);
-		}
-}
-
-static void vficon_append_column(ViewFile *vf, gint n)
-{
-	ColumnData *cd;
-	GtkTreeViewColumn *column;
-	GtkCellRenderer *renderer;
-
-	column = gtk_tree_view_column_new();
-	gtk_tree_view_column_set_min_width(column, 0);
-
-	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
-	gtk_tree_view_column_set_alignment(column, 0.5);
-
-	renderer = gqv_cell_renderer_icon_new();
-	gtk_tree_view_column_pack_start(column, renderer, FALSE);
-	g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
-					 "ypad", THUMB_BORDER_PADDING,
-					 "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
-
-	g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
-	g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
-
-	cd = g_new0(ColumnData, 1);
-	cd->vf = vf;
-	cd->number = n;
-	gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
-
-	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
-
-	g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * base
- *-----------------------------------------------------------------------------
- */
-
-gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd)
-{
-	gboolean ret;
-
-	if (!dir_fd) return FALSE;
-	if (vf->dir_fd == dir_fd) return TRUE;
-
-	file_data_unref(vf->dir_fd);
-	vf->dir_fd = file_data_ref(dir_fd);
-
-	g_list_free(VFICON(vf)->selection);
-	VFICON(vf)->selection = NULL;
-
-	iconlist_free(vf->list);
-	vf->list = NULL;
-
-	/* NOTE: populate will clear the store for us */
-	ret = vficon_refresh_real(vf, FALSE);
-
-	VFICON(vf)->focus_id = NULL;
-	vficon_move_focus(vf, 0, 0, FALSE);
-
-	return ret;
-}
-
-void vficon_destroy_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vf_refresh_idle_cancel(vf);
-
-	file_data_unregister_notify_func(vf_notify_cb, vf);
-
-	tip_unschedule(vf);
-
-	vf_thumb_cleanup(vf);
-
-	iconlist_free(vf->list);
-	g_list_free(VFICON(vf)->selection);
-}
-
-ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
-{
-	GtkListStore *store;
-	GtkTreeSelection *selection;
-	gint i;
-
-	vf->info = g_new0(ViewFileInfoIcon, 1);
-
-	VFICON(vf)->show_text = options->show_icon_names;
-
-	store = gtk_list_store_new(1, G_TYPE_POINTER);
-	vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
-	g_object_unref(store);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
-
-	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
-	gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
-
-	for (i = 0; i < VFICON_MAX_COLUMNS; i++)
-		{
-		vficon_append_column(vf, i);
-		}
-
-	/* zero width column to hide tree view focus, we draw it ourselves */
-	vficon_append_column(vf, i);
-	/* end column to fill white space */
-	vficon_append_column(vf, i);
-
-	g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
-			 G_CALLBACK(vficon_sized_cb), vf);
-
-	gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
-			      GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
-
-	g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
-			 G_CALLBACK(vficon_motion_cb), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
-			 G_CALLBACK(vficon_leave_cb), vf);
-
-	/* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
-	vficon_populate_at_new_size(vf, 1, 1, FALSE);
-
-	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
-
-	return vf;
-}
-
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_icon.h	Fri Jul 07 12:11:56 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2004 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 VIEW_FILE_ICON_H
-#define VIEW_FILE_ICON_H
-
-gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
-gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
-gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
-
-void vficon_dnd_init(ViewFile *vf);
-
-void vficon_destroy_cb(GtkWidget *widget, gpointer data);
-ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd);
-
-gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd);
-gboolean vficon_refresh(ViewFile *vf);
-
-void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend);
-
-void vficon_marks_set(ViewFile *vf, gboolean enable);
-
-GList *vficon_selection_get_one(ViewFile *vf, FileData *fd);
-GList *vficon_pop_menu_file_list(ViewFile *vf);
-void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data);
-void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data);
-void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data);
-void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data);
-void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data);
-
-FileData *vficon_index_get_data(ViewFile *vf, gint row);
-gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd);
-guint vficon_count(ViewFile *vf, gint64 *bytes);
-GList *vficon_get_list(ViewFile *vf);
-
-gboolean vficon_index_is_selected(ViewFile *vf, gint row);
-guint vficon_selection_count(ViewFile *vf, gint64 *bytes);
-GList *vficon_selection_get_list(ViewFile *vf);
-GList *vficon_selection_get_list_by_index(ViewFile *vf);
-
-void vficon_select_all(ViewFile *vf);
-void vficon_select_none(ViewFile *vf);
-void vficon_select_invert(ViewFile *vf);
-void vficon_select_by_fd(ViewFile *vf, FileData *fd);
-
-void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode);
-void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode);
-
-
-void vficon_thumb_progress_count(GList *list, gint *count, gint *done);
-void vficon_set_thumb_fd(ViewFile *vf, FileData *fd);
-FileData *vficon_thumb_next_fd(ViewFile *vf);
-void vficon_thumb_reset_all(ViewFile *vf);
-
-#endif
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_list.c	Fri Jul 07 12:11:56 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2052 +0,0 @@
-/*
- * Copyright (C) 2004 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 "view_file_list.h"
-
-#include "bar.h"
-#include "cache_maint.h"
-#include "dnd.h"
-#include "editors.h"
-#include "img-view.h"
-#include "layout.h"
-#include "layout_image.h"
-#include "menu.h"
-#include "metadata.h"
-#include "thumb.h"
-#include "utilops.h"
-#include "ui_fileops.h"
-#include "ui_menu.h"
-#include "ui_tree_edit.h"
-#include "uri_utils.h"
-#include "view_file.h"
-
-#include <gdk/gdkkeysyms.h> /* for keyboard values */
-
-/* Index to tree store */
-enum {
-	FILE_COLUMN_POINTER = 0,
-	FILE_COLUMN_VERSION,
-	FILE_COLUMN_THUMB,
-	FILE_COLUMN_FORMATTED,
-	FILE_COLUMN_NAME,
-	FILE_COLUMN_SIDECARS,
-	FILE_COLUMN_SIZE,
-	FILE_COLUMN_DATE,
-	FILE_COLUMN_EXPANDED,
-	FILE_COLUMN_COLOR,
-	FILE_COLUMN_MARKS,
-	FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
-	FILE_COLUMN_COUNT
-};
-
-
-/* Index to tree view */
-enum {
-	FILE_VIEW_COLUMN_MARKS = 0,
-	FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
-	FILE_VIEW_COLUMN_THUMB,
-	FILE_VIEW_COLUMN_FORMATTED,
-	FILE_VIEW_COLUMN_SIZE,
-	FILE_VIEW_COLUMN_DATE,
-	FILE_VIEW_COLUMN_COUNT
-};
-
-
-
-static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
-static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
-static void vflist_populate_view(ViewFile *vf, gboolean force);
-static gboolean vflist_is_multiline(ViewFile *vf);
-static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
-
-
-/*
- *-----------------------------------------------------------------------------
- * misc
- *-----------------------------------------------------------------------------
- */
-typedef struct {
-	FileData *fd;
-	GtkTreeIter *iter;
-	gboolean found;
-	gint row;
-} ViewFileFindRowData;
-
-static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
-{
-	ViewFileFindRowData *find = data;
-	FileData *fd;
-	gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
-	if (fd == find->fd)
-		{
-		*find->iter = *iter;
-		find->found = TRUE;
-		return TRUE;
-		}
-	find->row++;
-	return FALSE;
-}
-
-static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
-{
-	GtkTreeModel *store;
-	ViewFileFindRowData data = {fd, iter, FALSE, 0};
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
-
-	if (data.found)
-		{
-		return data.row;
-		}
-
-	return -1;
-}
-
-static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
-{
-	GtkTreePath *tpath;
-	GtkTreeViewColumn *column;
-
-	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
-					  &tpath, &column, NULL, NULL))
-		{
-		GtkTreeModel *store;
-		GtkTreeIter row;
-		FileData *fd;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		gtk_tree_model_get_iter(store, &row, tpath);
-		gtk_tree_path_free(tpath);
-		gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
-
-		return fd;
-		}
-
-	return NULL;
-}
-
-static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
-{
-	FileData *fd;
-	gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
-
-	/* it seems that gtk_tree_store_clear may call some callbacks
-	   that use the column. Set the pointer to NULL to be safe. */
-	gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
-	file_data_unref(fd);
-	return FALSE;
-}
-
-static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
-{
-	GtkTreeModel *store;
-	GList *files = NULL;
-
-	if (unlock_files && vf->marks_enabled)
-		{
-		// unlock locked files in this directory
-		filelist_read(vf->dir_fd, &files, NULL);
-		while (files)
-			{
-			FileData *fd = files->data;
-			files = files->next;
-			file_data_unlock(fd);
-			file_data_unref(fd);  // undo the ref that got added in filelist_read
-			}
-		}
-
-	g_list_free(files);
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
-	gtk_tree_store_clear(GTK_TREE_STORE(store));
-}
-
-void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
-{
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-
-	if (vflist_find_row(vf, fd, &iter) < 0) return;
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
-}
-
-static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
-{
-	GtkTreeModel *store;
-	GtkTreePath *tpath;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-
-	tpath = gtk_tree_model_get_path(store, iter);
-	gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
-	gtk_tree_path_free(tpath);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * dnd
- *-----------------------------------------------------------------------------
- */
-
-static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
-			   GtkSelectionData *selection_data, guint info,
-			   guint time, gpointer data)
-{
-	ViewFile *vf = data;
-	GList *list = NULL;
-
-	if (!VFLIST(vf)->click_fd) return;
-
-	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
-		{
-		list = vf_selection_get_list(vf);
-		}
-	else
-		{
-		list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
-		}
-
-	if (!list) return;
-	uri_selection_data_set_uris_from_filelist(selection_data, list);
-	filelist_free(list);
-}
-
-static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
-
-	if (VFLIST(vf)->thumbs_enabled &&
-	    VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
-		{
-		guint items;
-
-		if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
-			items = vf_selection_count(vf, NULL);
-		else
-			items = 1;
-
-		dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
-		}
-}
-
-static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
-
-	if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
-		{
-		vf_refresh(vf);
-		}
-}
-
-static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
-				      int x, int y, GtkSelectionData *selection,
-				      guint info, guint time, gpointer data)
-{
-	ViewFile *vf = data;
-
-	if (info == TARGET_TEXT_PLAIN) {
-		FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
-
-		if (fd) {
-			/* Add keywords to file */
-			gchar *str = (gchar *) gtk_selection_data_get_text(selection);
-			GList *kw_list = string_to_keywords_list(str);
-
-			metadata_append_list(fd, KEYWORD_KEY, kw_list);
-			string_list_free(kw_list);
-			g_free(str);
-		}
-	}
-}
-
-void vflist_dnd_init(ViewFile *vf)
-{
-	gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
-			    dnd_file_drag_types, dnd_file_drag_types_count,
-			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-	gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
-			    dnd_file_drag_types, dnd_file_drag_types_count,
-			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-
-	g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
-			 G_CALLBACK(vflist_dnd_get), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
-			 G_CALLBACK(vflist_dnd_begin), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_end",
-			 G_CALLBACK(vflist_dnd_end), vf);
-	g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
-			 G_CALLBACK(vflist_drag_data_received), vf);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * pop-up menu
- *-----------------------------------------------------------------------------
- */
-
-GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
-{
-	GList *list = g_list_append(NULL, file_data_ref(fd));
-
-	if (fd->sidecar_files)
-		{
-		/* check if the row is expanded */
-		GtkTreeModel *store;
-		GtkTreeIter iter;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		if (vflist_find_row(vf, fd, &iter) >= 0)
-			{
-			GtkTreePath *tpath;
-
-			tpath = gtk_tree_model_get_path(store, &iter);
-			if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
-				{
-				/* unexpanded - add whole group */
-				GList *work = fd->sidecar_files;
-				while (work)
-					{
-					FileData *sfd = work->data;
-					list = g_list_prepend(list, file_data_ref(sfd));
-					work = work->next;
-					}
-				}
-			gtk_tree_path_free(tpath);
-			}
-		list = g_list_reverse(list);
-		}
-
-	return list;
-}
-
-GList *vflist_pop_menu_file_list(ViewFile *vf)
-{
-	if (!VFLIST(vf)->click_fd) return NULL;
-
-	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
-		{
-		return vf_selection_get_list(vf);
-		}
-	return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
-}
-
-
-void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
-		{
-		GList *list;
-
-		list = vf_selection_get_list(vf);
-		view_window_new_from_list(list);
-		filelist_free(list);
-		}
-	else
-		{
-		view_window_new(VFLIST(vf)->click_fd);
-		}
-}
-
-void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-	GList *list;
-
-	list = vf_pop_menu_file_list(vf);
-	if (options->file_ops.enable_in_place_rename &&
-	    list && !list->next && VFLIST(vf)->click_fd)
-		{
-		GtkTreeModel *store;
-		GtkTreeIter iter;
-
-		filelist_free(list);
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
-			{
-			GtkTreePath *tpath;
-
-			tpath = gtk_tree_model_get_path(store, &iter);
-			tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
-					  FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
-					  vflist_row_rename_cb, vf);
-			gtk_tree_path_free(tpath);
-			}
-		return;
-		}
-
-	file_util_rename(NULL, list, vf->listview);
-}
-
-void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
-	if (vf->layout)
-		{
-		layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
-		}
-	else
-		{
-		vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
-		}
-}
-
-void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
-	vf_refresh(vf);
-	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
-}
-
-void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-	vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
-	VFLIST(vf)->click_fd = NULL;
-	vf->popup = NULL;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * callbacks
- *-----------------------------------------------------------------------------
- */
-
-static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
-{
-	ViewFile *vf = data;
-	gchar *new_path;
-
-	if (!new || !new[0]) return FALSE;
-
-	new_path = g_build_filename(vf->dir_fd->path, new, NULL);
-
-	if (strchr(new, G_DIR_SEPARATOR) != NULL)
-		{
-		gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
-		file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
-		g_free(text);
-		}
-	else
-		{
-		gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
-		FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
-		file_util_rename_simple(fd, new_path, vf->listview);
-		file_data_unref(fd);
-		g_free(old_path);
-		}
-
-	g_free(new_path);
-
-	return FALSE;
-}
-
-static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-	GtkTreePath *tpath;
-	gint cw, ch;
-
-	if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	tpath = gtk_tree_model_get_path(store, &iter);
-	tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
-	gtk_tree_path_free(tpath);
-	*y += ch;
-	popup_menu_position_clamp(menu, x, y, 0);
-}
-
-gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreePath *tpath;
-
-	if (event->keyval != GDK_KEY_Menu) return FALSE;
-
-	gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
-	if (tpath)
-		{
-		GtkTreeModel *store;
-		GtkTreeIter iter;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
-		gtk_tree_path_free(tpath);
-		}
-	else
-		{
-		VFLIST(vf)->click_fd = NULL;
-		}
-
-	vf->popup = vf_pop_menu(vf);
-	gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
-
-	return TRUE;
-}
-
-gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreePath *tpath;
-	GtkTreeIter iter;
-	FileData *fd = NULL;
-	GtkTreeViewColumn *column;
-
-	vf->clicked_mark = 0;
-
-	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
-					  &tpath, &column, NULL, NULL))
-		{
-		GtkTreeModel *store;
-		gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
-
-		if (bevent->button == MOUSE_BUTTON_LEFT &&
-		    col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
-			return FALSE;
-
-		if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
-			vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-		gtk_tree_path_free(tpath);
-		}
-
-	VFLIST(vf)->click_fd = fd;
-
-	if (bevent->button == MOUSE_BUTTON_RIGHT)
-		{
-		vf->popup = vf_pop_menu(vf);
-		gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
-				bevent->button, bevent->time);
-		return TRUE;
-		}
-
-	if (!fd) return FALSE;
-
-	if (bevent->button == MOUSE_BUTTON_MIDDLE)
-		{
-		if (!vflist_row_is_selected(vf, fd))
-			{
-			vflist_color_set(vf, fd, TRUE);
-			}
-		return TRUE;
-		}
-
-
-	if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
-	    !(bevent->state & GDK_SHIFT_MASK ) &&
-	    !(bevent->state & GDK_CONTROL_MASK ) &&
-	    vflist_row_is_selected(vf, fd))
-		{
-		GtkTreeSelection *selection;
-
-		gtk_widget_grab_focus(widget);
-
-
-		/* returning FALSE and further processing of the event is needed for
-		   correct operation of the expander, to show the sidecar files.
-		   It however resets the selection of multiple files. With this condition
-		   it should work for both cases */
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
-		return (gtk_tree_selection_count_selected_rows(selection) > 1);
-		}
-
-	if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
-		{
-		if (vf->layout) layout_image_full_screen_start(vf->layout);
-		}
-
-	return FALSE;
-}
-
-gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreePath *tpath;
-	GtkTreeIter iter;
-	FileData *fd = NULL;
-
-	if (bevent->button == MOUSE_BUTTON_MIDDLE)
-		{
-		vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
-		}
-
-	if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
-		{
-		return TRUE;
-		}
-
-	if ((bevent->x != 0 || bevent->y != 0) &&
-	    gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
-					  &tpath, NULL, NULL, NULL))
-		{
-		GtkTreeModel *store;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-		gtk_tree_path_free(tpath);
-		}
-
-	if (bevent->button == MOUSE_BUTTON_MIDDLE)
-		{
-		if (fd && VFLIST(vf)->click_fd == fd)
-			{
-			GtkTreeSelection *selection;
-
-			selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
-			if (vflist_row_is_selected(vf, fd))
-				{
-				gtk_tree_selection_unselect_iter(selection, &iter);
-				}
-			else
-				{
-				gtk_tree_selection_select_iter(selection, &iter);
-				}
-			}
-		return TRUE;
-		}
-
-	if (fd && VFLIST(vf)->click_fd == fd &&
-	    !(bevent->state & GDK_SHIFT_MASK ) &&
-	    !(bevent->state & GDK_CONTROL_MASK ) &&
-	    vflist_row_is_selected(vf, fd))
-		{
-		GtkTreeSelection *selection;
-
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
-		gtk_tree_selection_unselect_all(selection);
-		gtk_tree_selection_select_iter(selection, &iter);
-		vflist_move_cursor(vf, &iter);
-		}
-
-	return FALSE;
-}
-
-static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
-{
-	FileData *read_ahead_fd = NULL;
-	gint row;
-	FileData *cur_fd;
-
-	if (!sel_fd) return;
-
-	cur_fd = layout_image_get_fd(vf->layout);
-	if (sel_fd == cur_fd) return; /* no change */
-
-	row = g_list_index(vf->list, sel_fd);
-	// FIXME sidecar data
-
-	if (sel_fd && options->image.enable_read_ahead && row >= 0)
-		{
-		if (row > g_list_index(vf->list, cur_fd) &&
-		    (guint) (row + 1) < vf_count(vf, NULL))
-			{
-			read_ahead_fd = vf_index_get_data(vf, row + 1);
-			}
-		else if (row > 0)
-			{
-			read_ahead_fd = vf_index_get_data(vf, row - 1);
-			}
-		}
-
-	layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
-}
-
-static gboolean vflist_select_idle_cb(gpointer data)
-{
-	ViewFile *vf = data;
-
-	if (!vf->layout)
-		{
-		VFLIST(vf)->select_idle_id = 0;
-		return FALSE;
-		}
-
-	vf_send_update(vf);
-
-	if (VFLIST(vf)->select_fd)
-		{
-		vflist_select_image(vf, VFLIST(vf)->select_fd);
-		VFLIST(vf)->select_fd = NULL;
-		}
-
-	VFLIST(vf)->select_idle_id = 0;
-	return FALSE;
-}
-
-static void vflist_select_idle_cancel(ViewFile *vf)
-{
-	if (VFLIST(vf)->select_idle_id)
-		{
-		g_source_remove(VFLIST(vf)->select_idle_id);
-		VFLIST(vf)->select_idle_id = 0;
-		}
-}
-
-static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
-				 gboolean path_currently_selected, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeIter iter;
-
-	if (!path_currently_selected &&
-	    gtk_tree_model_get_iter(store, &iter, tpath))
-		{
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
-		}
-	else
-		{
-		VFLIST(vf)->select_fd = NULL;
-		}
-
-	if (vf->layout &&
-	    !VFLIST(vf)->select_idle_id)
-		{
-		VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
-		}
-
-	return TRUE;
-}
-
-static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
-{
-	ViewFile *vf = data;
-	vflist_set_expanded(vf, iter, TRUE);
-}
-
-static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
-{
-	ViewFile *vf = data;
-	vflist_set_expanded(vf, iter, FALSE);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * misc
- *-----------------------------------------------------------------------------
- */
-
-
-static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
- {
-	gboolean multiline = vflist_is_multiline(vf);
-	gchar *text;
-
-	if (multiline)
-		{
-		text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
-		}
-	else
-		{
-		text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
-		}
-	return text;
-}
-
-static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
-{
-	GtkTreeStore *store;
-	gchar *name;
-	gchar *sidecars;
-	gchar *size;
-	gchar *time;
-	gchar *formatted;
-
-	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-
-	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
-					FILE_COLUMN_NAME, &name,
-					FILE_COLUMN_SIDECARS, &sidecars,
-					FILE_COLUMN_SIZE, &size,
-					FILE_COLUMN_DATE, &time,
-					-1);
-	formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
-
-	gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
-					FILE_COLUMN_EXPANDED, expanded,
-					-1);
-	g_free(time);
-	g_free(size);
-	g_free(sidecars);
-	g_free(name);
-	g_free(formatted);
-}
-
-static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
-{
-	gchar *size;
-	gchar *sidecars = NULL;
-	gchar *name;
-	const gchar *time = text_from_time(fd->date);
-	gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
-	const gchar *disabled_grouping;
-	gchar *formatted;
-	gboolean expanded = FALSE;
-
-	if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
-		{
-		gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
-		}
-
-	sidecars = file_data_sc_list_to_string(fd);
-
-	disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
-	name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
-	size = text_from_size(fd->size);
-
-	formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
-
-	gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
-					FILE_COLUMN_VERSION, fd->version,
-					FILE_COLUMN_THUMB, fd->thumb_pixbuf,
-					FILE_COLUMN_FORMATTED, formatted,
-					FILE_COLUMN_SIDECARS, sidecars,
-					FILE_COLUMN_NAME, name,
-					FILE_COLUMN_SIZE, size,
-					FILE_COLUMN_DATE, time,
-#define STORE_SET_IS_SLOW 1
-#if STORE_SET_IS_SLOW
-/* this is 3x faster on a directory with 20000 files */
-					FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
-					FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
-					FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
-					FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
-					FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
-					FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
-#if FILEDATA_MARKS_SIZE != 6
-#error this needs to be updated
-#endif
-#endif
-					FILE_COLUMN_COLOR, FALSE, -1);
-
-#if !STORE_SET_IS_SLOW
-	{
-	gint i;
-	for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
-		gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
-	}
-#endif
-	g_free(size);
-	g_free(sidecars);
-	g_free(name);
-	g_free(formatted);
-}
-
-static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
-{
-	GList *work;
-	GtkTreeIter iter;
-	gboolean valid;
-	gint num_ordered = 0;
-	gint num_prepended = 0;
-
-	valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
-
-	work = list;
-	while (work)
-		{
-		gint match;
-		FileData *fd = work->data;
-		gboolean done = FALSE;
-
-		while (!done)
-			{
-			FileData *old_fd = NULL;
-			gint old_version = 0;
-
-			if (valid)
-				{
-				gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
-						   FILE_COLUMN_POINTER, &old_fd,
-						   FILE_COLUMN_VERSION, &old_version,
-						   -1);
-
-				if (fd == old_fd)
-					{
-					match = 0;
-					}
-				else
-					{
-					if (parent_iter)
-						match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
-					else
-						match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
-
-					if (match == 0) g_warning("multiple fd for the same path");
-					}
-
-				}
-			else
-				{
-				match = -1;
-				}
-
-			if (match < 0)
-				{
-				GtkTreeIter new;
-
-				if (valid)
-					{
-					num_ordered++;
-					gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
-					}
-				else
-					{
-					/*
-					    here should be used gtk_tree_store_append, but this function seems to be O(n)
-					    and it seems to be much faster to add new entries to the beginning and reorder later
-					*/
-					num_prepended++;
-					gtk_tree_store_prepend(store, &new, parent_iter);
-					}
-
-				vflist_setup_iter(vf, store, &new, file_data_ref(fd));
-				vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
-
-				if (g_list_find(selected, fd))
-					{
-					/* renamed files - the same fd appears at different position - select it again*/
-					GtkTreeSelection *selection;
-					selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-					gtk_tree_selection_select_iter(selection, &new);
-					}
-
-				done = TRUE;
-				}
-			else if (match > 0)
-				{
-				file_data_unref(old_fd);
-				valid = gtk_tree_store_remove(store, &iter);
-				}
-			else
-				{
-				num_ordered++;
-				if (fd->version != old_version || force)
-					{
-					vflist_setup_iter(vf, store, &iter, fd);
-					vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
-					}
-
-				if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
-
-				done = TRUE;
-				}
-			}
-		work = work->next;
-		}
-
-	while (valid)
-		{
-		FileData *old_fd;
-		gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
-		file_data_unref(old_fd);
-
-		valid = gtk_tree_store_remove(store, &iter);
-		}
-
-	/* move the prepended entries to the correct position */
-	if (num_prepended)
-		{
-		gint i;
-		gint num_total = num_prepended + num_ordered;
-		gint *new_order = g_malloc(num_total * sizeof(gint));
-
-		for (i = 0; i < num_total; i++)
-			{
-			if (i < num_ordered)
-				new_order[i] = num_prepended + i;
-			else
-				new_order[i] = num_total - 1 - i;
-			}
-		gtk_tree_store_reorder(store, parent_iter, new_order);
-
-		g_free(new_order);
-		}
-}
-
-void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
-{
-	gint i;
-	GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
-	gint *new_order;
-	GtkTreeStore *store;
-	GList *work;
-
-	if (vf->sort_method == type && vf->sort_ascend == ascend) return;
-	if (!vf->list) return;
-
-	work = vf->list;
-	i = 0;
-	while (work)
-		{
-		FileData *fd = work->data;
-		g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
-		i++;
-		work = work->next;
-		}
-
-	vf->sort_method = type;
-	vf->sort_ascend = ascend;
-
-	vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
-
-	new_order = g_malloc(i * sizeof(gint));
-
-	work = vf->list;
-	i = 0;
-	while (work)
-		{
-		FileData *fd = work->data;
-		new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
-		i++;
-		work = work->next;
-		}
-
-	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-	gtk_tree_store_reorder(store, NULL, new_order);
-
-	g_free(new_order);
-	g_hash_table_destroy(fd_idx_hash);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * thumb updates
- *-----------------------------------------------------------------------------
- */
-
-
-void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
-{
-	GList *work = list;
-	while (work)
-		{
-		FileData *fd = work->data;
-		work = work->next;
-
-		if (fd->thumb_pixbuf) (*done)++;
-
-		if (fd->sidecar_files)
-			{
-			vflist_thumb_progress_count(fd->sidecar_files, count, done);
-			}
-		(*count)++;
-		}
-}
-
-void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
-{
-	GtkTreeStore *store;
-	GtkTreeIter iter;
-
-	if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
-
-	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-	gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
-}
-
-FileData *vflist_thumb_next_fd(ViewFile *vf)
-{
-	GtkTreePath *tpath;
-	FileData *fd = NULL;
-
-	/* first check the visible files */
-
-	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
-		{
-		GtkTreeModel *store;
-		GtkTreeIter iter;
-		gboolean valid = TRUE;
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_path_free(tpath);
-
-		while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
-			{
-			FileData *nfd;
-
-			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
-
-			if (!nfd->thumb_pixbuf) fd = nfd;
-
-			valid = gtk_tree_model_iter_next(store, &iter);
-			}
-		}
-
-	/* then find first undone */
-
-	if (!fd)
-		{
-		GList *work = vf->list;
-		while (work && !fd)
-			{
-			FileData *fd_p = work->data;
-			if (!fd_p->thumb_pixbuf)
-				fd = fd_p;
-			else
-				{
-				GList *work2 = fd_p->sidecar_files;
-
-				while (work2 && !fd)
-					{
-					fd_p = work2->data;
-					if (!fd_p->thumb_pixbuf) fd = fd_p;
-					work2 = work2->next;
-					}
-				}
-			work = work->next;
-			}
-		}
-
-	return fd;
-}
-
-
-void vflist_thumb_reset_all(ViewFile *vf)
-{
-	GList *work = vf->list;
-	while (work)
-		{
-		FileData *fd = work->data;
-		if (fd->thumb_pixbuf)
-			{
-			g_object_unref(fd->thumb_pixbuf);
-			fd->thumb_pixbuf = NULL;
-			}
-		work = work->next;
-		}
-}
-
-/*
- *-----------------------------------------------------------------------------
- * row stuff
- *-----------------------------------------------------------------------------
- */
-
-FileData *vflist_index_get_data(ViewFile *vf, gint row)
-{
-	return g_list_nth_data(vf->list, row);
-}
-
-gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
-{
-	gint p = 0;
-	GList *work, *work2;
-
-	work = vf->list;
-	while (work)
-		{
-		FileData *list_fd = work->data;
-		if (list_fd == fd) return p;
-
-		work2 = list_fd->sidecar_files;
-		while (work2)
-			{
-			/* FIXME: return the same index also for sidecars
-			   it is sufficient for next/prev navigation but it should be rewritten
-			   without using indexes at all
-			*/
-			FileData *sidecar_fd = work2->data;
-			if (sidecar_fd == fd) return p;
-			work2 = work2->next;
-			}
-
-		work = work->next;
-		p++;
-		}
-
-	return -1;
-}
-
-guint vflist_count(ViewFile *vf, gint64 *bytes)
-{
-	if (bytes)
-		{
-		gint64 b = 0;
-		GList *work;
-
-		work = vf->list;
-		while (work)
-			{
-			FileData *fd = work->data;
-			work = work->next;
-			b += fd->size;
-			}
-
-		*bytes = b;
-		}
-
-	return g_list_length(vf->list);
-}
-
-GList *vflist_get_list(ViewFile *vf)
-{
-	GList *list = NULL;
-	GList *work;
-
-	work = vf->list;
-	while (work)
-		{
-		FileData *fd = work->data;
-		work = work->next;
-
-		list = g_list_prepend(list, file_data_ref(fd));
-		}
-
-	return g_list_reverse(list);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * selections
- *-----------------------------------------------------------------------------
- */
-
-static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
-{
-	GtkTreeModel *store;
-	GtkTreeSelection *selection;
-	GList *slist;
-	GList *work;
-	gboolean found = FALSE;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	slist = gtk_tree_selection_get_selected_rows(selection, &store);
-	work = slist;
-	while (!found && work)
-		{
-		GtkTreePath *tpath = work->data;
-		FileData *fd_n;
-		GtkTreeIter iter;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
-		if (fd_n == fd) found = TRUE;
-		work = work->next;
-		}
-	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
-	g_list_free(slist);
-
-	return found;
-}
-
-gboolean vflist_index_is_selected(ViewFile *vf, gint row)
-{
-	FileData *fd;
-
-	fd = vf_index_get_data(vf, row);
-	return vflist_row_is_selected(vf, fd);
-}
-
-guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
-{
-	GtkTreeModel *store;
-	GtkTreeSelection *selection;
-	GList *slist;
-	guint count;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	slist = gtk_tree_selection_get_selected_rows(selection, &store);
-
-	if (bytes)
-		{
-		gint64 b = 0;
-		GList *work;
-
-		work = slist;
-		while (work)
-			{
-			GtkTreePath *tpath = work->data;
-			GtkTreeIter iter;
-			FileData *fd;
-
-			gtk_tree_model_get_iter(store, &iter, tpath);
-			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-			b += fd->size;
-
-			work = work->next;
-			}
-
-		*bytes = b;
-		}
-
-	count = g_list_length(slist);
-	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
-	g_list_free(slist);
-
-	return count;
-}
-
-GList *vflist_selection_get_list(ViewFile *vf)
-{
-	GtkTreeModel *store;
-	GtkTreeSelection *selection;
-	GList *slist;
-	GList *list = NULL;
-	GList *work;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	slist = gtk_tree_selection_get_selected_rows(selection, &store);
-	work = slist;
-	while (work)
-		{
-		GtkTreePath *tpath = work->data;
-		FileData *fd;
-		GtkTreeIter iter;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-
-		list = g_list_prepend(list, file_data_ref(fd));
-
-		if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
-			{
-			/* unexpanded - add whole group */
-			GList *work2 = fd->sidecar_files;
-			while (work2)
-				{
-				FileData *sfd = work2->data;
-				list = g_list_prepend(list, file_data_ref(sfd));
-				work2 = work2->next;
-				}
-			}
-
-		work = work->next;
-		}
-	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
-	g_list_free(slist);
-
-	return g_list_reverse(list);
-}
-
-GList *vflist_selection_get_list_by_index(ViewFile *vf)
-{
-	GtkTreeModel *store;
-	GtkTreeSelection *selection;
-	GList *slist;
-	GList *list = NULL;
-	GList *work;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	slist = gtk_tree_selection_get_selected_rows(selection, &store);
-	work = slist;
-	while (work)
-		{
-		GtkTreePath *tpath = work->data;
-		FileData *fd;
-		GtkTreeIter iter;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-
-		list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
-
-		work = work->next;
-		}
-	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
-	g_list_free(slist);
-
-	return g_list_reverse(list);
-}
-
-void vflist_select_all(ViewFile *vf)
-{
-	GtkTreeSelection *selection;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_selection_select_all(selection);
-
-	VFLIST(vf)->select_fd = NULL;
-}
-
-void vflist_select_none(ViewFile *vf)
-{
-	GtkTreeSelection *selection;
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_selection_unselect_all(selection);
-}
-
-static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
-{
-	GtkTreePath *tpath;
-	gboolean result;
-
-	tpath = gtk_tree_model_get_path(store, iter);
-	result = gtk_tree_path_prev(tpath);
-	if (result)
-		gtk_tree_model_get_iter(store, iter, tpath);
-
-	gtk_tree_path_free(tpath);
-
-	return result;
-}
-
-static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
-{
-	if (!gtk_tree_model_get_iter_first(store, iter))
-		return FALSE;
-
-	while (TRUE)
-		{
-		GtkTreeIter next = *iter;
-
-		if (gtk_tree_model_iter_next(store, &next))
-			*iter = next;
-		else
-			break;
-		}
-
-	return TRUE;
-}
-
-void vflist_select_invert(ViewFile *vf)
-{
-	GtkTreeIter iter;
-	GtkTreeSelection *selection;
-	GtkTreeModel *store;
-	gboolean valid;
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-
-	/* Backward iteration prevents scrolling to the end of the list,
-	 * it scrolls to the first selected row instead. */
-	valid = tree_model_get_iter_last(store, &iter);
-
-	while (valid)
-		{
-		gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
-
-		if (selected)
-			gtk_tree_selection_unselect_iter(selection, &iter);
-		else
-			gtk_tree_selection_select_iter(selection, &iter);
-
-		valid = tree_model_iter_prev(store, &iter);
-		}
-}
-
-void vflist_select_by_fd(ViewFile *vf, FileData *fd)
-{
-	GtkTreeIter iter;
-
-	if (vflist_find_row(vf, fd, &iter) < 0) return;
-
-	tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
-
-	if (!vflist_row_is_selected(vf, fd))
-		{
-		GtkTreeSelection *selection;
-		GtkTreeModel *store;
-		GtkTreePath *tpath;
-
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-		gtk_tree_selection_unselect_all(selection);
-		gtk_tree_selection_select_iter(selection, &iter);
-		vflist_move_cursor(vf, &iter);
-
-		store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-		tpath = gtk_tree_model_get_path(store, &iter);
-		gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
-		gtk_tree_path_free(tpath);
-		}
-}
-
-static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
-{
-	GList *work;
-	FileData *fd = NULL;
-
-	if (sel_fd->parent) sel_fd = sel_fd->parent;
-	work = vf->list;
-
-	while (work)
-		{
-		gint match;
-		fd = work->data;
-		work = work->next;
-
-		match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
-
-		if (match >= 0) break;
-		}
-
-	if (fd) vflist_select_by_fd(vf, fd);
-
-}
-
-void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
-{
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-	GtkTreeSelection *selection;
-	gboolean valid;
-	gint n = mark - 1;
-
-	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
-
-	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-
-	valid = gtk_tree_model_get_iter_first(store, &iter);
-	while (valid)
-		{
-		FileData *fd;
-		gboolean mark_val, selected;
-		gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
-
-		mark_val = file_data_get_mark(fd, n);
-		selected = gtk_tree_selection_iter_is_selected(selection, &iter);
-
-		switch (mode)
-			{
-			case MTS_MODE_SET: selected = mark_val;
-				break;
-			case MTS_MODE_OR: selected = mark_val || selected;
-				break;
-			case MTS_MODE_AND: selected = mark_val && selected;
-				break;
-			case MTS_MODE_MINUS: selected = !mark_val && selected;
-				break;
-			}
-
-		if (selected)
-			gtk_tree_selection_select_iter(selection, &iter);
-		else
-			gtk_tree_selection_unselect_iter(selection, &iter);
-
-		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
-		}
-}
-
-void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
-{
-	GtkTreeModel *store;
-	GtkTreeSelection *selection;
-	GList *slist;
-	GList *work;
-	gint n = mark - 1;
-
-	g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	slist = gtk_tree_selection_get_selected_rows(selection, &store);
-	work = slist;
-	while (work)
-		{
-		GtkTreePath *tpath = work->data;
-		FileData *fd;
-		GtkTreeIter iter;
-
-		gtk_tree_model_get_iter(store, &iter, tpath);
-		gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-
-		/* the change has a very limited range and the standard notification would trigger
-		   complete re-read of the directory - try to do only minimal update instead */
-		file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
-
-		switch (mode)
-			{
-			case STM_MODE_SET: file_data_set_mark(fd, n, 1);
-				break;
-			case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
-				break;
-			case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
-				break;
-			}
-
-		if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
-			{
-			vf_refresh_idle(vf);
-			}
-		else
-			{
-			/* mark functions can have various side effects - update all columns to be sure */
-			vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
-			/* mark functions can change sidecars too */
-			vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
-			}
-
-
-		file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
-
-		work = work->next;
-		}
-	g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
-	g_list_free(slist);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * core (population)
- *-----------------------------------------------------------------------------
- */
-
-static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
-{
-	GtkTreeViewColumn *column;
-	GtkCellRenderer *cell;
-	GList *list;
-
-	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
-	if (!column) return;
-
-	gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
-
-	list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
-	if (!list) return;
-	cell = list->data;
-	g_list_free(list);
-
-	g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
-	gtk_tree_view_column_set_visible(column, thumb);
-
-	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
-	if (!column) return;
-	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
-
-	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
-	if (!column) return;
-	gtk_tree_view_column_set_visible(column, !multiline);
-
-	column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
-	if (!column) return;
-	gtk_tree_view_column_set_visible(column, !multiline);
-}
-
-static gboolean vflist_is_multiline(ViewFile *vf)
-{
-	return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
-}
-
-
-static void vflist_populate_view(ViewFile *vf, gboolean force)
-{
-	GtkTreeStore *store;
-	GList *selected;
-
-	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-
-	vf_thumb_stop(vf);
-
-	if (!vf->list)
-		{
-		vflist_store_clear(vf, FALSE);
-		vf_send_update(vf);
-		return;
-		}
-
-	vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
-
-	selected = vflist_selection_get_list(vf);
-
-	vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
-
-	if (selected && vflist_selection_count(vf, NULL) == 0)
-		{
-		/* all selected files disappeared */
-		vflist_select_closest(vf, selected->data);
-		}
-
-	filelist_free(selected);
-
-	vf_send_update(vf);
-	vf_thumb_update(vf);
-}
-
-gboolean vflist_refresh(ViewFile *vf)
-{
-	GList *old_list;
-	gboolean ret = TRUE;
-
-	old_list = vf->list;
-	vf->list = NULL;
-
-	DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
-	if (vf->dir_fd)
-		{
-		file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
-
-		ret = filelist_read(vf->dir_fd, &vf->list, NULL);
-
-		if (vf->marks_enabled)
-		        {
-		        // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
-		        // each time a mark is changed.
-		        file_data_lock_list(vf->list);
-		        }
-	        else
-			{
-			// FIXME: only do this when needed (aka when we just switched from
-			// FIXME: marks-enabled to marks-disabled)
-			file_data_unlock_list(vf->list);
-			}
-
-		vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
-		file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
-
-		DEBUG_1("%s vflist_refresh: sort", get_exec_time());
-		vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
-		}
-
-	DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
-
-	vflist_populate_view(vf, FALSE);
-
-	DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
-
-	filelist_free(old_list);
-	DEBUG_1("%s vflist_refresh: done", get_exec_time());
-
-	return ret;
-}
-
-
-
-/* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
-
-#define CELL_HEIGHT_OVERRIDE 512
-
-static void cell_renderer_height_override(GtkCellRenderer *renderer)
-{
-	GParamSpec *spec;
-
-	spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
-	if (spec && G_IS_PARAM_SPEC_INT(spec))
-		{
-		GParamSpecInt *spec_int;
-
-		spec_int = G_PARAM_SPEC_INT(spec);
-		if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
-		}
-}
-
-static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
-{
-	static GdkColor color;
-	static GtkWidget *done = NULL;
-
-	if (done != widget)
-		{
-		GtkStyle *style;
-
-		style = gtk_widget_get_style(widget);
-		memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
-		shift_color(&color, -1, 0);
-		done = widget;
-		}
-
-	return &color;
-}
-
-static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
-				     GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
-{
-	ViewFile *vf = data;
-	gboolean set;
-
-	gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
-	g_object_set(G_OBJECT(cell),
-		     "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
-		     "cell-background-set", set, NULL);
-}
-
-static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
-{
-	GtkTreeViewColumn *column;
-	GtkCellRenderer *renderer;
-
-	column = gtk_tree_view_column_new();
-	gtk_tree_view_column_set_title(column, title);
-	gtk_tree_view_column_set_min_width(column, 4);
-
-	if (!image)
-		{
-		gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
-		renderer = gtk_cell_renderer_text_new();
-		if (right_justify)
-			{
-			g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
-			}
-		gtk_tree_view_column_pack_start(column, renderer, TRUE);
-		gtk_tree_view_column_add_attribute(column, renderer, "text", n);
-		if (expand)
-			gtk_tree_view_column_set_expand(column, TRUE);
-		}
-	else
-		{
-		gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
-		renderer = gtk_cell_renderer_pixbuf_new();
-		cell_renderer_height_override(renderer);
-		gtk_tree_view_column_pack_start(column, renderer, TRUE);
-		gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
-		}
-
-	gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
-	g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
-	g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
-
-	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
-}
-
-static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
-{
-	ViewFile *vf = data;
-	GtkTreeStore *store;
-	GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
-	GtkTreeIter iter;
-	FileData *fd;
-	gboolean marked;
-	guint col_idx;
-
-	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-	if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
-		return;
-
-	col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
-
-	g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
-
-	gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
-	marked = !marked;
-
-	/* the change has a very limited range and the standard notification would trigger
-	   complete re-read of the directory - try to do only minimal update instead */
-	file_data_unregister_notify_func(vf_notify_cb, vf);
-	file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
-	if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
-		{
-		vf_refresh_idle(vf);
-		}
-	else
-		{
-		/* mark functions can have various side effects - update all columns to be sure */
-		vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
-		/* mark functions can change sidecars too */
-		vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
-		}
-	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
-
-	gtk_tree_path_free(path);
-}
-
-static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
-{
-	GtkTreeViewColumn *column;
-	GtkCellRenderer *renderer;
-
-	renderer = gtk_cell_renderer_toggle_new();
-	column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
-
-	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
-	g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
-	g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
-
-	gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
-	gtk_tree_view_column_set_fixed_width(column, 22);
-	gtk_tree_view_column_set_visible(column, vf->marks_enabled);
-
-
-	g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * base
- *-----------------------------------------------------------------------------
- */
-
-gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
-{
-	gboolean ret;
-	if (!dir_fd) return FALSE;
-	if (vf->dir_fd == dir_fd) return TRUE;
-
-	file_data_unref(vf->dir_fd);
-	vf->dir_fd = file_data_ref(dir_fd);
-
-	/* force complete reload */
-	vflist_store_clear(vf, TRUE);
-
-	filelist_free(vf->list);
-	vf->list = NULL;
-
-	ret = vf_refresh(vf);
-	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
-	return ret;
-}
-
-void vflist_destroy_cb(GtkWidget *widget, gpointer data)
-{
-	ViewFile *vf = data;
-
-	file_data_unregister_notify_func(vf_notify_cb, vf);
-
-	vflist_select_idle_cancel(vf);
-	vf_refresh_idle_cancel(vf);
-	vf_thumb_stop(vf);
-
-	filelist_free(vf->list);
-}
-
-ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
-{
-	GtkTreeStore *store;
-	GtkTreeSelection *selection;
-	GType flist_types[FILE_COLUMN_COUNT];
-	gint i;
-	gint column;
-
-	vf->info = g_new0(ViewFileInfoList, 1);
-
-	flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
-	flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
-	flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
-	flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
-	flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
-	flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
-	flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
-	flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
-	flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
-	flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
-	for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
-		flist_types[i] = G_TYPE_BOOLEAN;
-
-	store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
-
-	vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
-	g_object_unref(store);
-
-	g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
-			 G_CALLBACK(vflist_expand_cb), vf);
-
-	g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
-			 G_CALLBACK(vflist_collapse_cb), vf);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
-	gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
-
-	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
-	gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
-
-	column = 0;
-
-	for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
-		{
-		vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
-		g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
-		column++;
-		}
-
-	vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
-	g_assert(column == FILE_VIEW_COLUMN_THUMB);
-	column++;
-
-	vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
-	g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
-	column++;
-
-	vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
-	g_assert(column == FILE_VIEW_COLUMN_SIZE);
-	column++;
-
-	vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
-	g_assert(column == FILE_VIEW_COLUMN_DATE);
-	column++;
-
-	file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
-	return vf;
-}
-
-void vflist_thumb_set(ViewFile *vf, gboolean enable)
-{
-	if (VFLIST(vf)->thumbs_enabled == enable) return;
-
-	VFLIST(vf)->thumbs_enabled = enable;
-
-	/* vflist_populate_view is better than vf_refresh:
-	   - no need to re-read the directory
-	   - force update because the formatted string has changed
-	*/
-	if (vf->layout)
-		{
-		vflist_populate_view(vf, TRUE);
-		gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
-		}
-}
-
-void vflist_marks_set(ViewFile *vf, gboolean enable)
-{
-	GList *columns, *work;
-
-	columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
-
-	work = columns;
-	while (work)
-		{
-		GtkTreeViewColumn *column = work->data;
-		gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
-		work = work->next;
-
-		if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
-			gtk_tree_view_column_set_visible(column, enable);
-		}
-
-	if (enable)
-		{
-		// Previously disabled, which means that vf->list is complete
-		file_data_lock_list(vf->list);
-		}
-	else
-		{
-		// Previously enabled, which means that vf->list is incomplete
-		}
-
-	g_list_free(columns);
-}
-
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_list.h	Fri Jul 07 12:11:56 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2004 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 VIEW_FILE_LIST_H
-#define VIEW_FILE_LIST_H
-
-
-#include "filedata.h"
-
-gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
-gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
-gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
-
-void vflist_dnd_init(ViewFile *vf);
-
-void vflist_destroy_cb(GtkWidget *widget, gpointer data);
-ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd);
-
-gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd);
-gboolean vflist_refresh(ViewFile *vf);
-
-void vflist_thumb_set(ViewFile *vf, gboolean enable);
-void vflist_marks_set(ViewFile *vf, gboolean enable);
-void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend);
-
-GList *vflist_selection_get_one(ViewFile *vf, FileData *fd);
-GList *vflist_pop_menu_file_list(ViewFile *vf);
-void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data);
-void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data);
-void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data);
-void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data);
-void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data);
-
-FileData *vflist_index_get_data(ViewFile *vf, gint row);
-gint vflist_index_by_fd(ViewFile *vf, FileData *fd);
-guint vflist_count(ViewFile *vf, gint64 *bytes);
-GList *vflist_get_list(ViewFile *vf);
-
-gboolean vflist_index_is_selected(ViewFile *vf, gint row);
-guint vflist_selection_count(ViewFile *vf, gint64 *bytes);
-GList *vflist_selection_get_list(ViewFile *vf);
-GList *vflist_selection_get_list_by_index(ViewFile *vf);
-
-void vflist_select_all(ViewFile *vf);
-void vflist_select_none(ViewFile *vf);
-void vflist_select_invert(ViewFile *vf);
-void vflist_select_by_fd(ViewFile *vf, FileData *fd);
-
-void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode);
-void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode);
-
-void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set);
-
-void vflist_thumb_progress_count(GList *list, gint *count, gint *done);
-void vflist_set_thumb_fd(ViewFile *vf, FileData *fd);
-FileData *vflist_thumb_next_fd(ViewFile *vf);
-void vflist_thumb_reset_all(ViewFile *vf);
-
-#endif
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */