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