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