Trim trailing white spaces.
[geeqie.git] / src / view_file_list.c
index 5c27611..b0bd169 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Geeqie
  * (C) 2004 John Ellis
- * Copyright (C) 2008 The Geeqie Team
+ * Copyright (C) 2008 - 2012 The Geeqie Team
  *
  * Author: John Ellis
  *
 #include "main.h"
 #include "view_file_list.h"
 
+#include "bar.h"
 #include "cache_maint.h"
-#include "debug.h"
 #include "dnd.h"
 #include "editors.h"
 #include "img-view.h"
-#include "info.h"
 #include "layout.h"
 #include "layout_image.h"
 #include "menu.h"
+#include "metadata.h"
 #include "thumb.h"
 #include "utilops.h"
-#include "ui_bookmark.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 */
 
-#define VFLIST_INFO_POINTER(_vf_) ((ViewFileInfoList *)(_vf_->info))
-#define VFLIST_INFO(_vf_, _part_) (VFLIST_INFO_POINTER(_vf_)->_part_)
-
+/* 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,
@@ -49,9 +50,24 @@ enum {
 };
 
 
-static gint vflist_row_is_selected(ViewFile *vf, FileData *fd);
-static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
-static void vflist_populate_view(ViewFile *vf);
+/* 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);
 
 
 /*
@@ -62,7 +78,7 @@ static void vflist_populate_view(ViewFile *vf);
 typedef struct {
        FileData *fd;
        GtkTreeIter *iter;
-       gint found;
+       gboolean found;
        gint row;
 } ViewFileFindRowData;
 
@@ -74,7 +90,7 @@ static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTr
        if (fd == find->fd)
                {
                *find->iter = *iter;
-               find->found = 1;
+               find->found = TRUE;
                return TRUE;
                }
        find->row++;
@@ -84,7 +100,7 @@ static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTr
 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
 {
        GtkTreeModel *store;
-       ViewFileFindRowData data = {fd, iter, 0, 0};
+       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);
@@ -97,54 +113,50 @@ static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
        return -1;
 }
 
-
-/*
-static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
+static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
 {
-       gint i = 0;
-       while (work)
-               {
-               FileData *fd_p = work->data;
-               if (fd == fd_p) return i;
+       GtkTreePath *tpath;
+       GtkTreeViewColumn *column;
 
-               i++;
+       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;
 
-               GList *work2 = fd_p->sidecar_files;
-               while (work2)
-                       {
-                       fd_p = work2->data;
-                       if (fd == fd_p) return i;
+               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);
 
-                       i++;
-                       work2 = work2->next;
-                       }
-               work = work->next;
+               return fd;
                }
-       return -1;
+
+       return NULL;
 }
-*/
 
-static gint vflist_sidecar_list_count(GList *work)
+static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
 {
-       gint i = 0;
-       while (work)
-               {
-               FileData *fd = work->data;
-               i++;
+       FileData *fd;
+       gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
 
-               GList *work2 = fd->sidecar_files;
-               while (work2)
-                       {
-                       i++;
-                       work2 = work2->next;
-                       }
-               work = work->next;
-               }
-       return i;
+       /* 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)
+{
+       GtkTreeModel *store;
+       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));
+}
 
-static void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
+void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
 {
        GtkTreeModel *store;
        GtkTreeIter iter;
@@ -167,27 +179,6 @@ static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
 }
 
 
-static gint vflist_column_idx(ViewFile *vf, gint store_idx)
-{
-       GList *columns, *work;
-       gint i = 0;
-
-       columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
-       work = columns;
-       while (work)
-               {
-               GtkTreeViewColumn *column = work->data;
-               if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
-                       break;
-               work = work->next;
-               i++;
-               }
-
-       g_list_free(columns);
-       return i;
-}
-
-
 /*
  *-----------------------------------------------------------------------------
  * dnd
@@ -200,49 +191,40 @@ static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
 {
        ViewFile *vf = data;
        GList *list = NULL;
-       gchar *uri_text = NULL;
-       gint total;
 
-       if (!VFLIST_INFO(vf, click_fd)) return;
+       if (!VFLIST(vf)->click_fd) return;
 
-       if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
+       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_INFO(vf, click_fd)));
+               list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
                }
 
        if (!list) return;
-
-       uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
+       uri_selection_data_set_uris_from_filelist(selection_data, list);
        filelist_free(list);
-
-       DEBUG_1(uri_text);
-
-       gtk_selection_data_set(selection_data, selection_data->target,
-                              8, (guchar *)uri_text, total);
-       g_free(uri_text);
 }
 
 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
 {
        ViewFile *vf = data;
 
-       vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
+       vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
 
-       if (VFLIST_INFO(vf, thumbs_enabled) &&
-           VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
+       if (VFLIST(vf)->thumbs_enabled &&
+           VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
                {
-               gint items;
+               guint items;
 
-               if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
+               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_INFO(vf, click_fd)->pixbuf, items);
+               dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
                }
 }
 
@@ -250,25 +232,52 @@ static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer
 {
        ViewFile *vf = data;
 
-       vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
+       vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
 
-       if (context->action == GDK_ACTION_MOVE)
+       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);
 }
 
 /*
@@ -277,23 +286,58 @@ void vflist_dnd_init(ViewFile *vf)
  *-----------------------------------------------------------------------------
  */
 
+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_INFO(vf, click_fd)) return NULL;
+       if (!VFLIST(vf)->click_fd) return NULL;
 
-       if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
+       if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
                {
                return vf_selection_get_list(vf);
                }
-
-       return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
+       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_INFO(vf, click_fd)))
+       if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
                {
                GList *list;
 
@@ -303,7 +347,7 @@ void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
                }
        else
                {
-               view_window_new(VFLIST_INFO(vf, click_fd));
+               view_window_new(VFLIST(vf)->click_fd);
                }
 }
 
@@ -314,7 +358,7 @@ void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
 
        list = vf_pop_menu_file_list(vf);
        if (options->file_ops.enable_in_place_rename &&
-           list && !list->next && VFLIST_INFO(vf, click_fd))
+           list && !list->next && VFLIST(vf)->click_fd)
                {
                GtkTreeModel *store;
                GtkTreeIter iter;
@@ -322,13 +366,13 @@ void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
                filelist_free(list);
 
                store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-               if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
+               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,
-                                         vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
+                                         FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
                                          vflist_row_rename_cb, vf);
                        gtk_tree_path_free(tpath);
                        }
@@ -338,18 +382,18 @@ void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
        file_util_rename(NULL, list, vf->listview);
 }
 
-static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
+void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
 {
        ViewFile *vf = data;
 
-       vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
+       vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
        if (vf->layout)
                {
-               layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
+               layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
                }
        else
                {
-               vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
+               vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
                }
 }
 
@@ -357,168 +401,50 @@ void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
 {
        ViewFile *vf = data;
 
-       vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
+       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_INFO(vf, click_fd), FALSE);
-       VFLIST_INFO(vf, click_fd) = NULL;
+       vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
+       VFLIST(vf)->click_fd = NULL;
        vf->popup = NULL;
 }
 
 
-static GtkWidget *vflist_pop_menu(ViewFile *vf)
-{
-       GtkWidget *menu;
-       GtkWidget *item;
-       GtkWidget *submenu;
-       gint active;
-
-       vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
-       active = (VFLIST_INFO(vf, click_fd) != NULL);
-
-       menu = popup_menu_short_lived();
-       g_signal_connect(G_OBJECT(menu), "destroy",
-                        G_CALLBACK(vf_popup_destroy_cb), vf);
-
-       if (vf->clicked_mark > 0)
-               {
-               gint mark = vf->clicked_mark;
-               gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark);
-               gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark);
-               gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark);
-               gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark);
-               gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark);
-               gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark);
-               gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark);
-
-               g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
-
-               vf->active_mark = mark;
-               vf->clicked_mark = 0;
-
-               menu_item_add_sensitive(menu, str_set_mark, active,
-                                       G_CALLBACK(vf_pop_menu_set_mark_sel_cb), vf);
-
-               menu_item_add_sensitive(menu, str_res_mark, active,
-                                       G_CALLBACK(vf_pop_menu_res_mark_sel_cb), vf);
-
-               menu_item_add_sensitive(menu, str_toggle_mark, active,
-                                       G_CALLBACK(vf_pop_menu_toggle_mark_sel_cb), vf);
-
-               menu_item_add_divider(menu);
-
-               menu_item_add_sensitive(menu, str_sel_mark, active,
-                                       G_CALLBACK(vf_pop_menu_sel_mark_cb), vf);
-               menu_item_add_sensitive(menu, str_sel_mark_or, active,
-                                       G_CALLBACK(vf_pop_menu_sel_mark_or_cb), vf);
-               menu_item_add_sensitive(menu, str_sel_mark_and, active,
-                                       G_CALLBACK(vf_pop_menu_sel_mark_and_cb), vf);
-               menu_item_add_sensitive(menu, str_sel_mark_minus, active,
-                                       G_CALLBACK(vf_pop_menu_sel_mark_minus_cb), vf);
-
-               menu_item_add_divider(menu);
-
-               g_free(str_set_mark);
-               g_free(str_res_mark);
-               g_free(str_toggle_mark);
-               g_free(str_sel_mark);
-               g_free(str_sel_mark_and);
-               g_free(str_sel_mark_or);
-               g_free(str_sel_mark_minus);
-               }
-
-       submenu_add_edit(menu, &item, G_CALLBACK(vf_pop_menu_edit_cb), vf);
-       gtk_widget_set_sensitive(item, active);
-
-       menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
-                                     G_CALLBACK(vf_pop_menu_info_cb), vf);
-       menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
-                                     G_CALLBACK(vf_pop_menu_view_cb), vf);
-
-       menu_item_add_divider(menu);
-       menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
-                                     G_CALLBACK(vf_pop_menu_copy_cb), vf);
-       menu_item_add_sensitive(menu, _("_Move..."), active,
-                               G_CALLBACK(vf_pop_menu_move_cb), vf);
-       menu_item_add_sensitive(menu, _("_Rename..."), active,
-                               G_CALLBACK(vf_pop_menu_rename_cb), vf);
-       menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
-                                     G_CALLBACK(vf_pop_menu_delete_cb), vf);
-       if (options->show_copy_path)
-               menu_item_add_sensitive(menu, _("_Copy path"), active,
-                                       G_CALLBACK(vf_pop_menu_copy_path_cb), vf);
-
-       menu_item_add_divider(menu);
-
-       submenu = submenu_add_sort(NULL, G_CALLBACK(vf_pop_menu_sort_cb), vf,
-                                  FALSE, FALSE, TRUE, vf->sort_method);
-       menu_item_add_divider(submenu);
-       menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
-                           G_CALLBACK(vf_pop_menu_sort_ascend_cb), vf);
-
-       item = menu_item_add(menu, _("_Sort"), NULL, NULL);
-       gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
-
-       menu_item_add_check(menu, _("View as _icons"), FALSE,
-                           G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
-       menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
-                           G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
-       menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vf_pop_menu_refresh_cb), vf);
-
-       return menu;
-}
-
 /*
  *-----------------------------------------------------------------------------
  * callbacks
  *-----------------------------------------------------------------------------
  */
 
-static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
+static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
 {
        ViewFile *vf = data;
-       gchar *old_path;
        gchar *new_path;
 
-       if (strlen(new) == 0) return FALSE;
+       if (!new || !new[0]) return FALSE;
 
-       old_path = concat_dir_and_file(vf->path, old);
-       new_path = concat_dir_and_file(vf->path, new);
+       new_path = g_build_filename(vf->dir_fd->path, new, NULL);
 
-       if (strchr(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 if (isfile(new_path))
-               {
-               gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
-               file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
-               g_free(text);
-               }
        else
                {
-               gint row = vf_index_by_path(vf, old_path);
-               if (row >= 0)
-                       {
-                       GList *work = g_list_nth(vf->list, row);
-                       FileData *fd = work->data;
-
-                       if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
-                               {
-                               gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
-                               file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
-                               g_free(text);
-                               }
-                       }
-
+               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(old_path);
+       
        g_free(new_path);
 
        return FALSE;
@@ -532,7 +458,7 @@ static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *p
        GtkTreePath *tpath;
        gint cw, ch;
 
-       if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
+       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);
@@ -541,12 +467,12 @@ static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *p
        popup_menu_position_clamp(menu, x, y, 0);
 }
 
-gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
        ViewFile *vf = data;
        GtkTreePath *tpath;
 
-       if (event->keyval != GDK_Menu) return FALSE;
+       if (event->keyval != GDK_KEY_Menu) return FALSE;
 
        gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
        if (tpath)
@@ -556,21 +482,21 @@ gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
 
                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_INFO(vf, click_fd), -1);
+               gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
                gtk_tree_path_free(tpath);
                }
        else
                {
-               VFLIST_INFO(vf, click_fd) = NULL;
+               VFLIST(vf)->click_fd = NULL;
                }
 
-       vf->popup = vflist_pop_menu(vf);
+       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;
 }
 
-gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 {
        ViewFile *vf = data;
        GtkTreePath *tpath;
@@ -597,17 +523,14 @@ gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 
                gtk_tree_model_get_iter(store, &iter, tpath);
                gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-#if 0
-               gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
-#endif
                gtk_tree_path_free(tpath);
                }
 
-       VFLIST_INFO(vf, click_fd) = fd;
+       VFLIST(vf)->click_fd = fd;
 
        if (bevent->button == MOUSE_BUTTON_RIGHT)
                {
-               vf->popup = vflist_pop_menu(vf);
+               vf->popup = vf_pop_menu(vf);
                gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
                                bevent->button, bevent->time);
                return TRUE;
@@ -635,7 +558,7 @@ gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
                gtk_widget_grab_focus(widget);
 
 
-               /* returning FALSE and further processing of the event is needed for 
+               /* 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 */
@@ -643,17 +566,15 @@ gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
                return (gtk_tree_selection_count_selected_rows(selection) > 1);
                }
 
-#if 0
        if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
                {
                if (vf->layout) layout_image_full_screen_start(vf->layout);
                }
-#endif
 
        return FALSE;
 }
 
-gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 {
        ViewFile *vf = data;
        GtkTreePath *tpath;
@@ -662,7 +583,7 @@ gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 
        if (bevent->button == MOUSE_BUTTON_MIDDLE)
                {
-               vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
+               vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
                }
 
        if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
@@ -684,7 +605,7 @@ gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 
        if (bevent->button == MOUSE_BUTTON_MIDDLE)
                {
-               if (fd && VFLIST_INFO(vf, click_fd) == fd)
+               if (fd && VFLIST(vf)->click_fd == fd)
                        {
                        GtkTreeSelection *selection;
 
@@ -701,7 +622,7 @@ gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
                return TRUE;
                }
 
-       if (fd && VFLIST_INFO(vf, click_fd) == fd &&
+       if (fd && VFLIST(vf)->click_fd == fd &&
            !(bevent->state & GDK_SHIFT_MASK ) &&
            !(bevent->state & GDK_CONTROL_MASK ) &&
            vflist_row_is_selected(vf, fd))
@@ -712,7 +633,6 @@ gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
                gtk_tree_selection_unselect_all(selection);
                gtk_tree_selection_select_iter(selection, &iter);
                vflist_move_cursor(vf, &iter);
-//             return TRUE;// FIXME - expand
                }
 
        return FALSE;
@@ -723,6 +643,7 @@ 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);
@@ -734,7 +655,7 @@ static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
        if (sel_fd && options->image.enable_read_ahead && row >= 0)
                {
                if (row > g_list_index(vf->list, cur_fd) &&
-                   row + 1 < vf_count(vf, NULL))
+                   (guint) (row + 1) < vf_count(vf, NULL))
                        {
                        read_ahead_fd = vf_index_get_data(vf, row + 1);
                        }
@@ -747,32 +668,35 @@ static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
        layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
 }
 
-static gint vflist_select_idle_cb(gpointer data)
+static gboolean vflist_select_idle_cb(gpointer data)
 {
        ViewFile *vf = data;
 
        if (!vf->layout)
                {
-               VFLIST_INFO(vf, select_idle_id) = -1;
+               VFLIST(vf)->select_idle_id = 0;
                return FALSE;
                }
 
        vf_send_update(vf);
 
-       if (VFLIST_INFO(vf, select_fd))
+       if (VFLIST(vf)->select_fd)
                {
-               vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
-               VFLIST_INFO(vf, select_fd) = NULL;
+               vflist_select_image(vf, VFLIST(vf)->select_fd);
+               VFLIST(vf)->select_fd = NULL;
                }
 
-       VFLIST_INFO(vf, select_idle_id) = -1;
+       VFLIST(vf)->select_idle_id = 0;
        return FALSE;
 }
 
 static void vflist_select_idle_cancel(ViewFile *vf)
 {
-       if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
-       VFLIST_INFO(vf, select_idle_id) = -1;
+       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,
@@ -784,102 +708,190 @@ static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *stor
        if (!path_currently_selected &&
            gtk_tree_model_get_iter(store, &iter, tpath))
                {
-               gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
+               gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
                }
        else
                {
-               VFLIST_INFO(vf, select_fd) = NULL;
+               VFLIST(vf)->select_fd = NULL;
                }
 
        if (vf->layout &&
-           VFLIST_INFO(vf, select_idle_id) == -1)
+           !VFLIST(vf)->select_idle_id)
                {
-               VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
+               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 gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
-                                       gboolean path_currently_selected, gpointer data)
-{
-       return TRUE;
+
+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)
 {
-       int i;
        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);
+               }
 
-       if (fd->sidecar_files)
-               sidecars = file_data_sc_list_to_string(fd);
-       size = text_from_size(fd->size);
+       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_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
-                                       FILE_COLUMN_NAME, fd->name,
+                                       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, text_from_time(fd->date),
+                                       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);
-       for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
-               gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -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);
-       if (sidecars)
-               g_free(sidecars);
+       g_free(sidecars);
+       g_free(name);
+       g_free(formatted);
 }
 
-static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
+static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
 {
        GList *work;
-       GtkTreeIter s_iter;
-       gint valid;
-
-       vflist_setup_iter(vf, store, iter, fd);
-
-
-       /* this is almost the same code as in vflist_populate_view
-          maybe it should be made more generic and used in both places */
-
+       GtkTreeIter iter;
+       gboolean valid;
+       gint num_ordered = 0;
+       gint num_prepended = 0;
 
-       valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
+       valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
 
-       work = fd->sidecar_files;
+       work = list;
        while (work)
                {
                gint match;
-               FileData *sfd = work->data;
-               gint done = FALSE;
+               FileData *fd = work->data;
+               gboolean done = FALSE;
 
                while (!done)
                        {
-                       FileData *old_sfd = NULL;
+                       FileData *old_fd = NULL;
+                       gint old_version = 0;
 
                        if (valid)
                                {
-                               gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
+                               gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+                                                  FILE_COLUMN_POINTER, &old_fd,
+                                                  FILE_COLUMN_VERSION, &old_version,
+                                                  -1);
 
-                               if (sfd == old_sfd)
+                               if (fd == old_fd)
                                        {
                                        match = 0;
                                        }
                                else
                                        {
-                                       match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
+                                       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;
@@ -891,26 +903,47 @@ static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, G
 
                                if (valid)
                                        {
-                                       gtk_tree_store_insert_before(store, &new, iter, &s_iter);
+                                       num_ordered++;
+                                       gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
                                        }
                                else
                                        {
-                                       gtk_tree_store_append(store, &new, iter);
+                                       /*
+                                           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, sfd);
+                               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)
                                {
-                               valid = gtk_tree_store_remove(store, &s_iter);
+                               file_data_unref(old_fd);
+                               valid = gtk_tree_store_remove(store, &iter);
                                }
                        else
                                {
-                               vflist_setup_iter(vf, store, &s_iter, sfd);
+                               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), &s_iter);
+                               if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
 
                                done = TRUE;
                                }
@@ -920,11 +953,34 @@ static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, G
 
        while (valid)
                {
-               valid = gtk_tree_store_remove(store, &s_iter);
+               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, gint ascend)
+void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
 {
        gint i;
        GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
@@ -975,96 +1031,60 @@ void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
  *-----------------------------------------------------------------------------
  */
 
-static gint vflist_thumb_next(ViewFile *vf);
 
-static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
+void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
 {
-       if (vf->func_thumb_status)
+       GList *work = list;
+       while (work)
                {
-               vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
-               }
-}
-
-static void vflist_thumb_cleanup(ViewFile *vf)
-{
-       vflist_thumb_status(vf, 0.0, NULL);
-
-       vf->thumbs_count = 0;
-       vf->thumbs_running = FALSE;
-
-       thumb_loader_free(vf->thumbs_loader);
-       vf->thumbs_loader = NULL;
-
-       vf->thumbs_filedata = NULL;
-}
+               FileData *fd = work->data;
+               work = work->next;
 
-static void vflist_thumb_stop(ViewFile *vf)
-{
-       if (vf->thumbs_running) vflist_thumb_cleanup(vf);
+               if (fd->thumb_pixbuf) (*done)++;
+               
+               if (fd->sidecar_files)
+                       {
+                       vflist_thumb_progress_count(fd->sidecar_files, count, done);
+                       }
+               (*count)++;
+               }
 }
 
-static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
+void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
 {
        GtkTreeStore *store;
        GtkTreeIter iter;
 
        if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
 
-       if (fd->pixbuf) g_object_unref(fd->pixbuf);
-       fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
-
        store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-       gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
-
-       vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
-}
-
-static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
-{
-       ViewFile *vf = data;
-
-       if (vf->thumbs_filedata && vf->thumbs_loader == tl)
-               {
-               vflist_thumb_do(vf, tl, vf->thumbs_filedata);
-               }
-
-       while (vflist_thumb_next(vf));
-}
-
-static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
-{
-       ViewFile *vf = data;
-
-       if (vf->thumbs_filedata && vf->thumbs_loader == tl)
-               {
-               vflist_thumb_do(vf, tl, vf->thumbs_filedata);
-               }
-
-       while (vflist_thumb_next(vf));
+       gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
 }
 
-static gint vflist_thumb_next(ViewFile *vf)
+FileData *vflist_thumb_next_fd(ViewFile *vf)
 {
        GtkTreePath *tpath;
        FileData *fd = NULL;
 
        /* first check the visible files */
 
-       if (GTK_WIDGET_REALIZED(vf->listview) &&
-           gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+       if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
                {
                GtkTreeModel *store;
                GtkTreeIter iter;
-               gint valid = TRUE;
-
+               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)
                        {
-                       gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
-                       if (fd->pixbuf) fd = NULL;
+                       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);
                        }
@@ -1078,7 +1098,7 @@ static gint vflist_thumb_next(ViewFile *vf)
                while (work && !fd)
                        {
                        FileData *fd_p = work->data;
-                       if (!fd_p->pixbuf)
+                       if (!fd_p->thumb_pixbuf)
                                fd = fd_p;
                        else
                                {
@@ -1087,7 +1107,7 @@ static gint vflist_thumb_next(ViewFile *vf)
                                while (work2 && !fd)
                                        {
                                        fd_p = work2->data;
-                                       if (!fd_p->pixbuf) fd = fd_p;
+                                       if (!fd_p->thumb_pixbuf) fd = fd_p;
                                        work2 = work2->next;
                                        }
                                }
@@ -1095,47 +1115,23 @@ static gint vflist_thumb_next(ViewFile *vf)
                        }
                }
 
-       if (!fd)
-               {
-               /* done */
-               vflist_thumb_cleanup(vf);
-               return FALSE;
-               }
-
-       vf->thumbs_count++;
-
-       vf->thumbs_filedata = fd;
-
-       thumb_loader_free(vf->thumbs_loader);
+       return fd;
+}
 
-       vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
-       thumb_loader_set_callbacks(vf->thumbs_loader,
-                                  vflist_thumb_done_cb,
-                                  vflist_thumb_error_cb,
-                                  NULL,
-                                  vf);
 
-       if (!thumb_loader_start(vf->thumbs_loader, fd->path))
-               {
-               /* set icon to unknown, continue */
-               DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
-               vflist_thumb_do(vf, vf->thumbs_loader, fd);
-
-               return TRUE;
-               }
-
-       return FALSE;
-}
-
-static void vflist_thumb_update(ViewFile *vf)
+void vflist_thumb_reset_all(ViewFile *vf)
 {
-       vflist_thumb_stop(vf);
-       if (!VFLIST_INFO(vf, thumbs_enabled)) return;
-
-       vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
-       vf->thumbs_running = TRUE;
-
-       while (vflist_thumb_next(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;
+               }
 }
 
 /*
@@ -1149,36 +1145,37 @@ FileData *vflist_index_get_data(ViewFile *vf, gint row)
        return g_list_nth_data(vf->list, row);
 }
 
-static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
+gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
 {
        gint p = 0;
-       GList *work;
-
-       if (!path) return -1;
+       GList *work, *work2;
 
        work = vf->list;
        while (work)
                {
-               FileData *fd_n = work->data;
-               if (strcmp(path, fd_n->path) == 0)
+               FileData *list_fd = work->data;
+               if (list_fd == fd) return p;
+               
+               work2 = list_fd->sidecar_files;
+               while (work2)
                        {
-                       if (fd) *fd = fd_n;
-                       return p;
+                       /* 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++;
                }
 
-       if (fd) *fd = NULL;
        return -1;
 }
 
-gint vflist_index_by_path(ViewFile *vf, const gchar *path)
-{
-       return vflist_row_by_path(vf, path, NULL);
-}
-
-gint vflist_count(ViewFile *vf, gint64 *bytes)
+guint vflist_count(ViewFile *vf, gint64 *bytes)
 {
        if (bytes)
                {
@@ -1222,13 +1219,13 @@ GList *vflist_get_list(ViewFile *vf)
  *-----------------------------------------------------------------------------
  */
 
-static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
+static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
 {
        GtkTreeModel *store;
        GtkTreeSelection *selection;
        GList *slist;
        GList *work;
-       gint found = FALSE;
+       gboolean found = FALSE;
 
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
        slist = gtk_tree_selection_get_selected_rows(selection, &store);
@@ -1250,7 +1247,7 @@ static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
        return found;
 }
 
-gint vflist_index_is_selected(ViewFile *vf, gint row)
+gboolean vflist_index_is_selected(ViewFile *vf, gint row)
 {
        FileData *fd;
 
@@ -1258,12 +1255,12 @@ gint vflist_index_is_selected(ViewFile *vf, gint row)
        return vflist_row_is_selected(vf, fd);
 }
 
-gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
+guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
 {
        GtkTreeModel *store;
        GtkTreeSelection *selection;
        GList *slist;
-       gint count;
+       guint count;
 
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
        slist = gtk_tree_selection_get_selected_rows(selection, &store);
@@ -1318,6 +1315,18 @@ GList *vflist_selection_get_list(ViewFile *vf)
                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;
                }
@@ -1364,7 +1373,7 @@ void vflist_select_all(ViewFile *vf)
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
        gtk_tree_selection_select_all(selection);
 
-       VFLIST_INFO(vf, select_fd) = NULL;
+       VFLIST(vf)->select_fd = NULL;
 }
 
 void vflist_select_none(ViewFile *vf)
@@ -1424,7 +1433,7 @@ void vflist_select_invert(ViewFile *vf)
 
        while (valid)
                {
-               gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
+               gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
 
                if (selected)
                        gtk_tree_selection_unselect_iter(selection, &iter);
@@ -1461,12 +1470,35 @@ void vflist_select_by_fd(ViewFile *vf, FileData *fd)
                }
 }
 
+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;
-       gint valid;
+       gboolean valid;
        gint n = mark - 1;
 
        g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
@@ -1481,18 +1513,18 @@ void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
                gboolean mark_val, selected;
                gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
 
-               mark_val = fd->marks[n];
+               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;
+                       case MTS_MODE_OR: selected = mark_val || selected;
                                break;
-                       case MTS_MODE_AND: selected = mark_val & selected;
+                       case MTS_MODE_AND: selected = mark_val && selected;
                                break;
-                       case MTS_MODE_MINUS: selected = !mark_val & selected;
+                       case MTS_MODE_MINUS: selected = !mark_val && selected;
                                break;
                        }
 
@@ -1527,17 +1559,34 @@ void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
                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: fd->marks[n] = 1;
+                       case STM_MODE_SET: file_data_set_mark(fd, n, 1);
                                break;
-                       case STM_MODE_RESET: fd->marks[n] = 0;
+                       case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
                                break;
-                       case STM_MODE_TOGGLE: fd->marks[n] = !fd->marks[n];
+                       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);
+                       }
 
-               gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, fd->marks[n], -1);
+               
+               file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
 
                work = work->next;
                }
@@ -1551,164 +1600,111 @@ void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
  *-----------------------------------------------------------------------------
  */
 
-static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
+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_COLUMN_THUMB - 1);
+       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, ((thumb) ? options->thumbnails.max_width : 4) + 10);
+       gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
 
-       list = gtk_tree_view_column_get_cell_renderers(column);
+       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", (thumb) ? options->thumbnails.max_height : -1, NULL);
-       gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
+       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)
+
+static void vflist_populate_view(ViewFile *vf, gboolean force)
 {
        GtkTreeStore *store;
-       GtkTreeIter iter;
-       gint thumbs;
-       GList *work;
-       GtkTreeRowReference *visible_row = NULL;
-       GtkTreePath *tpath;
-       gint valid;
+       GList *selected;
 
        store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-       thumbs = VFLIST_INFO(vf, thumbs_enabled);
 
-       vflist_thumb_stop(vf);
+       vf_thumb_stop(vf);
 
        if (!vf->list)
                {
-               gtk_tree_store_clear(store);
+               vflist_store_clear(vf);
                vf_send_update(vf);
                return;
                }
 
-       if (GTK_WIDGET_REALIZED(vf->listview) &&
-           gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
-               {
-               visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
-               gtk_tree_path_free(tpath);
-               }
-
-       vflist_listview_set_height(vf->listview, thumbs);
+       vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
 
-       valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
-
-       work = vf->list;
-       while (work)
-               {
-               gint match;
-               FileData *fd = work->data;
-               gint done = FALSE;
-
-               while (!done)
-                       {
-                       FileData *old_fd = NULL;
-
-                       if (valid)
-                               {
-                               gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
-
-                               if (fd == old_fd)
-                                       {
-                                       match = 0;
-                                       }
-                               else
-                                       {
-                                       match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
-                                       if (match == 0)
-                                               match = -1; /* probably should not happen*/
-                                       }
-                               }
-
-                       else
-                               {
-                               match = -1;
-                               }
-
-                       if (match < 0)
-                               {
-                               GtkTreeIter new;
-
-                               if (valid)
-                                       {
-                                       gtk_tree_store_insert_before(store, &new, NULL, &iter);
-                                       }
-                               else
-                                       {
-                                       gtk_tree_store_append(store, &new, NULL);
-                                       }
-                               vflist_setup_iter_with_sidecars(vf, store, &new, fd);
-
-                               done = TRUE;
-                               }
-                       else if (match > 0)
-                               {
-                               valid = gtk_tree_store_remove(store, &iter);
-                               }
-                       else
-                               {
-                               vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
-
-                               if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
-
-                               done = TRUE;
-                               }
-                       }
-               work = work->next;
-               }
-
-       while (valid)
-               {
-               valid = gtk_tree_store_remove(store, &iter);
-               }
+       selected = vflist_selection_get_list(vf);
+       
+       vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
 
-       if (visible_row)
+       if (selected && vflist_selection_count(vf, NULL) == 0)
                {
-               if (gtk_tree_row_reference_valid(visible_row))
-                       {
-                       tpath = gtk_tree_row_reference_get_path(visible_row);
-                       gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
-                       gtk_tree_path_free(tpath);
-                       }
-               gtk_tree_row_reference_free(visible_row);
+               /* all selected files disappeared */
+               vflist_select_closest(vf, selected->data);
                }
 
+       filelist_free(selected);
+       
        vf_send_update(vf);
-       vflist_thumb_update(vf);
+       vf_thumb_update(vf);
 }
 
-gint vflist_refresh(ViewFile *vf)
+gboolean vflist_refresh(ViewFile *vf)
 {
        GList *old_list;
-       gint ret = TRUE;
+       gboolean ret = TRUE;
 
        old_list = vf->list;
        vf->list = NULL;
 
-       if (vf->path)
+       DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
+       if (vf->dir_fd)
                {
-               ret = filelist_read(vf->path, &vf->list, NULL);
+               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);
+               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);
                }
 
-       vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
-       vflist_populate_view(vf);
+       DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
+
+       vflist_populate_view(vf, FALSE);
 
        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
@@ -1757,7 +1753,7 @@ static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRend
                     "cell-background-set", set, NULL);
 }
 
-static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
+static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
 {
        GtkTreeViewColumn *column;
        GtkCellRenderer *renderer;
@@ -1795,26 +1791,44 @@ static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title,
        gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
 }
 
-static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
+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 mark;
+       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;
+               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, &mark, -1);
-       mark = !mark;
-       fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
+       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_store_set(store, &iter, col_idx, mark, -1);
        gtk_tree_path_free(path);
 }
 
@@ -1822,10 +1836,6 @@ static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar
 {
        GtkTreeViewColumn *column;
        GtkCellRenderer *renderer;
-       GtkTreeStore *store;
-       gint index;
-
-       store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
 
        renderer = gtk_cell_renderer_toggle_new();
        column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
@@ -1834,12 +1844,12 @@ static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar
        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));
 
-       index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
-       gtk_tree_view_column_set_fixed_width(column, 16);
+       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), store);
+       g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
 }
 
 /*
@@ -1848,58 +1858,58 @@ static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar
  *-----------------------------------------------------------------------------
  */
 
-gint vflist_set_path(ViewFile *vf, const gchar *path)
+gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
 {
-       GtkTreeStore *store;
+       gboolean ret;
+       if (!dir_fd) return FALSE;
+       if (vf->dir_fd == dir_fd) return TRUE;
 
-       if (!path) return FALSE;
-       if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
-
-       g_free(vf->path);
-       vf->path = g_strdup(path);
+       file_data_unref(vf->dir_fd);
+       vf->dir_fd = file_data_ref(dir_fd);
 
        /* force complete reload */
-       store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-       gtk_tree_store_clear(store);
+       vflist_store_clear(vf);
 
        filelist_free(vf->list);
        vf->list = NULL;
 
-       return vf_refresh(vf);
+       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);
-       vflist_thumb_stop(vf);
+       vf_refresh_idle_cancel(vf);
+       vf_thumb_stop(vf);
 
        filelist_free(vf->list);
 }
 
-ViewFile *vflist_new(ViewFile *vf, const gchar *path)
+ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
 {
        GtkTreeStore *store;
        GtkTreeSelection *selection;
-
        GType flist_types[FILE_COLUMN_COUNT];
-       int i;
+       gint i;
+       gint column;
 
        vf->info = g_new0(ViewFileInfoList, 1);
        
-       VFLIST_INFO(vf, click_fd) = NULL;
-       VFLIST_INFO(vf, select_fd) = NULL;
-       VFLIST_INFO(vf, thumbs_enabled) = FALSE;
-
-       VFLIST_INFO(vf, select_idle_id) = -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;
@@ -1909,6 +1919,12 @@ ViewFile *vflist_new(ViewFile *vf, const gchar *path)
        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);
@@ -1916,36 +1932,56 @@ ViewFile *vflist_new(ViewFile *vf, const gchar *path)
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
        gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
 
-       vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
+       column = 0;
 
        for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
-               vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
+               {
+               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_NAME, _("Name"), FALSE, FALSE, FALSE);
-       vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
+       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, gint enable)
+void vflist_thumb_set(ViewFile *vf, gboolean enable)
 {
-       if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
+       if (VFLIST(vf)->thumbs_enabled == enable) return;
 
-       VFLIST_INFO(vf, thumbs_enabled) = enable;
-       if (vf->layout) vf_refresh(vf);
+       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, gint enable)
+void vflist_marks_set(ViewFile *vf, gboolean enable)
 {
        GList *columns, *work;
 
-       if (vf->marks_enabled == enable) return;
-
-       vf->marks_enabled = enable;
-
        columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
 
        work = columns;
@@ -1960,208 +1996,6 @@ void vflist_marks_set(ViewFile *vf, gint enable)
                }
 
        g_list_free(columns);
-       //vf_refresh(vf);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * maintenance (for rename, move, remove)
- *-----------------------------------------------------------------------------
- */
-
-static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
-{
-       GList *list = NULL;
-       GList *work;
-       gint rev = row - 1;
-       row ++;
-
-       work = ignore_list;
-       while (work)
-               {
-               gint f = vf_index_by_path(vf, work->data);
-               if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
-               work = work->next;
-               }
-
-       while (list)
-               {
-               gint c = TRUE;
-               work = list;
-               while (work && c)
-                       {
-                       gpointer p = work->data;
-                       work = work->next;
-                       if (row == GPOINTER_TO_INT(p))
-                               {
-                               row++;
-                               c = FALSE;
-                               }
-                       if (rev == GPOINTER_TO_INT(p))
-                               {
-                               rev--;
-                               c = FALSE;
-                               }
-                       if (!c) list = g_list_remove(list, p);
-                       }
-               if (c && list)
-                       {
-                       g_list_free(list);
-                       list = NULL;
-                       }
-               }
-       if (row > count - 1)
-               {
-               if (rev < 0)
-                       return -1;
-               else
-                       return rev;
-               }
-       else
-               {
-               return row;
-               }
-}
-
-gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
-{
-       gint ret = FALSE;
-       gchar *source_base;
-       gchar *dest_base;
-
-       if (g_list_index(vf->list, fd) < 0) return FALSE;
-
-       source_base = remove_level_from_path(fd->change->source);
-       dest_base = remove_level_from_path(fd->change->dest);
-
-
-       if (strcmp(source_base, dest_base) == 0)
-               {
-               GtkTreeStore *store;
-               GtkTreeIter iter;
-               GtkTreeIter position;
-               gint old_row;
-               gint n;
-
-               old_row = g_list_index(vf->list, fd);
-
-               vf->list = g_list_remove(vf->list, fd);
-
-               vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
-               n = g_list_index(vf->list, fd);
-
-               store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-               if (vflist_find_row(vf, fd, &iter) >= 0 &&
-                   gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
-                       {
-                       if (old_row >= n)
-                               {
-                               gtk_tree_store_move_before(store, &iter, &position);
-                               }
-                       else
-                               {
-                               gtk_tree_store_move_after(store, &iter, &position);
-                               }
-                       }
-               gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
-
-               ret = TRUE;
-               }
-       else
-               {
-               ret = vflist_maint_removed(vf, fd, NULL);
-               }
-
-       g_free(source_base);
-       g_free(dest_base);
-
-       return ret;
-}
-
-gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
-{
-       GtkTreeIter iter;
-       GList *list;
-       gint row;
-       gint new_row = -1;
-
-       row = g_list_index(vf->list, fd);
-       if (row < 0) return FALSE;
-
-       if (vflist_index_is_selected(vf, row) &&
-           layout_image_get_collection(vf->layout, NULL) == NULL)
-               {
-               gint n;
-
-               n = vf_count(vf, NULL);
-               if (ignore_list)
-                       {
-                       new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
-                       DEBUG_1("row = %d, closest is %d", row, new_row);
-                       }
-               else
-                       {
-                       if (row + 1 < n)
-                               {
-                               new_row = row + 1;
-                               }
-                       else if (row > 0)
-                               {
-                               new_row = row - 1;
-                               }
-                       }
-               vf_select_none(vf);
-               if (new_row >= 0)
-                       {
-                       fd = vf_index_get_data(vf, new_row);
-                       if (vflist_find_row(vf, fd, &iter) >= 0)
-                               {
-                               GtkTreeSelection *selection;
-
-                               selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
-                               gtk_tree_selection_select_iter(selection, &iter);
-                               vflist_move_cursor(vf, &iter);
-                               }
-                       }
-               }
-
-       fd = vf_index_get_data(vf, row);
-       if (vflist_find_row(vf, fd, &iter) >= 0)
-               {
-               GtkTreeStore *store;
-               store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
-               gtk_tree_store_remove(store, &iter);
-               }
-       list = g_list_nth(vf->list, row);
-       fd = list->data;
-
-       /* thumbnail loader check */
-       if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
-       if (vf->thumbs_count > 0) vf->thumbs_count--;
-
-       vf->list = g_list_remove(vf->list, fd);
-       file_data_unref(fd);
-
-       vf_send_update(vf);
-
-       return TRUE;
 }
 
-gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
-{
-       gint ret = FALSE;
-       gchar *buf;
-
-       if (!fd->change->source || !vf->path) return FALSE;
-
-       buf = remove_level_from_path(fd->change->source);
-
-       if (strcmp(buf, vf->path) == 0)
-               {
-               ret = vflist_maint_removed(vf, fd, ignore_list);
-               }
-
-       g_free(buf);
-
-       return ret;
-}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */