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