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