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