Use FileData locks to avoid expensive reloads with marks enabled
[geeqie.git] / src / view_file_list.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include "main.h"
14 #include "view_file_list.h"
15
16 #include "bar.h"
17 #include "cache_maint.h"
18 #include "dnd.h"
19 #include "editors.h"
20 #include "img-view.h"
21 #include "layout.h"
22 #include "layout_image.h"
23 #include "menu.h"
24 #include "metadata.h"
25 #include "thumb.h"
26 #include "utilops.h"
27 #include "ui_fileops.h"
28 #include "ui_menu.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
32
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
34
35 /* Index to tree store */
36 enum {
37         FILE_COLUMN_POINTER = 0,
38         FILE_COLUMN_VERSION,
39         FILE_COLUMN_THUMB,
40         FILE_COLUMN_FORMATTED,
41         FILE_COLUMN_NAME,
42         FILE_COLUMN_SIDECARS,
43         FILE_COLUMN_SIZE,
44         FILE_COLUMN_DATE,
45         FILE_COLUMN_EXPANDED,
46         FILE_COLUMN_COLOR,
47         FILE_COLUMN_MARKS,
48         FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
49         FILE_COLUMN_COUNT
50 };
51
52
53 /* Index to tree view */
54 enum {
55         FILE_VIEW_COLUMN_MARKS = 0,
56         FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
57         FILE_VIEW_COLUMN_THUMB,
58         FILE_VIEW_COLUMN_FORMATTED,
59         FILE_VIEW_COLUMN_SIZE,
60         FILE_VIEW_COLUMN_DATE,
61         FILE_VIEW_COLUMN_COUNT
62 };
63
64
65
66 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf, gboolean force);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
71
72
73 /*
74  *-----------------------------------------------------------------------------
75  * misc
76  *-----------------------------------------------------------------------------
77  */
78 typedef struct {
79         FileData *fd;
80         GtkTreeIter *iter;
81         gboolean found;
82         gint row;
83 } ViewFileFindRowData;
84
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
86 {
87         ViewFileFindRowData *find = data;
88         FileData *fd;
89         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
90         if (fd == find->fd)
91                 {
92                 *find->iter = *iter;
93                 find->found = TRUE;
94                 return TRUE;
95                 }
96         find->row++;
97         return FALSE;
98 }
99
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
101 {
102         GtkTreeModel *store;
103         ViewFileFindRowData data = {fd, iter, FALSE, 0};
104
105         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106         gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
107
108         if (data.found)
109                 {
110                 return data.row;
111                 }
112
113         return -1;
114 }
115
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
117 {
118         GtkTreePath *tpath;
119         GtkTreeViewColumn *column;
120
121         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122                                           &tpath, &column, NULL, NULL))
123                 {
124                 GtkTreeModel *store;
125                 GtkTreeIter row;
126                 FileData *fd;
127
128                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129                 gtk_tree_model_get_iter(store, &row, tpath);
130                 gtk_tree_path_free(tpath);
131                 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
132
133                 return fd;
134                 }
135
136         return NULL;
137 }
138
139 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
140 {
141         FileData *fd;
142         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
143
144         /* it seems that gtk_tree_store_clear may call some callbacks
145            that use the column. Set the pointer to NULL to be safe. */
146         gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
147         file_data_unref(fd);
148         return FALSE;
149 }
150
151 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
152 {
153         GtkTreeModel *store;
154         GList *files = NULL;
155
156         if (unlock_files && vf->marks_enabled)
157                 {
158                 // unlock locked files in this directory
159                 filelist_read(vf->dir_fd, &files, NULL);
160                 while (files)
161                         {
162                         FileData *fd = files->data;
163                         files = files->next;
164                         file_data_unlock(fd);
165                         file_data_unref(fd);  // undo the ref that got added in filelist_read
166                         }
167                 }
168
169         g_list_free(files);
170         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
171         gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
172         gtk_tree_store_clear(GTK_TREE_STORE(store));
173 }
174
175 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
176 {
177         GtkTreeModel *store;
178         GtkTreeIter iter;
179
180         if (vflist_find_row(vf, fd, &iter) < 0) return;
181         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
182         gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
183 }
184
185 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
186 {
187         GtkTreeModel *store;
188         GtkTreePath *tpath;
189
190         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
191
192         tpath = gtk_tree_model_get_path(store, iter);
193         gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
194         gtk_tree_path_free(tpath);
195 }
196
197
198 /*
199  *-----------------------------------------------------------------------------
200  * dnd
201  *-----------------------------------------------------------------------------
202  */
203
204 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
205                            GtkSelectionData *selection_data, guint info,
206                            guint time, gpointer data)
207 {
208         ViewFile *vf = data;
209         GList *list = NULL;
210
211         if (!VFLIST(vf)->click_fd) return;
212
213         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
214                 {
215                 list = vf_selection_get_list(vf);
216                 }
217         else
218                 {
219                 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
220                 }
221
222         if (!list) return;
223         uri_selection_data_set_uris_from_filelist(selection_data, list);
224         filelist_free(list);
225 }
226
227 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
228 {
229         ViewFile *vf = data;
230
231         vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
232
233         if (VFLIST(vf)->thumbs_enabled &&
234             VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
235                 {
236                 guint items;
237
238                 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
239                         items = vf_selection_count(vf, NULL);
240                 else
241                         items = 1;
242
243                 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
244                 }
245 }
246
247 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
248 {
249         ViewFile *vf = data;
250
251         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
252
253         if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
254                 {
255                 vf_refresh(vf);
256                 }
257 }
258
259 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
260                                       int x, int y, GtkSelectionData *selection,
261                                       guint info, guint time, gpointer data)
262 {
263         ViewFile *vf = data;
264
265         if (info == TARGET_TEXT_PLAIN) {
266                 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
267
268                 if (fd) {
269                         /* Add keywords to file */
270                         gchar *str = (gchar *) gtk_selection_data_get_text(selection);
271                         GList *kw_list = string_to_keywords_list(str);
272
273                         metadata_append_list(fd, KEYWORD_KEY, kw_list);
274                         string_list_free(kw_list);
275                         g_free(str);
276                 }
277         }
278 }
279
280 void vflist_dnd_init(ViewFile *vf)
281 {
282         gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
283                             dnd_file_drag_types, dnd_file_drag_types_count,
284                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
285         gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
286                             dnd_file_drag_types, dnd_file_drag_types_count,
287                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
288
289         g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
290                          G_CALLBACK(vflist_dnd_get), vf);
291         g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
292                          G_CALLBACK(vflist_dnd_begin), vf);
293         g_signal_connect(G_OBJECT(vf->listview), "drag_end",
294                          G_CALLBACK(vflist_dnd_end), vf);
295         g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
296                          G_CALLBACK(vflist_drag_data_received), vf);
297 }
298
299 /*
300  *-----------------------------------------------------------------------------
301  * pop-up menu
302  *-----------------------------------------------------------------------------
303  */
304
305 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
306 {
307         GList *list = g_list_append(NULL, file_data_ref(fd));
308
309         if (fd->sidecar_files)
310                 {
311                 /* check if the row is expanded */
312                 GtkTreeModel *store;
313                 GtkTreeIter iter;
314
315                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
316                 if (vflist_find_row(vf, fd, &iter) >= 0)
317                         {
318                         GtkTreePath *tpath;
319
320                         tpath = gtk_tree_model_get_path(store, &iter);
321                         if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
322                                 {
323                                 /* unexpanded - add whole group */
324                                 GList *work = fd->sidecar_files;
325                                 while (work)
326                                         {
327                                         FileData *sfd = work->data;
328                                         list = g_list_prepend(list, file_data_ref(sfd));
329                                         work = work->next;
330                                         }
331                                 }
332                         gtk_tree_path_free(tpath);
333                         }
334                 list = g_list_reverse(list);
335                 }
336
337         return list;
338 }
339
340 GList *vflist_pop_menu_file_list(ViewFile *vf)
341 {
342         if (!VFLIST(vf)->click_fd) return NULL;
343
344         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
345                 {
346                 return vf_selection_get_list(vf);
347                 }
348         return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
349 }
350
351
352 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
353 {
354         ViewFile *vf = data;
355
356         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
357                 {
358                 GList *list;
359
360                 list = vf_selection_get_list(vf);
361                 view_window_new_from_list(list);
362                 filelist_free(list);
363                 }
364         else
365                 {
366                 view_window_new(VFLIST(vf)->click_fd);
367                 }
368 }
369
370 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
371 {
372         ViewFile *vf = data;
373         GList *list;
374
375         list = vf_pop_menu_file_list(vf);
376         if (options->file_ops.enable_in_place_rename &&
377             list && !list->next && VFLIST(vf)->click_fd)
378                 {
379                 GtkTreeModel *store;
380                 GtkTreeIter iter;
381
382                 filelist_free(list);
383
384                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
385                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
386                         {
387                         GtkTreePath *tpath;
388
389                         tpath = gtk_tree_model_get_path(store, &iter);
390                         tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
391                                           FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
392                                           vflist_row_rename_cb, vf);
393                         gtk_tree_path_free(tpath);
394                         }
395                 return;
396                 }
397
398         file_util_rename(NULL, list, vf->listview);
399 }
400
401 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
402 {
403         ViewFile *vf = data;
404
405         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
406         if (vf->layout)
407                 {
408                 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
409                 }
410         else
411                 {
412                 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
413                 }
414 }
415
416 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
417 {
418         ViewFile *vf = data;
419
420         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
421         vf_refresh(vf);
422         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
423 }
424
425 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
426 {
427         ViewFile *vf = data;
428         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
429         VFLIST(vf)->click_fd = NULL;
430         vf->popup = NULL;
431 }
432
433
434 /*
435  *-----------------------------------------------------------------------------
436  * callbacks
437  *-----------------------------------------------------------------------------
438  */
439
440 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
441 {
442         ViewFile *vf = data;
443         gchar *new_path;
444
445         if (!new || !new[0]) return FALSE;
446
447         new_path = g_build_filename(vf->dir_fd->path, new, NULL);
448
449         if (strchr(new, G_DIR_SEPARATOR) != NULL)
450                 {
451                 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
452                 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
453                 g_free(text);
454                 }
455         else
456                 {
457                 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
458                 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
459                 file_util_rename_simple(fd, new_path, vf->listview);
460                 file_data_unref(fd);
461                 g_free(old_path);
462                 }
463
464         g_free(new_path);
465
466         return FALSE;
467 }
468
469 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
470 {
471         ViewFile *vf = data;
472         GtkTreeModel *store;
473         GtkTreeIter iter;
474         GtkTreePath *tpath;
475         gint cw, ch;
476
477         if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
478         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
479         tpath = gtk_tree_model_get_path(store, &iter);
480         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
481         gtk_tree_path_free(tpath);
482         *y += ch;
483         popup_menu_position_clamp(menu, x, y, 0);
484 }
485
486 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
487 {
488         ViewFile *vf = data;
489         GtkTreePath *tpath;
490
491         if (event->keyval != GDK_KEY_Menu) return FALSE;
492
493         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
494         if (tpath)
495                 {
496                 GtkTreeModel *store;
497                 GtkTreeIter iter;
498
499                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
500                 gtk_tree_model_get_iter(store, &iter, tpath);
501                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
502                 gtk_tree_path_free(tpath);
503                 }
504         else
505                 {
506                 VFLIST(vf)->click_fd = NULL;
507                 }
508
509         vf->popup = vf_pop_menu(vf);
510         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
511
512         return TRUE;
513 }
514
515 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
516 {
517         ViewFile *vf = data;
518         GtkTreePath *tpath;
519         GtkTreeIter iter;
520         FileData *fd = NULL;
521         GtkTreeViewColumn *column;
522
523         vf->clicked_mark = 0;
524
525         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
526                                           &tpath, &column, NULL, NULL))
527                 {
528                 GtkTreeModel *store;
529                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
530
531                 if (bevent->button == MOUSE_BUTTON_LEFT &&
532                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
533                         return FALSE;
534
535                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
536                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
537
538                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
539
540                 gtk_tree_model_get_iter(store, &iter, tpath);
541                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
542                 gtk_tree_path_free(tpath);
543                 }
544
545         VFLIST(vf)->click_fd = fd;
546
547         if (bevent->button == MOUSE_BUTTON_RIGHT)
548                 {
549                 vf->popup = vf_pop_menu(vf);
550                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
551                                 bevent->button, bevent->time);
552                 return TRUE;
553                 }
554
555         if (!fd) return FALSE;
556
557         if (bevent->button == MOUSE_BUTTON_MIDDLE)
558                 {
559                 if (!vflist_row_is_selected(vf, fd))
560                         {
561                         vflist_color_set(vf, fd, TRUE);
562                         }
563                 return TRUE;
564                 }
565
566
567         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
568             !(bevent->state & GDK_SHIFT_MASK ) &&
569             !(bevent->state & GDK_CONTROL_MASK ) &&
570             vflist_row_is_selected(vf, fd))
571                 {
572                 GtkTreeSelection *selection;
573
574                 gtk_widget_grab_focus(widget);
575
576
577                 /* returning FALSE and further processing of the event is needed for
578                    correct operation of the expander, to show the sidecar files.
579                    It however resets the selection of multiple files. With this condition
580                    it should work for both cases */
581                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
582                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
583                 }
584
585         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
586                 {
587                 if (vf->layout) layout_image_full_screen_start(vf->layout);
588                 }
589
590         return FALSE;
591 }
592
593 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
594 {
595         ViewFile *vf = data;
596         GtkTreePath *tpath;
597         GtkTreeIter iter;
598         FileData *fd = NULL;
599
600         if (bevent->button == MOUSE_BUTTON_MIDDLE)
601                 {
602                 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
603                 }
604
605         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
606                 {
607                 return TRUE;
608                 }
609
610         if ((bevent->x != 0 || bevent->y != 0) &&
611             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
612                                           &tpath, NULL, NULL, NULL))
613                 {
614                 GtkTreeModel *store;
615
616                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
617                 gtk_tree_model_get_iter(store, &iter, tpath);
618                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
619                 gtk_tree_path_free(tpath);
620                 }
621
622         if (bevent->button == MOUSE_BUTTON_MIDDLE)
623                 {
624                 if (fd && VFLIST(vf)->click_fd == fd)
625                         {
626                         GtkTreeSelection *selection;
627
628                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
629                         if (vflist_row_is_selected(vf, fd))
630                                 {
631                                 gtk_tree_selection_unselect_iter(selection, &iter);
632                                 }
633                         else
634                                 {
635                                 gtk_tree_selection_select_iter(selection, &iter);
636                                 }
637                         }
638                 return TRUE;
639                 }
640
641         if (fd && VFLIST(vf)->click_fd == fd &&
642             !(bevent->state & GDK_SHIFT_MASK ) &&
643             !(bevent->state & GDK_CONTROL_MASK ) &&
644             vflist_row_is_selected(vf, fd))
645                 {
646                 GtkTreeSelection *selection;
647
648                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
649                 gtk_tree_selection_unselect_all(selection);
650                 gtk_tree_selection_select_iter(selection, &iter);
651                 vflist_move_cursor(vf, &iter);
652                 }
653
654         return FALSE;
655 }
656
657 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
658 {
659         FileData *read_ahead_fd = NULL;
660         gint row;
661         FileData *cur_fd;
662
663         if (!sel_fd) return;
664
665         cur_fd = layout_image_get_fd(vf->layout);
666         if (sel_fd == cur_fd) return; /* no change */
667
668         row = g_list_index(vf->list, sel_fd);
669         // FIXME sidecar data
670
671         if (sel_fd && options->image.enable_read_ahead && row >= 0)
672                 {
673                 if (row > g_list_index(vf->list, cur_fd) &&
674                     (guint) (row + 1) < vf_count(vf, NULL))
675                         {
676                         read_ahead_fd = vf_index_get_data(vf, row + 1);
677                         }
678                 else if (row > 0)
679                         {
680                         read_ahead_fd = vf_index_get_data(vf, row - 1);
681                         }
682                 }
683
684         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
685 }
686
687 static gboolean vflist_select_idle_cb(gpointer data)
688 {
689         ViewFile *vf = data;
690
691         if (!vf->layout)
692                 {
693                 VFLIST(vf)->select_idle_id = 0;
694                 return FALSE;
695                 }
696
697         vf_send_update(vf);
698
699         if (VFLIST(vf)->select_fd)
700                 {
701                 vflist_select_image(vf, VFLIST(vf)->select_fd);
702                 VFLIST(vf)->select_fd = NULL;
703                 }
704
705         VFLIST(vf)->select_idle_id = 0;
706         return FALSE;
707 }
708
709 static void vflist_select_idle_cancel(ViewFile *vf)
710 {
711         if (VFLIST(vf)->select_idle_id)
712                 {
713                 g_source_remove(VFLIST(vf)->select_idle_id);
714                 VFLIST(vf)->select_idle_id = 0;
715                 }
716 }
717
718 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
719                                  gboolean path_currently_selected, gpointer data)
720 {
721         ViewFile *vf = data;
722         GtkTreeIter iter;
723
724         if (!path_currently_selected &&
725             gtk_tree_model_get_iter(store, &iter, tpath))
726                 {
727                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
728                 }
729         else
730                 {
731                 VFLIST(vf)->select_fd = NULL;
732                 }
733
734         if (vf->layout &&
735             !VFLIST(vf)->select_idle_id)
736                 {
737                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
738                 }
739
740         return TRUE;
741 }
742
743 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
744 {
745         ViewFile *vf = data;
746         vflist_set_expanded(vf, iter, TRUE);
747 }
748
749 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
750 {
751         ViewFile *vf = data;
752         vflist_set_expanded(vf, iter, FALSE);
753 }
754
755 /*
756  *-----------------------------------------------------------------------------
757  * misc
758  *-----------------------------------------------------------------------------
759  */
760
761
762 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
763  {
764         gboolean multiline = vflist_is_multiline(vf);
765         gchar *text;
766
767         if (multiline)
768                 {
769                 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
770                 }
771         else
772                 {
773                 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
774                 }
775         return text;
776 }
777
778 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
779 {
780         GtkTreeStore *store;
781         gchar *name;
782         gchar *sidecars;
783         gchar *size;
784         gchar *time;
785         gchar *formatted;
786
787         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
788
789         gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
790                                         FILE_COLUMN_NAME, &name,
791                                         FILE_COLUMN_SIDECARS, &sidecars,
792                                         FILE_COLUMN_SIZE, &size,
793                                         FILE_COLUMN_DATE, &time,
794                                         -1);
795         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
796
797         gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
798                                         FILE_COLUMN_EXPANDED, expanded,
799                                         -1);
800         g_free(time);
801         g_free(size);
802         g_free(sidecars);
803         g_free(name);
804         g_free(formatted);
805 }
806
807 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
808 {
809         gchar *size;
810         gchar *sidecars = NULL;
811         gchar *name;
812         const gchar *time = text_from_time(fd->date);
813         gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
814         const gchar *disabled_grouping;
815         gchar *formatted;
816         gboolean expanded = FALSE;
817
818         if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
819                 {
820                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
821                 }
822
823         sidecars = file_data_sc_list_to_string(fd);
824
825         disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
826         name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
827         size = text_from_size(fd->size);
828
829         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
830
831         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
832                                         FILE_COLUMN_VERSION, fd->version,
833                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
834                                         FILE_COLUMN_FORMATTED, formatted,
835                                         FILE_COLUMN_SIDECARS, sidecars,
836                                         FILE_COLUMN_NAME, name,
837                                         FILE_COLUMN_SIZE, size,
838                                         FILE_COLUMN_DATE, time,
839 #define STORE_SET_IS_SLOW 1
840 #if STORE_SET_IS_SLOW
841 /* this is 3x faster on a directory with 20000 files */
842                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
843                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
844                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
845                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
846                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
847                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
848 #if FILEDATA_MARKS_SIZE != 6
849 #error this needs to be updated
850 #endif
851 #endif
852                                         FILE_COLUMN_COLOR, FALSE, -1);
853
854 #if !STORE_SET_IS_SLOW
855         {
856         gint i;
857         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
858                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
859         }
860 #endif
861         g_free(size);
862         g_free(sidecars);
863         g_free(name);
864         g_free(formatted);
865 }
866
867 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
868 {
869         GList *work;
870         GtkTreeIter iter;
871         gboolean valid;
872         gint num_ordered = 0;
873         gint num_prepended = 0;
874
875         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
876
877         work = list;
878         while (work)
879                 {
880                 gint match;
881                 FileData *fd = work->data;
882                 gboolean done = FALSE;
883
884                 while (!done)
885                         {
886                         FileData *old_fd = NULL;
887                         gint old_version = 0;
888
889                         if (valid)
890                                 {
891                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
892                                                    FILE_COLUMN_POINTER, &old_fd,
893                                                    FILE_COLUMN_VERSION, &old_version,
894                                                    -1);
895
896                                 if (fd == old_fd)
897                                         {
898                                         match = 0;
899                                         }
900                                 else
901                                         {
902                                         if (parent_iter)
903                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
904                                         else
905                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
906
907                                         if (match == 0) g_warning("multiple fd for the same path");
908                                         }
909
910                                 }
911                         else
912                                 {
913                                 match = -1;
914                                 }
915
916                         if (match < 0)
917                                 {
918                                 GtkTreeIter new;
919
920                                 if (valid)
921                                         {
922                                         num_ordered++;
923                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
924                                         }
925                                 else
926                                         {
927                                         /*
928                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
929                                             and it seems to be much faster to add new entries to the beginning and reorder later
930                                         */
931                                         num_prepended++;
932                                         gtk_tree_store_prepend(store, &new, parent_iter);
933                                         }
934
935                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
936                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
937
938                                 if (g_list_find(selected, fd))
939                                         {
940                                         /* renamed files - the same fd appears at different position - select it again*/
941                                         GtkTreeSelection *selection;
942                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
943                                         gtk_tree_selection_select_iter(selection, &new);
944                                         }
945
946                                 done = TRUE;
947                                 }
948                         else if (match > 0)
949                                 {
950                                 file_data_unref(old_fd);
951                                 valid = gtk_tree_store_remove(store, &iter);
952                                 }
953                         else
954                                 {
955                                 num_ordered++;
956                                 if (fd->version != old_version || force)
957                                         {
958                                         vflist_setup_iter(vf, store, &iter, fd);
959                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
960                                         }
961
962                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
963
964                                 done = TRUE;
965                                 }
966                         }
967                 work = work->next;
968                 }
969
970         while (valid)
971                 {
972                 FileData *old_fd;
973                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
974                 file_data_unref(old_fd);
975
976                 valid = gtk_tree_store_remove(store, &iter);
977                 }
978
979         /* move the prepended entries to the correct position */
980         if (num_prepended)
981                 {
982                 gint i;
983                 gint num_total = num_prepended + num_ordered;
984                 gint *new_order = g_malloc(num_total * sizeof(gint));
985
986                 for (i = 0; i < num_total; i++)
987                         {
988                         if (i < num_ordered)
989                                 new_order[i] = num_prepended + i;
990                         else
991                                 new_order[i] = num_total - 1 - i;
992                         }
993                 gtk_tree_store_reorder(store, parent_iter, new_order);
994
995                 g_free(new_order);
996                 }
997 }
998
999 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1000 {
1001         gint i;
1002         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1003         gint *new_order;
1004         GtkTreeStore *store;
1005         GList *work;
1006
1007         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1008         if (!vf->list) return;
1009
1010         work = vf->list;
1011         i = 0;
1012         while (work)
1013                 {
1014                 FileData *fd = work->data;
1015                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1016                 i++;
1017                 work = work->next;
1018                 }
1019
1020         vf->sort_method = type;
1021         vf->sort_ascend = ascend;
1022
1023         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1024
1025         new_order = g_malloc(i * sizeof(gint));
1026
1027         work = vf->list;
1028         i = 0;
1029         while (work)
1030                 {
1031                 FileData *fd = work->data;
1032                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1033                 i++;
1034                 work = work->next;
1035                 }
1036
1037         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1038         gtk_tree_store_reorder(store, NULL, new_order);
1039
1040         g_free(new_order);
1041         g_hash_table_destroy(fd_idx_hash);
1042 }
1043
1044 /*
1045  *-----------------------------------------------------------------------------
1046  * thumb updates
1047  *-----------------------------------------------------------------------------
1048  */
1049
1050
1051 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1052 {
1053         GList *work = list;
1054         while (work)
1055                 {
1056                 FileData *fd = work->data;
1057                 work = work->next;
1058
1059                 if (fd->thumb_pixbuf) (*done)++;
1060
1061                 if (fd->sidecar_files)
1062                         {
1063                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
1064                         }
1065                 (*count)++;
1066                 }
1067 }
1068
1069 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1070 {
1071         GtkTreeStore *store;
1072         GtkTreeIter iter;
1073
1074         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1075
1076         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1077         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1078 }
1079
1080 FileData *vflist_thumb_next_fd(ViewFile *vf)
1081 {
1082         GtkTreePath *tpath;
1083         FileData *fd = NULL;
1084
1085         /* first check the visible files */
1086
1087         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1088                 {
1089                 GtkTreeModel *store;
1090                 GtkTreeIter iter;
1091                 gboolean valid = TRUE;
1092
1093                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1094                 gtk_tree_model_get_iter(store, &iter, tpath);
1095                 gtk_tree_path_free(tpath);
1096
1097                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1098                         {
1099                         FileData *nfd;
1100
1101                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1102
1103                         if (!nfd->thumb_pixbuf) fd = nfd;
1104
1105                         valid = gtk_tree_model_iter_next(store, &iter);
1106                         }
1107                 }
1108
1109         /* then find first undone */
1110
1111         if (!fd)
1112                 {
1113                 GList *work = vf->list;
1114                 while (work && !fd)
1115                         {
1116                         FileData *fd_p = work->data;
1117                         if (!fd_p->thumb_pixbuf)
1118                                 fd = fd_p;
1119                         else
1120                                 {
1121                                 GList *work2 = fd_p->sidecar_files;
1122
1123                                 while (work2 && !fd)
1124                                         {
1125                                         fd_p = work2->data;
1126                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1127                                         work2 = work2->next;
1128                                         }
1129                                 }
1130                         work = work->next;
1131                         }
1132                 }
1133
1134         return fd;
1135 }
1136
1137
1138 void vflist_thumb_reset_all(ViewFile *vf)
1139 {
1140         GList *work = vf->list;
1141         while (work)
1142                 {
1143                 FileData *fd = work->data;
1144                 if (fd->thumb_pixbuf)
1145                         {
1146                         g_object_unref(fd->thumb_pixbuf);
1147                         fd->thumb_pixbuf = NULL;
1148                         }
1149                 work = work->next;
1150                 }
1151 }
1152
1153 /*
1154  *-----------------------------------------------------------------------------
1155  * row stuff
1156  *-----------------------------------------------------------------------------
1157  */
1158
1159 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1160 {
1161         return g_list_nth_data(vf->list, row);
1162 }
1163
1164 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1165 {
1166         gint p = 0;
1167         GList *work, *work2;
1168
1169         work = vf->list;
1170         while (work)
1171                 {
1172                 FileData *list_fd = work->data;
1173                 if (list_fd == fd) return p;
1174
1175                 work2 = list_fd->sidecar_files;
1176                 while (work2)
1177                         {
1178                         /* FIXME: return the same index also for sidecars
1179                            it is sufficient for next/prev navigation but it should be rewritten
1180                            without using indexes at all
1181                         */
1182                         FileData *sidecar_fd = work2->data;
1183                         if (sidecar_fd == fd) return p;
1184                         work2 = work2->next;
1185                         }
1186
1187                 work = work->next;
1188                 p++;
1189                 }
1190
1191         return -1;
1192 }
1193
1194 guint vflist_count(ViewFile *vf, gint64 *bytes)
1195 {
1196         if (bytes)
1197                 {
1198                 gint64 b = 0;
1199                 GList *work;
1200
1201                 work = vf->list;
1202                 while (work)
1203                         {
1204                         FileData *fd = work->data;
1205                         work = work->next;
1206                         b += fd->size;
1207                         }
1208
1209                 *bytes = b;
1210                 }
1211
1212         return g_list_length(vf->list);
1213 }
1214
1215 GList *vflist_get_list(ViewFile *vf)
1216 {
1217         GList *list = NULL;
1218         GList *work;
1219
1220         work = vf->list;
1221         while (work)
1222                 {
1223                 FileData *fd = work->data;
1224                 work = work->next;
1225
1226                 list = g_list_prepend(list, file_data_ref(fd));
1227                 }
1228
1229         return g_list_reverse(list);
1230 }
1231
1232 /*
1233  *-----------------------------------------------------------------------------
1234  * selections
1235  *-----------------------------------------------------------------------------
1236  */
1237
1238 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1239 {
1240         GtkTreeModel *store;
1241         GtkTreeSelection *selection;
1242         GList *slist;
1243         GList *work;
1244         gboolean found = FALSE;
1245
1246         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1247         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1248         work = slist;
1249         while (!found && work)
1250                 {
1251                 GtkTreePath *tpath = work->data;
1252                 FileData *fd_n;
1253                 GtkTreeIter iter;
1254
1255                 gtk_tree_model_get_iter(store, &iter, tpath);
1256                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1257                 if (fd_n == fd) found = TRUE;
1258                 work = work->next;
1259                 }
1260         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1261         g_list_free(slist);
1262
1263         return found;
1264 }
1265
1266 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1267 {
1268         FileData *fd;
1269
1270         fd = vf_index_get_data(vf, row);
1271         return vflist_row_is_selected(vf, fd);
1272 }
1273
1274 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1275 {
1276         GtkTreeModel *store;
1277         GtkTreeSelection *selection;
1278         GList *slist;
1279         guint count;
1280
1281         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1282         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1283
1284         if (bytes)
1285                 {
1286                 gint64 b = 0;
1287                 GList *work;
1288
1289                 work = slist;
1290                 while (work)
1291                         {
1292                         GtkTreePath *tpath = work->data;
1293                         GtkTreeIter iter;
1294                         FileData *fd;
1295
1296                         gtk_tree_model_get_iter(store, &iter, tpath);
1297                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1298                         b += fd->size;
1299
1300                         work = work->next;
1301                         }
1302
1303                 *bytes = b;
1304                 }
1305
1306         count = g_list_length(slist);
1307         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1308         g_list_free(slist);
1309
1310         return count;
1311 }
1312
1313 GList *vflist_selection_get_list(ViewFile *vf)
1314 {
1315         GtkTreeModel *store;
1316         GtkTreeSelection *selection;
1317         GList *slist;
1318         GList *list = NULL;
1319         GList *work;
1320
1321         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1322         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1323         work = slist;
1324         while (work)
1325                 {
1326                 GtkTreePath *tpath = work->data;
1327                 FileData *fd;
1328                 GtkTreeIter iter;
1329
1330                 gtk_tree_model_get_iter(store, &iter, tpath);
1331                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1332
1333                 list = g_list_prepend(list, file_data_ref(fd));
1334
1335                 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1336                         {
1337                         /* unexpanded - add whole group */
1338                         GList *work2 = fd->sidecar_files;
1339                         while (work2)
1340                                 {
1341                                 FileData *sfd = work2->data;
1342                                 list = g_list_prepend(list, file_data_ref(sfd));
1343                                 work2 = work2->next;
1344                                 }
1345                         }
1346
1347                 work = work->next;
1348                 }
1349         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1350         g_list_free(slist);
1351
1352         return g_list_reverse(list);
1353 }
1354
1355 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1356 {
1357         GtkTreeModel *store;
1358         GtkTreeSelection *selection;
1359         GList *slist;
1360         GList *list = NULL;
1361         GList *work;
1362
1363         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1364         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1365         work = slist;
1366         while (work)
1367                 {
1368                 GtkTreePath *tpath = work->data;
1369                 FileData *fd;
1370                 GtkTreeIter iter;
1371
1372                 gtk_tree_model_get_iter(store, &iter, tpath);
1373                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1374
1375                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1376
1377                 work = work->next;
1378                 }
1379         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1380         g_list_free(slist);
1381
1382         return g_list_reverse(list);
1383 }
1384
1385 void vflist_select_all(ViewFile *vf)
1386 {
1387         GtkTreeSelection *selection;
1388
1389         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1390         gtk_tree_selection_select_all(selection);
1391
1392         VFLIST(vf)->select_fd = NULL;
1393 }
1394
1395 void vflist_select_none(ViewFile *vf)
1396 {
1397         GtkTreeSelection *selection;
1398
1399         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1400         gtk_tree_selection_unselect_all(selection);
1401 }
1402
1403 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1404 {
1405         GtkTreePath *tpath;
1406         gboolean result;
1407
1408         tpath = gtk_tree_model_get_path(store, iter);
1409         result = gtk_tree_path_prev(tpath);
1410         if (result)
1411                 gtk_tree_model_get_iter(store, iter, tpath);
1412
1413         gtk_tree_path_free(tpath);
1414
1415         return result;
1416 }
1417
1418 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1419 {
1420         if (!gtk_tree_model_get_iter_first(store, iter))
1421                 return FALSE;
1422
1423         while (TRUE)
1424                 {
1425                 GtkTreeIter next = *iter;
1426
1427                 if (gtk_tree_model_iter_next(store, &next))
1428                         *iter = next;
1429                 else
1430                         break;
1431                 }
1432
1433         return TRUE;
1434 }
1435
1436 void vflist_select_invert(ViewFile *vf)
1437 {
1438         GtkTreeIter iter;
1439         GtkTreeSelection *selection;
1440         GtkTreeModel *store;
1441         gboolean valid;
1442
1443         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1444         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1445
1446         /* Backward iteration prevents scrolling to the end of the list,
1447          * it scrolls to the first selected row instead. */
1448         valid = tree_model_get_iter_last(store, &iter);
1449
1450         while (valid)
1451                 {
1452                 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1453
1454                 if (selected)
1455                         gtk_tree_selection_unselect_iter(selection, &iter);
1456                 else
1457                         gtk_tree_selection_select_iter(selection, &iter);
1458
1459                 valid = tree_model_iter_prev(store, &iter);
1460                 }
1461 }
1462
1463 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1464 {
1465         GtkTreeIter iter;
1466
1467         if (vflist_find_row(vf, fd, &iter) < 0) return;
1468
1469         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1470
1471         if (!vflist_row_is_selected(vf, fd))
1472                 {
1473                 GtkTreeSelection *selection;
1474                 GtkTreeModel *store;
1475                 GtkTreePath *tpath;
1476
1477                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1478                 gtk_tree_selection_unselect_all(selection);
1479                 gtk_tree_selection_select_iter(selection, &iter);
1480                 vflist_move_cursor(vf, &iter);
1481
1482                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1483                 tpath = gtk_tree_model_get_path(store, &iter);
1484                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1485                 gtk_tree_path_free(tpath);
1486                 }
1487 }
1488
1489 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1490 {
1491         GList *work;
1492         FileData *fd = NULL;
1493
1494         if (sel_fd->parent) sel_fd = sel_fd->parent;
1495         work = vf->list;
1496
1497         while (work)
1498                 {
1499                 gint match;
1500                 fd = work->data;
1501                 work = work->next;
1502
1503                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1504
1505                 if (match >= 0) break;
1506                 }
1507
1508         if (fd) vflist_select_by_fd(vf, fd);
1509
1510 }
1511
1512 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1513 {
1514         GtkTreeModel *store;
1515         GtkTreeIter iter;
1516         GtkTreeSelection *selection;
1517         gboolean valid;
1518         gint n = mark - 1;
1519
1520         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1521
1522         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1523         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1524
1525         valid = gtk_tree_model_get_iter_first(store, &iter);
1526         while (valid)
1527                 {
1528                 FileData *fd;
1529                 gboolean mark_val, selected;
1530                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1531
1532                 mark_val = file_data_get_mark(fd, n);
1533                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1534
1535                 switch (mode)
1536                         {
1537                         case MTS_MODE_SET: selected = mark_val;
1538                                 break;
1539                         case MTS_MODE_OR: selected = mark_val || selected;
1540                                 break;
1541                         case MTS_MODE_AND: selected = mark_val && selected;
1542                                 break;
1543                         case MTS_MODE_MINUS: selected = !mark_val && selected;
1544                                 break;
1545                         }
1546
1547                 if (selected)
1548                         gtk_tree_selection_select_iter(selection, &iter);
1549                 else
1550                         gtk_tree_selection_unselect_iter(selection, &iter);
1551
1552                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1553                 }
1554 }
1555
1556 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1557 {
1558         GtkTreeModel *store;
1559         GtkTreeSelection *selection;
1560         GList *slist;
1561         GList *work;
1562         gint n = mark - 1;
1563
1564         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1565
1566         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1567         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1568         work = slist;
1569         while (work)
1570                 {
1571                 GtkTreePath *tpath = work->data;
1572                 FileData *fd;
1573                 GtkTreeIter iter;
1574
1575                 gtk_tree_model_get_iter(store, &iter, tpath);
1576                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1577
1578                 /* the change has a very limited range and the standard notification would trigger
1579                    complete re-read of the directory - try to do only minimal update instead */
1580                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1581
1582                 switch (mode)
1583                         {
1584                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1585                                 break;
1586                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1587                                 break;
1588                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1589                                 break;
1590                         }
1591
1592                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1593                         {
1594                         vf_refresh_idle(vf);
1595                         }
1596                 else
1597                         {
1598                         /* mark functions can have various side effects - update all columns to be sure */
1599                         vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1600                         /* mark functions can change sidecars too */
1601                         vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1602                         }
1603
1604
1605                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1606
1607                 work = work->next;
1608                 }
1609         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1610         g_list_free(slist);
1611 }
1612
1613 /*
1614  *-----------------------------------------------------------------------------
1615  * core (population)
1616  *-----------------------------------------------------------------------------
1617  */
1618
1619 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1620 {
1621         GtkTreeViewColumn *column;
1622         GtkCellRenderer *cell;
1623         GList *list;
1624
1625         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1626         if (!column) return;
1627
1628         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1629
1630         list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1631         if (!list) return;
1632         cell = list->data;
1633         g_list_free(list);
1634
1635         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1636         gtk_tree_view_column_set_visible(column, thumb);
1637
1638         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1639         if (!column) return;
1640         gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1641
1642         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1643         if (!column) return;
1644         gtk_tree_view_column_set_visible(column, !multiline);
1645
1646         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1647         if (!column) return;
1648         gtk_tree_view_column_set_visible(column, !multiline);
1649 }
1650
1651 static gboolean vflist_is_multiline(ViewFile *vf)
1652 {
1653         return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1654 }
1655
1656
1657 static void vflist_populate_view(ViewFile *vf, gboolean force)
1658 {
1659         GtkTreeStore *store;
1660         GList *selected;
1661
1662         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1663
1664         vf_thumb_stop(vf);
1665
1666         if (!vf->list)
1667                 {
1668                 vflist_store_clear(vf, FALSE);
1669                 vf_send_update(vf);
1670                 return;
1671                 }
1672
1673         vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1674
1675         selected = vflist_selection_get_list(vf);
1676
1677         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1678
1679         if (selected && vflist_selection_count(vf, NULL) == 0)
1680                 {
1681                 /* all selected files disappeared */
1682                 vflist_select_closest(vf, selected->data);
1683                 }
1684
1685         filelist_free(selected);
1686
1687         vf_send_update(vf);
1688         vf_thumb_update(vf);
1689 }
1690
1691 gboolean vflist_refresh(ViewFile *vf)
1692 {
1693         GList *old_list;
1694         gboolean ret = TRUE;
1695
1696         old_list = vf->list;
1697         vf->list = NULL;
1698
1699         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1700         if (vf->dir_fd)
1701                 {
1702                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1703
1704                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1705
1706                 if (vf->marks_enabled)
1707                         {
1708                         // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1709                         // each time a mark is changed.
1710                         file_data_lock_list(vf->list);
1711                         }
1712                 else
1713                         {
1714                         // FIXME: only do this when needed (aka when we just switched from
1715                         // FIXME: marks-enabled to marks-disabled)
1716                         file_data_unlock_list(vf->list);
1717                         }
1718
1719                 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1720                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1721
1722                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1723                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1724                 }
1725
1726         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1727
1728         vflist_populate_view(vf, FALSE);
1729
1730         DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1731
1732         filelist_free(old_list);
1733         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1734
1735         return ret;
1736 }
1737
1738
1739
1740 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1741
1742 #define CELL_HEIGHT_OVERRIDE 512
1743
1744 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1745 {
1746         GParamSpec *spec;
1747
1748         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1749         if (spec && G_IS_PARAM_SPEC_INT(spec))
1750                 {
1751                 GParamSpecInt *spec_int;
1752
1753                 spec_int = G_PARAM_SPEC_INT(spec);
1754                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1755                 }
1756 }
1757
1758 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1759 {
1760         static GdkColor color;
1761         static GtkWidget *done = NULL;
1762
1763         if (done != widget)
1764                 {
1765                 GtkStyle *style;
1766
1767                 style = gtk_widget_get_style(widget);
1768                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1769                 shift_color(&color, -1, 0);
1770                 done = widget;
1771                 }
1772
1773         return &color;
1774 }
1775
1776 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1777                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1778 {
1779         ViewFile *vf = data;
1780         gboolean set;
1781
1782         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1783         g_object_set(G_OBJECT(cell),
1784                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1785                      "cell-background-set", set, NULL);
1786 }
1787
1788 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1789 {
1790         GtkTreeViewColumn *column;
1791         GtkCellRenderer *renderer;
1792
1793         column = gtk_tree_view_column_new();
1794         gtk_tree_view_column_set_title(column, title);
1795         gtk_tree_view_column_set_min_width(column, 4);
1796
1797         if (!image)
1798                 {
1799                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1800                 renderer = gtk_cell_renderer_text_new();
1801                 if (right_justify)
1802                         {
1803                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1804                         }
1805                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1806                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1807                 if (expand)
1808                         gtk_tree_view_column_set_expand(column, TRUE);
1809                 }
1810         else
1811                 {
1812                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1813                 renderer = gtk_cell_renderer_pixbuf_new();
1814                 cell_renderer_height_override(renderer);
1815                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1816                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1817                 }
1818
1819         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1820         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1821         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1822
1823         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1824 }
1825
1826 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1827 {
1828         ViewFile *vf = data;
1829         GtkTreeStore *store;
1830         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1831         GtkTreeIter iter;
1832         FileData *fd;
1833         gboolean marked;
1834         guint col_idx;
1835
1836         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1837         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1838                 return;
1839
1840         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1841
1842         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1843
1844         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1845         marked = !marked;
1846
1847         /* the change has a very limited range and the standard notification would trigger
1848            complete re-read of the directory - try to do only minimal update instead */
1849         file_data_unregister_notify_func(vf_notify_cb, vf);
1850         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1851         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1852                 {
1853                 vf_refresh_idle(vf);
1854                 }
1855         else
1856                 {
1857                 /* mark functions can have various side effects - update all columns to be sure */
1858                 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1859                 /* mark functions can change sidecars too */
1860                 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1861                 }
1862         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1863
1864         gtk_tree_path_free(path);
1865 }
1866
1867 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1868 {
1869         GtkTreeViewColumn *column;
1870         GtkCellRenderer *renderer;
1871
1872         renderer = gtk_cell_renderer_toggle_new();
1873         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1874
1875         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1876         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1877         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1878
1879         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1880         gtk_tree_view_column_set_fixed_width(column, 22);
1881         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1882
1883
1884         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1885 }
1886
1887 /*
1888  *-----------------------------------------------------------------------------
1889  * base
1890  *-----------------------------------------------------------------------------
1891  */
1892
1893 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1894 {
1895         gboolean ret;
1896         if (!dir_fd) return FALSE;
1897         if (vf->dir_fd == dir_fd) return TRUE;
1898
1899         file_data_unref(vf->dir_fd);
1900         vf->dir_fd = file_data_ref(dir_fd);
1901
1902         /* force complete reload */
1903         vflist_store_clear(vf, TRUE);
1904
1905         filelist_free(vf->list);
1906         vf->list = NULL;
1907
1908         ret = vf_refresh(vf);
1909         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1910         return ret;
1911 }
1912
1913 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1914 {
1915         ViewFile *vf = data;
1916
1917         file_data_unregister_notify_func(vf_notify_cb, vf);
1918
1919         vflist_select_idle_cancel(vf);
1920         vf_refresh_idle_cancel(vf);
1921         vf_thumb_stop(vf);
1922
1923         filelist_free(vf->list);
1924 }
1925
1926 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1927 {
1928         GtkTreeStore *store;
1929         GtkTreeSelection *selection;
1930         GType flist_types[FILE_COLUMN_COUNT];
1931         gint i;
1932         gint column;
1933
1934         vf->info = g_new0(ViewFileInfoList, 1);
1935
1936         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1937         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1938         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1939         flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1940         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1941         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1942         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1943         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1944         flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1945         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1946         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1947                 flist_types[i] = G_TYPE_BOOLEAN;
1948
1949         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1950
1951         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1952         g_object_unref(store);
1953
1954         g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1955                          G_CALLBACK(vflist_expand_cb), vf);
1956
1957         g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1958                          G_CALLBACK(vflist_collapse_cb), vf);
1959
1960         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1961         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1962         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1963
1964         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1965         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1966
1967         column = 0;
1968
1969         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1970                 {
1971                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1972                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1973                 column++;
1974                 }
1975
1976         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1977         g_assert(column == FILE_VIEW_COLUMN_THUMB);
1978         column++;
1979
1980         vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1981         g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1982         column++;
1983
1984         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1985         g_assert(column == FILE_VIEW_COLUMN_SIZE);
1986         column++;
1987
1988         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1989         g_assert(column == FILE_VIEW_COLUMN_DATE);
1990         column++;
1991
1992         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1993         return vf;
1994 }
1995
1996 void vflist_thumb_set(ViewFile *vf, gboolean enable)
1997 {
1998         if (VFLIST(vf)->thumbs_enabled == enable) return;
1999
2000         VFLIST(vf)->thumbs_enabled = enable;
2001
2002         /* vflist_populate_view is better than vf_refresh:
2003            - no need to re-read the directory
2004            - force update because the formatted string has changed
2005         */
2006         if (vf->layout)
2007                 {
2008                 vflist_populate_view(vf, TRUE);
2009                 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2010                 }
2011 }
2012
2013 void vflist_marks_set(ViewFile *vf, gboolean enable)
2014 {
2015         GList *columns, *work;
2016
2017         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2018
2019         work = columns;
2020         while (work)
2021                 {
2022                 GtkTreeViewColumn *column = work->data;
2023                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2024                 work = work->next;
2025
2026                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2027                         gtk_tree_view_column_set_visible(column, enable);
2028                 }
2029
2030         if (enable)
2031                 {
2032                 // Previously disabled, which means that vf->list is complete
2033                 file_data_lock_list(vf->list);
2034                 }
2035         else
2036                 {
2037                 // Previously enabled, which means that vf->list is incomplete
2038                 }
2039
2040         g_list_free(columns);
2041 }
2042
2043 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */