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