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