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