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