fixed updating of selection after deleting the last image from the list
[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 1
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         FileData *fd = NULL;
1522         
1523         if (sel_fd->parent) sel_fd = sel_fd->parent;
1524         work = vf->list;
1525         
1526         while (work)
1527                 {
1528                 gint match;
1529                 fd = work->data;
1530                 work = work->next;
1531
1532                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1533                 
1534                 if (match >= 0) break;
1535                 }
1536
1537         if (fd) vflist_select_by_fd(vf, fd);
1538
1539 }
1540
1541 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1542 {
1543         GtkTreeModel *store;
1544         GtkTreeIter iter;
1545         GtkTreeSelection *selection;
1546         gint valid;
1547         gint n = mark - 1;
1548
1549         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1550
1551         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1552         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1553
1554         valid = gtk_tree_model_get_iter_first(store, &iter);
1555         while (valid)
1556                 {
1557                 FileData *fd;
1558                 gboolean mark_val, selected;
1559                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1560
1561                 mark_val = file_data_get_mark(fd, n);
1562                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1563
1564                 switch (mode)
1565                         {
1566                         case MTS_MODE_SET: selected = mark_val;
1567                                 break;
1568                         case MTS_MODE_OR: selected = mark_val | selected;
1569                                 break;
1570                         case MTS_MODE_AND: selected = mark_val & selected;
1571                                 break;
1572                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1573                                 break;
1574                         }
1575
1576                 if (selected)
1577                         gtk_tree_selection_select_iter(selection, &iter);
1578                 else
1579                         gtk_tree_selection_unselect_iter(selection, &iter);
1580
1581                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1582                 }
1583 }
1584
1585 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1586 {
1587         GtkTreeModel *store;
1588         GtkTreeSelection *selection;
1589         GList *slist;
1590         GList *work;
1591         gint n = mark - 1;
1592
1593         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1594
1595         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1596         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1597         work = slist;
1598         while (work)
1599                 {
1600                 GtkTreePath *tpath = work->data;
1601                 FileData *fd;
1602                 GtkTreeIter iter;
1603
1604                 gtk_tree_model_get_iter(store, &iter, tpath);
1605                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1606
1607                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1608
1609                 switch (mode)
1610                         {
1611                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1612                                 break;
1613                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1614                                 break;
1615                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1616                                 break;
1617                         }
1618                 
1619                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1620                         {
1621                         vf_refresh_idle(vf);
1622                         }
1623
1624                 
1625                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1626
1627                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1628
1629                 work = work->next;
1630                 }
1631         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1632         g_list_free(slist);
1633 }
1634
1635 /*
1636  *-----------------------------------------------------------------------------
1637  * core (population)
1638  *-----------------------------------------------------------------------------
1639  */
1640
1641 static void vflist_listview_set_columns(GtkWidget *listview, gint thumb)
1642 {
1643         GtkTreeViewColumn *column;
1644         GtkCellRenderer *cell;
1645         GList *list;
1646         gboolean multiline;
1647
1648         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1649         if (!column) return;
1650
1651         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1652
1653         list = gtk_tree_view_column_get_cell_renderers(column);
1654         if (!list) return;
1655         cell = list->data;
1656         g_list_free(list);
1657
1658         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1659         gtk_tree_view_column_set_visible(column, thumb);
1660
1661         multiline = (thumb && options->thumbnails.max_height >= 48);
1662         
1663         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1664         if (!column) return;
1665         gtk_tree_view_column_set_visible(column, multiline);
1666         if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1667
1668         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1669         if (!column) return;
1670         gtk_tree_view_column_set_visible(column, !multiline);
1671         if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1672
1673         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1674         if (!column) return;
1675         gtk_tree_view_column_set_visible(column, !multiline);
1676
1677         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1678         if (!column) return;
1679         gtk_tree_view_column_set_visible(column, !multiline);
1680
1681         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1682 }
1683
1684 static void vflist_populate_view(ViewFile *vf)
1685 {
1686         GtkTreeStore *store;
1687         gint thumbs;
1688         GList *selected;
1689
1690         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1691         thumbs = VFLIST(vf)->thumbs_enabled;
1692
1693         vflist_thumb_stop(vf);
1694
1695         if (!vf->list)
1696                 {
1697                 vflist_store_clear(vf);
1698                 vf_send_update(vf);
1699                 return;
1700                 }
1701
1702         vflist_listview_set_columns(vf->listview, thumbs);
1703
1704         selected = vflist_selection_get_list(vf);
1705         
1706         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1707
1708         if (selected && vflist_selection_count(vf, NULL) == 0)
1709                 {
1710                 /* all selected files disappeared */
1711                 vflist_select_closest(vf, selected->data);
1712                 }
1713
1714         filelist_free(selected);
1715         
1716         vf_send_update(vf);
1717         vflist_thumb_update(vf);
1718 }
1719
1720 gint vflist_refresh(ViewFile *vf)
1721 {
1722         GList *old_list;
1723         gint ret = TRUE;
1724
1725         old_list = vf->list;
1726         vf->list = NULL;
1727
1728         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1729         if (vf->dir_fd)
1730                 {
1731                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1732
1733                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1734                 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1735                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1736
1737                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1738                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1739                 }
1740
1741         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1742
1743         vflist_populate_view(vf);
1744
1745         filelist_free(old_list);
1746         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1747
1748         return ret;
1749 }
1750
1751
1752
1753 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1754
1755 #define CELL_HEIGHT_OVERRIDE 512
1756
1757 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1758 {
1759         GParamSpec *spec;
1760
1761         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1762         if (spec && G_IS_PARAM_SPEC_INT(spec))
1763                 {
1764                 GParamSpecInt *spec_int;
1765
1766                 spec_int = G_PARAM_SPEC_INT(spec);
1767                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1768                 }
1769 }
1770
1771 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1772 {
1773         static GdkColor color;
1774         static GtkWidget *done = NULL;
1775
1776         if (done != widget)
1777                 {
1778                 GtkStyle *style;
1779
1780                 style = gtk_widget_get_style(widget);
1781                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1782                 shift_color(&color, -1, 0);
1783                 done = widget;
1784                 }
1785
1786         return &color;
1787 }
1788
1789 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1790                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1791 {
1792         ViewFile *vf = data;
1793         gboolean set;
1794
1795         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1796         g_object_set(G_OBJECT(cell),
1797                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1798                      "cell-background-set", set, NULL);
1799 }
1800
1801 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1802 {
1803         GtkTreeViewColumn *column;
1804         GtkCellRenderer *renderer;
1805
1806         column = gtk_tree_view_column_new();
1807         gtk_tree_view_column_set_title(column, title);
1808         gtk_tree_view_column_set_min_width(column, 4);
1809
1810         if (!image)
1811                 {
1812                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1813                 renderer = gtk_cell_renderer_text_new();
1814                 if (right_justify)
1815                         {
1816                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1817                         }
1818                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1819                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1820                 if (expand)
1821                         gtk_tree_view_column_set_expand(column, TRUE);
1822                 }
1823         else
1824                 {
1825                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1826                 renderer = gtk_cell_renderer_pixbuf_new();
1827                 cell_renderer_height_override(renderer);
1828                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1829                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1830                 }
1831
1832         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1833         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1834         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1835
1836         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1837 }
1838
1839 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1840 {
1841         ViewFile *vf = data;
1842         GtkTreeStore *store;
1843         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1844         GtkTreeIter iter;
1845         FileData *fd;
1846         gboolean mark;
1847         guint col_idx;
1848
1849         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1850         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1851                 return;
1852
1853         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1854
1855         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1856
1857         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1858         mark = !mark;
1859         file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1860         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1861         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1862                 {
1863                 vf_refresh_idle(vf);
1864                 }
1865         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1866
1867         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1868         gtk_tree_path_free(path);
1869 }
1870
1871 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1872 {
1873         GtkTreeViewColumn *column;
1874         GtkCellRenderer *renderer;
1875         GtkTreeStore *store;
1876         gint index;
1877
1878         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1879
1880         renderer = gtk_cell_renderer_toggle_new();
1881         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1882
1883         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1884         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1885         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1886
1887         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1888         gtk_tree_view_column_set_fixed_width(column, 18);
1889         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1890
1891
1892         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1893 }
1894
1895 /*
1896  *-----------------------------------------------------------------------------
1897  * base
1898  *-----------------------------------------------------------------------------
1899  */
1900
1901 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1902 {
1903         if (!dir_fd) return FALSE;
1904         if (vf->dir_fd == dir_fd) return TRUE;
1905
1906         file_data_unref(vf->dir_fd);
1907         vf->dir_fd = file_data_ref(dir_fd);
1908
1909         /* force complete reload */
1910         vflist_store_clear(vf);
1911
1912         filelist_free(vf->list);
1913         vf->list = NULL;
1914
1915         return vf_refresh(vf);
1916 }
1917
1918 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1919 {
1920         ViewFile *vf = data;
1921
1922         file_data_unregister_notify_func(vf_notify_cb, vf);
1923
1924         vflist_select_idle_cancel(vf);
1925         vf_refresh_idle_cancel(vf);
1926         vflist_thumb_stop(vf);
1927
1928         filelist_free(vf->list);
1929 }
1930
1931 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1932 {
1933         GtkTreeStore *store;
1934         GtkTreeSelection *selection;
1935
1936         GType flist_types[FILE_COLUMN_COUNT];
1937         gint i;
1938         gint column;
1939
1940         vf->info = g_new0(ViewFileInfoList, 1);
1941         
1942         VFLIST(vf)->click_fd = NULL;
1943         VFLIST(vf)->select_fd = NULL;
1944         VFLIST(vf)->thumbs_enabled = FALSE;
1945
1946         VFLIST(vf)->select_idle_id = -1;
1947
1948         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1949         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1950         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1951         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1952         flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1953         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1954         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1955         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1956         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1957                 flist_types[i] = G_TYPE_BOOLEAN;
1958
1959         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1960
1961         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1962         g_object_unref(store);
1963
1964         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1965         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1966         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1967
1968         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1969         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1970
1971         column = 0;
1972
1973         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1974                 {
1975                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1976                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1977                 column++;
1978                 }
1979
1980         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1981         g_assert(column == FILE_VIEW_COLUMN_THUMB);
1982         column++;
1983         
1984         vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1985         g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1986         column++;
1987
1988         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1989         g_assert(column == FILE_VIEW_COLUMN_NAME);
1990         column++;
1991
1992         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1993         g_assert(column == FILE_VIEW_COLUMN_SIZE);
1994         column++;
1995
1996         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1997         g_assert(column == FILE_VIEW_COLUMN_DATE);
1998         column++;
1999
2000         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2001         return vf;
2002 }
2003
2004 void vflist_thumb_set(ViewFile *vf, gint enable)
2005 {
2006         if (VFLIST(vf)->thumbs_enabled == enable) return;
2007
2008         VFLIST(vf)->thumbs_enabled = enable;
2009         if (vf->layout) vf_refresh(vf);
2010 }
2011
2012 void vflist_marks_set(ViewFile *vf, gint enable)
2013 {
2014         GList *columns, *work;
2015
2016         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2017
2018         work = columns;
2019         while (work)
2020                 {
2021                 GtkTreeViewColumn *column = work->data;
2022                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2023                 work = work->next;
2024
2025                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2026                         gtk_tree_view_column_set_visible(column, enable);
2027                 }
2028
2029         g_list_free(columns);
2030         //vf_refresh(vf);
2031 }
2032
2033 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */