expose the file grouping flag to the user
[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 = 0;
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 = 0;
734         return FALSE;
735 }
736
737 static void vflist_select_idle_cancel(ViewFile *vf)
738 {
739         if (VFLIST(vf)->select_idle_id)
740                 {
741                 g_source_remove(VFLIST(vf)->select_idle_id);
742                 VFLIST(vf)->select_idle_id = 0;
743                 }
744 }
745
746 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
747                                  gboolean path_currently_selected, gpointer data)
748 {
749         ViewFile *vf = data;
750         GtkTreeIter iter;
751
752         if (!path_currently_selected &&
753             gtk_tree_model_get_iter(store, &iter, tpath))
754                 {
755                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
756                 }
757         else
758                 {
759                 VFLIST(vf)->select_fd = NULL;
760                 }
761
762         if (vf->layout &&
763             !VFLIST(vf)->select_idle_id)
764                 {
765                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
766                 }
767
768         return TRUE;
769 }
770
771 /*
772  *-----------------------------------------------------------------------------
773  * misc
774  *-----------------------------------------------------------------------------
775  */
776
777 /*
778 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779                                         gboolean path_currently_selected, gpointer data)
780 {
781         return TRUE;
782 }
783 */
784
785 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
786 {
787         gchar *size;
788         gchar *sidecars = NULL;
789         gchar *name_sidecars;
790         gchar *multiline;
791         const gchar *time = text_from_time(fd->date);
792         gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
793
794
795         if (fd->sidecar_files)
796                 {
797                 sidecars = file_data_sc_list_to_string(fd);
798                 name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
799                 }
800         else
801                 {
802                 gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
803                 name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
804                 }
805         size = text_from_size(fd->size);
806         
807         multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
808
809         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
810                                         FILE_COLUMN_VERSION, fd->version,
811                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
812                                         FILE_COLUMN_MULTILINE, multiline,
813                                         FILE_COLUMN_NAME, name_sidecars,
814                                         FILE_COLUMN_SIZE, size,
815                                         FILE_COLUMN_DATE, time,
816 #define STORE_SET_IS_SLOW 1
817 #if STORE_SET_IS_SLOW
818 /* this is 3x faster on a directory with 20000 files */
819                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
820                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
821                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
822                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
823                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
824                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
825 #if FILEDATA_MARKS_SIZE != 6
826 #error this needs to be updated
827 #endif
828 #endif
829                                         FILE_COLUMN_COLOR, FALSE, -1);
830
831 #if !STORE_SET_IS_SLOW
832         {
833         gint i;
834         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
835                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
836         }
837 #endif
838         g_free(size);
839         g_free(sidecars);
840         g_free(name_sidecars);
841         g_free(multiline);
842 }
843
844 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
845 {
846         GList *work;
847         GtkTreeIter iter;
848         gboolean valid;
849         gint num_ordered = 0;
850         gint num_prepended = 0;
851
852         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
853
854         work = list;
855         while (work)
856                 {
857                 gint match;
858                 FileData *fd = work->data;
859                 gboolean done = FALSE;
860
861                 while (!done)
862                         {
863                         FileData *old_fd = NULL;
864                         gint old_version = 0;
865
866                         if (valid)
867                                 {
868                                 num_ordered++;
869                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
870                                                    FILE_COLUMN_POINTER, &old_fd,
871                                                    FILE_COLUMN_VERSION, &old_version,
872                                                    -1);
873
874                                 if (fd == old_fd)
875                                         {
876                                         match = 0;
877                                         }
878                                 else
879                                         {
880                                         if (parent_iter)
881                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
882                                         else
883                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
884
885                                         if (match == 0) g_warning("multiple fd for the same path");
886                                         }
887                                         
888                                 }
889                         else
890                                 {
891                                 match = -1;
892                                 }
893
894                         if (match < 0)
895                                 {
896                                 GtkTreeIter new;
897
898                                 if (valid)
899                                         {
900                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
901                                         }
902                                 else
903                                         {
904                                         /*
905                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
906                                             and it seems to be much faster to add new entries to the beginning and reorder later
907                                         */
908                                         num_prepended++;
909                                         gtk_tree_store_prepend(store, &new, parent_iter);
910                                         }
911
912                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
913                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
914                                 
915                                 if (g_list_find(selected, fd))
916                                         {
917                                         /* renamed files - the same fd appears at different position - select it again*/
918                                         GtkTreeSelection *selection;
919                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
920                                         gtk_tree_selection_select_iter(selection, &new);
921                                         }
922
923                                 done = TRUE;
924                                 }
925                         else if (match > 0)
926                                 {
927                                 file_data_unref(old_fd);
928                                 valid = gtk_tree_store_remove(store, &iter);
929                                 }
930                         else
931                                 {
932                                 if (fd->version != old_version)
933                                         {
934                                         vflist_setup_iter(vf, store, &iter, fd);
935                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
936                                         }
937
938                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
939
940                                 done = TRUE;
941                                 }
942                         }
943                 work = work->next;
944                 }
945
946         while (valid)
947                 {
948                 FileData *old_fd;
949                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
950                 file_data_unref(old_fd);
951
952                 valid = gtk_tree_store_remove(store, &iter);
953                 }
954                 
955         /* move the prepended entries to the correct position */
956         if (num_prepended)
957                 {
958                 gint i;
959                 gint num_total = num_prepended + num_ordered;
960                 gint *new_order = g_malloc(num_total * sizeof(gint));
961                 
962                 for (i = 0; i < num_total; i++)
963                         {
964                         if (i < num_ordered)
965                                 new_order[i] = num_prepended + i;
966                         else
967                                 new_order[i] = num_total - 1 - i;
968                         }
969                 gtk_tree_store_reorder(store, parent_iter, new_order);
970
971                 g_free(new_order);
972                 }
973 }
974
975 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
976 {
977         gint i;
978         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
979         gint *new_order;
980         GtkTreeStore *store;
981         GList *work;
982
983         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
984         if (!vf->list) return;
985
986         work = vf->list;
987         i = 0;
988         while (work)
989                 {
990                 FileData *fd = work->data;
991                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
992                 i++;
993                 work = work->next;
994                 }
995
996         vf->sort_method = type;
997         vf->sort_ascend = ascend;
998
999         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1000
1001         new_order = g_malloc(i * sizeof(gint));
1002
1003         work = vf->list;
1004         i = 0;
1005         while (work)
1006                 {
1007                 FileData *fd = work->data;
1008                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1009                 i++;
1010                 work = work->next;
1011                 }
1012
1013         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1014         gtk_tree_store_reorder(store, NULL, new_order);
1015
1016         g_free(new_order);
1017         g_hash_table_destroy(fd_idx_hash);
1018 }
1019
1020 /*
1021  *-----------------------------------------------------------------------------
1022  * thumb updates
1023  *-----------------------------------------------------------------------------
1024  */
1025
1026 static gboolean vflist_thumb_next(ViewFile *vf);
1027
1028 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1029 {
1030         GList *work = list;
1031         while (work)
1032                 {
1033                 FileData *fd = work->data;
1034                 work = work->next;
1035
1036                 if (fd->thumb_pixbuf) (*done)++;
1037                 
1038                 if (fd->sidecar_files)
1039                         {
1040                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
1041                         }
1042                 (*count)++;
1043                 }
1044 }
1045
1046 static gdouble vflist_thumb_progress(ViewFile *vf)
1047 {
1048         gint count = 0;
1049         gint done = 0;
1050         
1051         vflist_thumb_progress_count(vf->list, &count, &done);
1052
1053         DEBUG_1("thumb progress: %d of %d", done, count);
1054         return (gdouble)done / count;
1055 }
1056
1057
1058 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1059 {
1060         if (vf->func_thumb_status)
1061                 {
1062                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1063                 }
1064 }
1065
1066 static void vflist_thumb_cleanup(ViewFile *vf)
1067 {
1068         vflist_thumb_status(vf, 0.0, NULL);
1069
1070         vf->thumbs_running = FALSE;
1071
1072         thumb_loader_free(vf->thumbs_loader);
1073         vf->thumbs_loader = NULL;
1074
1075         vf->thumbs_filedata = NULL;
1076 }
1077
1078 static void vflist_thumb_stop(ViewFile *vf)
1079 {
1080         if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1081 }
1082
1083 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1084 {
1085         GtkTreeStore *store;
1086         GtkTreeIter iter;
1087
1088         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1089
1090         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1091         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1092
1093         vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1094 }
1095
1096 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1097 {
1098         ViewFile *vf = data;
1099
1100         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1101                 {
1102                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1103                 }
1104
1105         while (vflist_thumb_next(vf));
1106 }
1107
1108 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1109 {
1110         ViewFile *vf = data;
1111
1112         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1113                 {
1114                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1115                 }
1116
1117         while (vflist_thumb_next(vf));
1118 }
1119
1120 static gboolean vflist_thumb_next(ViewFile *vf)
1121 {
1122         GtkTreePath *tpath;
1123         FileData *fd = NULL;
1124
1125         /* first check the visible files */
1126
1127         if (GTK_WIDGET_REALIZED(vf->listview) &&
1128             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1129                 {
1130                 GtkTreeModel *store;
1131                 GtkTreeIter iter;
1132                 gboolean valid = TRUE;
1133         
1134                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1135                 gtk_tree_model_get_iter(store, &iter, tpath);
1136                 gtk_tree_path_free(tpath);
1137
1138                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1139                         {
1140                         FileData *nfd;
1141
1142                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1143
1144                         if (!nfd->thumb_pixbuf) fd = nfd;
1145
1146                         valid = gtk_tree_model_iter_next(store, &iter);
1147                         }
1148                 }
1149
1150         /* then find first undone */
1151
1152         if (!fd)
1153                 {
1154                 GList *work = vf->list;
1155                 while (work && !fd)
1156                         {
1157                         FileData *fd_p = work->data;
1158                         if (!fd_p->thumb_pixbuf)
1159                                 fd = fd_p;
1160                         else
1161                                 {
1162                                 GList *work2 = fd_p->sidecar_files;
1163
1164                                 while (work2 && !fd)
1165                                         {
1166                                         fd_p = work2->data;
1167                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1168                                         work2 = work2->next;
1169                                         }
1170                                 }
1171                         work = work->next;
1172                         }
1173                 }
1174
1175         if (!fd)
1176                 {
1177                 /* done */
1178                 vflist_thumb_cleanup(vf);
1179                 return FALSE;
1180                 }
1181
1182         vf->thumbs_filedata = fd;
1183
1184         thumb_loader_free(vf->thumbs_loader);
1185
1186         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1187         thumb_loader_set_callbacks(vf->thumbs_loader,
1188                                    vflist_thumb_done_cb,
1189                                    vflist_thumb_error_cb,
1190                                    NULL,
1191                                    vf);
1192
1193         if (!thumb_loader_start(vf->thumbs_loader, fd))
1194                 {
1195                 /* set icon to unknown, continue */
1196                 DEBUG_1("thumb loader start failed %s", fd->path);
1197                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1198
1199                 return TRUE;
1200                 }
1201
1202         return FALSE;
1203 }
1204
1205 void vflist_thumb_update(ViewFile *vf)
1206 {
1207         vflist_thumb_stop(vf);
1208         if (!VFLIST(vf)->thumbs_enabled) return;
1209
1210         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1211         vf->thumbs_running = TRUE;
1212
1213         if (thumb_format_changed)
1214                 {
1215                 GList *work = vf->list;
1216                 while (work)
1217                         {
1218                         FileData *fd = work->data;
1219                         if (fd->thumb_pixbuf)
1220                                 {
1221                                 g_object_unref(fd->thumb_pixbuf);
1222                                 fd->thumb_pixbuf = NULL;
1223                                 }
1224                         work = work->next;
1225                         }
1226
1227                 thumb_format_changed = FALSE;
1228                 }
1229
1230         while (vflist_thumb_next(vf));
1231 }
1232
1233 /*
1234  *-----------------------------------------------------------------------------
1235  * row stuff
1236  *-----------------------------------------------------------------------------
1237  */
1238
1239 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1240 {
1241         return g_list_nth_data(vf->list, row);
1242 }
1243
1244 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1245 {
1246         gint p = 0;
1247         GList *work, *work2;
1248
1249         work = vf->list;
1250         while (work)
1251                 {
1252                 FileData *list_fd = work->data;
1253                 if (list_fd == fd) return p;
1254                 
1255                 work2 = list_fd->sidecar_files;
1256                 while (work2)
1257                         {
1258                         /* FIXME: return the same index also for sidecars
1259                            it is sufficient for next/prev navigation but it should be rewritten 
1260                            without using indexes at all
1261                         */
1262                         FileData *sidecar_fd = work2->data;
1263                         if (sidecar_fd == fd) return p;
1264                         work2 = work2->next;
1265                         }
1266                 
1267                 work = work->next;
1268                 p++;
1269                 }
1270
1271         return -1;
1272 }
1273
1274 guint vflist_count(ViewFile *vf, gint64 *bytes)
1275 {
1276         if (bytes)
1277                 {
1278                 gint64 b = 0;
1279                 GList *work;
1280
1281                 work = vf->list;
1282                 while (work)
1283                         {
1284                         FileData *fd = work->data;
1285                         work = work->next;
1286                         b += fd->size;
1287                         }
1288
1289                 *bytes = b;
1290                 }
1291
1292         return g_list_length(vf->list);
1293 }
1294
1295 GList *vflist_get_list(ViewFile *vf)
1296 {
1297         GList *list = NULL;
1298         GList *work;
1299
1300         work = vf->list;
1301         while (work)
1302                 {
1303                 FileData *fd = work->data;
1304                 work = work->next;
1305
1306                 list = g_list_prepend(list, file_data_ref(fd));
1307                 }
1308
1309         return g_list_reverse(list);
1310 }
1311
1312 /*
1313  *-----------------------------------------------------------------------------
1314  * selections
1315  *-----------------------------------------------------------------------------
1316  */
1317
1318 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1319 {
1320         GtkTreeModel *store;
1321         GtkTreeSelection *selection;
1322         GList *slist;
1323         GList *work;
1324         gboolean found = FALSE;
1325
1326         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1327         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1328         work = slist;
1329         while (!found && work)
1330                 {
1331                 GtkTreePath *tpath = work->data;
1332                 FileData *fd_n;
1333                 GtkTreeIter iter;
1334
1335                 gtk_tree_model_get_iter(store, &iter, tpath);
1336                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1337                 if (fd_n == fd) found = TRUE;
1338                 work = work->next;
1339                 }
1340         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1341         g_list_free(slist);
1342
1343         return found;
1344 }
1345
1346 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1347 {
1348         FileData *fd;
1349
1350         fd = vf_index_get_data(vf, row);
1351         return vflist_row_is_selected(vf, fd);
1352 }
1353
1354 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1355 {
1356         GtkTreeModel *store;
1357         GtkTreeSelection *selection;
1358         GList *slist;
1359         guint count;
1360
1361         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1362         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1363
1364         if (bytes)
1365                 {
1366                 gint64 b = 0;
1367                 GList *work;
1368
1369                 work = slist;
1370                 while (work)
1371                         {
1372                         GtkTreePath *tpath = work->data;
1373                         GtkTreeIter iter;
1374                         FileData *fd;
1375
1376                         gtk_tree_model_get_iter(store, &iter, tpath);
1377                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1378                         b += fd->size;
1379
1380                         work = work->next;
1381                         }
1382
1383                 *bytes = b;
1384                 }
1385
1386         count = g_list_length(slist);
1387         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1388         g_list_free(slist);
1389
1390         return count;
1391 }
1392
1393 GList *vflist_selection_get_list(ViewFile *vf)
1394 {
1395         GtkTreeModel *store;
1396         GtkTreeSelection *selection;
1397         GList *slist;
1398         GList *list = NULL;
1399         GList *work;
1400
1401         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1402         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1403         work = slist;
1404         while (work)
1405                 {
1406                 GtkTreePath *tpath = work->data;
1407                 FileData *fd;
1408                 GtkTreeIter iter;
1409
1410                 gtk_tree_model_get_iter(store, &iter, tpath);
1411                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1412
1413                 list = g_list_prepend(list, file_data_ref(fd));
1414
1415                 work = work->next;
1416                 }
1417         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1418         g_list_free(slist);
1419
1420         return g_list_reverse(list);
1421 }
1422
1423 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1424 {
1425         GtkTreeModel *store;
1426         GtkTreeSelection *selection;
1427         GList *slist;
1428         GList *list = NULL;
1429         GList *work;
1430
1431         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1432         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1433         work = slist;
1434         while (work)
1435                 {
1436                 GtkTreePath *tpath = work->data;
1437                 FileData *fd;
1438                 GtkTreeIter iter;
1439
1440                 gtk_tree_model_get_iter(store, &iter, tpath);
1441                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442
1443                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1444
1445                 work = work->next;
1446                 }
1447         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1448         g_list_free(slist);
1449
1450         return g_list_reverse(list);
1451 }
1452
1453 void vflist_select_all(ViewFile *vf)
1454 {
1455         GtkTreeSelection *selection;
1456
1457         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1458         gtk_tree_selection_select_all(selection);
1459
1460         VFLIST(vf)->select_fd = NULL;
1461 }
1462
1463 void vflist_select_none(ViewFile *vf)
1464 {
1465         GtkTreeSelection *selection;
1466
1467         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1468         gtk_tree_selection_unselect_all(selection);
1469 }
1470
1471 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1472 {
1473         GtkTreePath *tpath;
1474         gboolean result;
1475
1476         tpath = gtk_tree_model_get_path(store, iter);
1477         result = gtk_tree_path_prev(tpath);
1478         if (result)
1479                 gtk_tree_model_get_iter(store, iter, tpath);
1480
1481         gtk_tree_path_free(tpath);
1482
1483         return result;
1484 }
1485
1486 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1487 {
1488         if (!gtk_tree_model_get_iter_first(store, iter))
1489                 return FALSE;
1490
1491         while (TRUE)
1492                 {
1493                 GtkTreeIter next = *iter;
1494                 
1495                 if (gtk_tree_model_iter_next(store, &next))
1496                         *iter = next;
1497                 else
1498                         break;
1499                 }
1500         
1501         return TRUE;
1502 }
1503
1504 void vflist_select_invert(ViewFile *vf)
1505 {
1506         GtkTreeIter iter;
1507         GtkTreeSelection *selection;
1508         GtkTreeModel *store;
1509         gboolean valid;
1510
1511         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1512         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1513
1514         /* Backward iteration prevents scrolling to the end of the list,
1515          * it scrolls to the first selected row instead. */
1516         valid = tree_model_get_iter_last(store, &iter);
1517
1518         while (valid)
1519                 {
1520                 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1521
1522                 if (selected)
1523                         gtk_tree_selection_unselect_iter(selection, &iter);
1524                 else
1525                         gtk_tree_selection_select_iter(selection, &iter);
1526                                 
1527                 valid = tree_model_iter_prev(store, &iter);
1528                 }
1529 }
1530
1531 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1532 {
1533         GtkTreeIter iter;
1534
1535         if (vflist_find_row(vf, fd, &iter) < 0) return;
1536
1537         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1538
1539         if (!vflist_row_is_selected(vf, fd))
1540                 {
1541                 GtkTreeSelection *selection;
1542                 GtkTreeModel *store;
1543                 GtkTreePath *tpath;
1544
1545                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1546                 gtk_tree_selection_unselect_all(selection);
1547                 gtk_tree_selection_select_iter(selection, &iter);
1548                 vflist_move_cursor(vf, &iter);
1549
1550                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1551                 tpath = gtk_tree_model_get_path(store, &iter);
1552                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1553                 gtk_tree_path_free(tpath);
1554                 }
1555 }
1556
1557 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1558 {
1559         GList *work;
1560         FileData *fd = NULL;
1561         
1562         if (sel_fd->parent) sel_fd = sel_fd->parent;
1563         work = vf->list;
1564         
1565         while (work)
1566                 {
1567                 gint match;
1568                 fd = work->data;
1569                 work = work->next;
1570
1571                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1572                 
1573                 if (match >= 0) break;
1574                 }
1575
1576         if (fd) vflist_select_by_fd(vf, fd);
1577
1578 }
1579
1580 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1581 {
1582         GtkTreeModel *store;
1583         GtkTreeIter iter;
1584         GtkTreeSelection *selection;
1585         gboolean valid;
1586         gint n = mark - 1;
1587
1588         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589
1590         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1591         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1592
1593         valid = gtk_tree_model_get_iter_first(store, &iter);
1594         while (valid)
1595                 {
1596                 FileData *fd;
1597                 gboolean mark_val, selected;
1598                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1599
1600                 mark_val = file_data_get_mark(fd, n);
1601                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1602
1603                 switch (mode)
1604                         {
1605                         case MTS_MODE_SET: selected = mark_val;
1606                                 break;
1607                         case MTS_MODE_OR: selected = mark_val | selected;
1608                                 break;
1609                         case MTS_MODE_AND: selected = mark_val & selected;
1610                                 break;
1611                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1612                                 break;
1613                         }
1614
1615                 if (selected)
1616                         gtk_tree_selection_select_iter(selection, &iter);
1617                 else
1618                         gtk_tree_selection_unselect_iter(selection, &iter);
1619
1620                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1621                 }
1622 }
1623
1624 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1625 {
1626         GtkTreeModel *store;
1627         GtkTreeSelection *selection;
1628         GList *slist;
1629         GList *work;
1630         gint n = mark - 1;
1631
1632         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1633
1634         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1635         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1636         work = slist;
1637         while (work)
1638                 {
1639                 GtkTreePath *tpath = work->data;
1640                 FileData *fd;
1641                 GtkTreeIter iter;
1642
1643                 gtk_tree_model_get_iter(store, &iter, tpath);
1644                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1645
1646                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1647
1648                 switch (mode)
1649                         {
1650                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1651                                 break;
1652                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1653                                 break;
1654                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1655                                 break;
1656                         }
1657                 
1658                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1659                         {
1660                         vf_refresh_idle(vf);
1661                         }
1662                 else
1663                         {
1664                         /* mark functions can have various side effects - update all columns to be sure */
1665                         vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1666                         }
1667
1668                 
1669                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1670
1671                 work = work->next;
1672                 }
1673         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1674         g_list_free(slist);
1675 }
1676
1677 /*
1678  *-----------------------------------------------------------------------------
1679  * core (population)
1680  *-----------------------------------------------------------------------------
1681  */
1682
1683 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1684 {
1685         GtkTreeViewColumn *column;
1686         GtkCellRenderer *cell;
1687         GList *list;
1688         gboolean multiline;
1689
1690         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1691         if (!column) return;
1692
1693         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1694
1695         list = gtk_tree_view_column_get_cell_renderers(column);
1696         if (!list) return;
1697         cell = list->data;
1698         g_list_free(list);
1699
1700         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1701         gtk_tree_view_column_set_visible(column, thumb);
1702
1703         multiline = (thumb && options->thumbnails.max_height >= 48);
1704         
1705         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1706         if (!column) return;
1707         gtk_tree_view_column_set_visible(column, multiline);
1708         if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1709
1710         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1711         if (!column) return;
1712         gtk_tree_view_column_set_visible(column, !multiline);
1713         if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1714
1715         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1716         if (!column) return;
1717         gtk_tree_view_column_set_visible(column, !multiline);
1718
1719         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1720         if (!column) return;
1721         gtk_tree_view_column_set_visible(column, !multiline);
1722
1723         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1724 }
1725
1726 static void vflist_populate_view(ViewFile *vf)
1727 {
1728         GtkTreeStore *store;
1729         gboolean thumbs_enabled;
1730         GList *selected;
1731
1732         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1733         thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1734
1735         vflist_thumb_stop(vf);
1736
1737         if (!vf->list)
1738                 {
1739                 vflist_store_clear(vf);
1740                 vf_send_update(vf);
1741                 return;
1742                 }
1743
1744         vflist_listview_set_columns(vf->listview, thumbs_enabled);
1745
1746         selected = vflist_selection_get_list(vf);
1747         
1748         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1749
1750         if (selected && vflist_selection_count(vf, NULL) == 0)
1751                 {
1752                 /* all selected files disappeared */
1753                 vflist_select_closest(vf, selected->data);
1754                 }
1755
1756         filelist_free(selected);
1757         
1758         vf_send_update(vf);
1759         vflist_thumb_update(vf);
1760 }
1761
1762 gboolean vflist_refresh(ViewFile *vf)
1763 {
1764         GList *old_list;
1765         gboolean ret = TRUE;
1766
1767         old_list = vf->list;
1768         vf->list = NULL;
1769
1770         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1771         if (vf->dir_fd)
1772                 {
1773                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1774
1775                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1776                 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1777                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1778
1779                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1780                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1781                 }
1782
1783         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1784
1785         vflist_populate_view(vf);
1786
1787         filelist_free(old_list);
1788         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1789
1790         return ret;
1791 }
1792
1793
1794
1795 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1796
1797 #define CELL_HEIGHT_OVERRIDE 512
1798
1799 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1800 {
1801         GParamSpec *spec;
1802
1803         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1804         if (spec && G_IS_PARAM_SPEC_INT(spec))
1805                 {
1806                 GParamSpecInt *spec_int;
1807
1808                 spec_int = G_PARAM_SPEC_INT(spec);
1809                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1810                 }
1811 }
1812
1813 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1814 {
1815         static GdkColor color;
1816         static GtkWidget *done = NULL;
1817
1818         if (done != widget)
1819                 {
1820                 GtkStyle *style;
1821
1822                 style = gtk_widget_get_style(widget);
1823                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1824                 shift_color(&color, -1, 0);
1825                 done = widget;
1826                 }
1827
1828         return &color;
1829 }
1830
1831 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1832                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1833 {
1834         ViewFile *vf = data;
1835         gboolean set;
1836
1837         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1838         g_object_set(G_OBJECT(cell),
1839                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1840                      "cell-background-set", set, NULL);
1841 }
1842
1843 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1844 {
1845         GtkTreeViewColumn *column;
1846         GtkCellRenderer *renderer;
1847
1848         column = gtk_tree_view_column_new();
1849         gtk_tree_view_column_set_title(column, title);
1850         gtk_tree_view_column_set_min_width(column, 4);
1851
1852         if (!image)
1853                 {
1854                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1855                 renderer = gtk_cell_renderer_text_new();
1856                 if (right_justify)
1857                         {
1858                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1859                         }
1860                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1861                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1862                 if (expand)
1863                         gtk_tree_view_column_set_expand(column, TRUE);
1864                 }
1865         else
1866                 {
1867                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1868                 renderer = gtk_cell_renderer_pixbuf_new();
1869                 cell_renderer_height_override(renderer);
1870                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1871                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1872                 }
1873
1874         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1875         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1876         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1877
1878         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1879 }
1880
1881 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1882 {
1883         ViewFile *vf = data;
1884         GtkTreeStore *store;
1885         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1886         GtkTreeIter iter;
1887         FileData *fd;
1888         gboolean marked;
1889         guint col_idx;
1890
1891         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1892         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1893                 return;
1894
1895         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1896
1897         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1898
1899         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1900         marked = !marked;
1901         file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1902         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1903         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1904                 {
1905                 vf_refresh_idle(vf);
1906                 }
1907         else
1908                 {
1909                 /* mark functions can have various side effects - update all columns to be sure */
1910                 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1911                 }
1912         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1913
1914         gtk_tree_path_free(path);
1915 }
1916
1917 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1918 {
1919         GtkTreeViewColumn *column;
1920         GtkCellRenderer *renderer;
1921         GtkTreeStore *store;
1922         gint index;
1923
1924         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1925
1926         renderer = gtk_cell_renderer_toggle_new();
1927         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1928
1929         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1930         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1931         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1932
1933         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1934         gtk_tree_view_column_set_fixed_width(column, 22);
1935         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1936
1937
1938         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1939 }
1940
1941 /*
1942  *-----------------------------------------------------------------------------
1943  * base
1944  *-----------------------------------------------------------------------------
1945  */
1946
1947 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1948 {
1949         if (!dir_fd) return FALSE;
1950         if (vf->dir_fd == dir_fd) return TRUE;
1951
1952         file_data_unref(vf->dir_fd);
1953         vf->dir_fd = file_data_ref(dir_fd);
1954
1955         /* force complete reload */
1956         vflist_store_clear(vf);
1957
1958         filelist_free(vf->list);
1959         vf->list = NULL;
1960
1961         return vf_refresh(vf);
1962 }
1963
1964 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1965 {
1966         ViewFile *vf = data;
1967
1968         file_data_unregister_notify_func(vf_notify_cb, vf);
1969
1970         vflist_select_idle_cancel(vf);
1971         vf_refresh_idle_cancel(vf);
1972         vflist_thumb_stop(vf);
1973
1974         filelist_free(vf->list);
1975 }
1976
1977 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1978 {
1979         GtkTreeStore *store;
1980         GtkTreeSelection *selection;
1981         GType flist_types[FILE_COLUMN_COUNT];
1982         gint i;
1983         gint column;
1984
1985         vf->info = g_new0(ViewFileInfoList, 1);
1986         
1987         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1988         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1989         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1990         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1991         flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1992         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1993         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1994         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1995         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1996                 flist_types[i] = G_TYPE_BOOLEAN;
1997
1998         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1999
2000         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2001         g_object_unref(store);
2002
2003         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2004         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2005         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2006
2007         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2008         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2009
2010         column = 0;
2011
2012         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2013                 {
2014                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2015                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2016                 column++;
2017                 }
2018
2019         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2020         g_assert(column == FILE_VIEW_COLUMN_THUMB);
2021         column++;
2022         
2023         vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
2024         g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
2025         column++;
2026
2027         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
2028         g_assert(column == FILE_VIEW_COLUMN_NAME);
2029         column++;
2030
2031         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2032         g_assert(column == FILE_VIEW_COLUMN_SIZE);
2033         column++;
2034
2035         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2036         g_assert(column == FILE_VIEW_COLUMN_DATE);
2037         column++;
2038
2039         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2040         return vf;
2041 }
2042
2043 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2044 {
2045         if (VFLIST(vf)->thumbs_enabled == enable) return;
2046
2047         VFLIST(vf)->thumbs_enabled = enable;
2048         if (vf->layout) vf_refresh(vf);
2049 }
2050
2051 void vflist_marks_set(ViewFile *vf, gboolean enable)
2052 {
2053         GList *columns, *work;
2054
2055         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2056
2057         work = columns;
2058         while (work)
2059                 {
2060                 GtkTreeViewColumn *column = work->data;
2061                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2062                 work = work->next;
2063
2064                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2065                         gtk_tree_view_column_set_visible(column, enable);
2066                 }
2067
2068         g_list_free(columns);
2069         //vf_refresh(vf);
2070 }
2071
2072 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */