do not use cell_data_func
[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_FORMATTED,
41         FILE_COLUMN_NAME,
42         FILE_COLUMN_SIDECARS,
43         FILE_COLUMN_SIZE,
44         FILE_COLUMN_DATE,
45         FILE_COLUMN_EXPANDED,
46         FILE_COLUMN_COLOR,
47         FILE_COLUMN_MARKS,
48         FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
49         FILE_COLUMN_COUNT
50 };
51
52
53 /* Index to tree view */
54 enum {
55         FILE_VIEW_COLUMN_MARKS = 0,
56         FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
57         FILE_VIEW_COLUMN_THUMB,
58         FILE_VIEW_COLUMN_FORMATTED,
59         FILE_VIEW_COLUMN_SIZE,
60         FILE_VIEW_COLUMN_DATE,
61         FILE_VIEW_COLUMN_COUNT
62 };
63
64
65
66 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
71
72
73 /*
74  *-----------------------------------------------------------------------------
75  * misc
76  *-----------------------------------------------------------------------------
77  */
78 typedef struct {
79         FileData *fd;
80         GtkTreeIter *iter;
81         gboolean found;
82         gint row;
83 } ViewFileFindRowData;
84
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
86 {
87         ViewFileFindRowData *find = data;
88         FileData *fd;
89         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
90         if (fd == find->fd)
91                 {
92                 *find->iter = *iter;
93                 find->found = TRUE;
94                 return TRUE;
95                 }
96         find->row++;
97         return FALSE;
98 }
99
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
101 {
102         GtkTreeModel *store;
103         ViewFileFindRowData data = {fd, iter, FALSE, 0};
104
105         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106         gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
107
108         if (data.found)
109                 {
110                 return data.row;
111                 }
112
113         return -1;
114 }
115
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
117 {
118         GtkTreePath *tpath;
119         GtkTreeViewColumn *column;
120
121         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122                                           &tpath, &column, NULL, NULL))
123                 {
124                 GtkTreeModel *store;
125                 GtkTreeIter row;
126                 FileData *fd;
127
128                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129                 gtk_tree_model_get_iter(store, &row, tpath);
130                 gtk_tree_path_free(tpath);
131                 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
132
133                 return fd;
134                 }
135
136         return NULL;
137 }
138
139 #if 0
140 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
141 {
142         gint i = 0;
143         while (work)
144                 {
145                 FileData *fd_p = work->data;
146                 if (fd == fd_p) return i;
147
148                 i++;
149
150                 GList *work2 = fd_p->sidecar_files;
151                 while (work2)
152                         {
153                         fd_p = work2->data;
154                         if (fd == fd_p) return i;
155
156                         i++;
157                         work2 = work2->next;
158                         }
159                 work = work->next;
160                 }
161         return -1;
162 }
163
164 static gint vflist_sidecar_list_count(GList *work)
165 {
166         gint i = 0;
167         while (work)
168                 {
169                 FileData *fd = work->data;
170                 i++;
171
172                 GList *work2 = fd->sidecar_files;
173                 while (work2)
174                         {
175                         i++;
176                         work2 = work2->next;
177                         }
178                 work = work->next;
179                 }
180         return i;
181 }
182 #endif
183
184 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
185 {
186         FileData *fd;
187         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
188         file_data_unref(fd);
189         return FALSE;
190 }
191
192 static void vflist_store_clear(ViewFile *vf)
193 {
194         GtkTreeModel *store;
195         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
196         gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
197         gtk_tree_store_clear(GTK_TREE_STORE(store));
198 }
199
200 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
201 {
202         GtkTreeModel *store;
203         GtkTreeIter iter;
204
205         if (vflist_find_row(vf, fd, &iter) < 0) return;
206         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
207         gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
208 }
209
210 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
211 {
212         GtkTreeModel *store;
213         GtkTreePath *tpath;
214
215         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
216
217         tpath = gtk_tree_model_get_path(store, iter);
218         gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
219         gtk_tree_path_free(tpath);
220 }
221
222
223 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
224 {
225         GList *columns, *work;
226         gint i = 0;
227
228         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
229         work = columns;
230         while (work)
231                 {
232                 GtkTreeViewColumn *column = work->data;
233                 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
234                         break;
235                 work = work->next;
236                 i++;
237                 }
238
239         g_list_free(columns);
240         return i;
241 }
242
243
244 /*
245  *-----------------------------------------------------------------------------
246  * dnd
247  *-----------------------------------------------------------------------------
248  */
249
250 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
251                            GtkSelectionData *selection_data, guint info,
252                            guint time, gpointer data)
253 {
254         ViewFile *vf = data;
255         GList *list = NULL;
256         gchar *uri_text = NULL;
257         gint total;
258
259         if (!VFLIST(vf)->click_fd) return;
260
261         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
262                 {
263                 list = vf_selection_get_list(vf);
264                 }
265         else
266                 {
267                 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
268                 }
269
270         if (!list) return;
271
272         uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
273         filelist_free(list);
274
275         DEBUG_1("%s", uri_text);
276
277         gtk_selection_data_set(selection_data, selection_data->target,
278                                8, (guchar *)uri_text, total);
279         g_free(uri_text);
280 }
281
282 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
283 {
284         ViewFile *vf = data;
285
286         vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
287
288         if (VFLIST(vf)->thumbs_enabled &&
289             VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
290                 {
291                 guint items;
292
293                 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
294                         items = vf_selection_count(vf, NULL);
295                 else
296                         items = 1;
297
298                 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
299                 }
300 }
301
302 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
303 {
304         ViewFile *vf = data;
305
306         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
307
308         if (context->action == GDK_ACTION_MOVE)
309                 {
310                 vf_refresh(vf);
311                 }
312 }
313
314 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
315                                       int x, int y, GtkSelectionData *selection,
316                                       guint info, guint time, gpointer data)
317 {
318         ViewFile *vf = data;
319
320         if (info == TARGET_TEXT_PLAIN) {
321                 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
322
323                 if (fd) {
324                         /* Add keywords to file */
325                         gchar *str = g_strndup((gchar *)selection->data, selection->length);
326                         GList *kw_list = string_to_keywords_list(str);
327                         
328                         metadata_append_list(fd, KEYWORD_KEY, kw_list);
329                         string_list_free(kw_list);
330                         g_free(str);
331 /*
332 file notification should handle this automatically
333                         if (vf->layout && vf->layout->bar_info) {
334                                 bar_set_fd(vf->layout->bar_info, fd);
335                         }
336 */
337                 }
338         }
339 }
340
341 void vflist_dnd_init(ViewFile *vf)
342 {
343         gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
344                             dnd_file_drag_types, dnd_file_drag_types_count,
345                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
346         gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
347                             dnd_file_drag_types, dnd_file_drag_types_count,
348                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
349
350         g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
351                          G_CALLBACK(vflist_dnd_get), vf);
352         g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
353                          G_CALLBACK(vflist_dnd_begin), vf);
354         g_signal_connect(G_OBJECT(vf->listview), "drag_end",
355                          G_CALLBACK(vflist_dnd_end), vf);
356         g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
357                          G_CALLBACK(vflist_drag_data_received), vf);
358 }
359
360 /*
361  *-----------------------------------------------------------------------------
362  * pop-up menu
363  *-----------------------------------------------------------------------------
364  */
365
366 GList *vflist_pop_menu_file_list(ViewFile *vf)
367 {
368         GList *list;
369         if (!VFLIST(vf)->click_fd) return NULL;
370
371         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
372                 {
373                 return vf_selection_get_list(vf);
374                 }
375
376         list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
377
378         if (VFLIST(vf)->click_fd->sidecar_files)
379                 {
380                 /* check if the row is expanded */
381                 GtkTreeModel *store;
382                 GtkTreeIter iter;
383                 
384                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
385                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
386                         {
387                         GtkTreePath *tpath;
388
389                         tpath = gtk_tree_model_get_path(store, &iter);
390                         if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
391                                 {
392                                 /* unexpanded - add whole group */
393                                 GList *work = VFLIST(vf)->click_fd->sidecar_files;
394                                 while (work)
395                                         {
396                                         FileData *sfd = work->data;
397                                         list = g_list_prepend(list, file_data_ref(sfd));
398                                         work = work->next;
399                                         }
400                                 }
401                         gtk_tree_path_free(tpath);
402                         }
403                 list = g_list_reverse(list);
404                 }
405
406         return list;
407 }
408
409 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
410 {
411         ViewFile *vf = data;
412
413         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
414                 {
415                 GList *list;
416
417                 list = vf_selection_get_list(vf);
418                 view_window_new_from_list(list);
419                 filelist_free(list);
420                 }
421         else
422                 {
423                 view_window_new(VFLIST(vf)->click_fd);
424                 }
425 }
426
427 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
428 {
429         ViewFile *vf = data;
430         GList *list;
431
432         list = vf_pop_menu_file_list(vf);
433         if (options->file_ops.enable_in_place_rename &&
434             list && !list->next && VFLIST(vf)->click_fd)
435                 {
436                 GtkTreeModel *store;
437                 GtkTreeIter iter;
438
439                 filelist_free(list);
440
441                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
442                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
443                         {
444                         GtkTreePath *tpath;
445
446                         tpath = gtk_tree_model_get_path(store, &iter);
447                         tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
448                                           vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
449                                           vflist_row_rename_cb, vf);
450                         gtk_tree_path_free(tpath);
451                         }
452                 return;
453                 }
454
455         file_util_rename(NULL, list, vf->listview);
456 }
457
458 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
459 {
460         ViewFile *vf = data;
461
462         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
463         if (vf->layout)
464                 {
465                 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
466                 }
467         else
468                 {
469                 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
470                 }
471 }
472
473 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
474 {
475         ViewFile *vf = data;
476
477         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
478         vf_refresh(vf);
479 }
480
481 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
482 {
483         ViewFile *vf = data;
484         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
485         VFLIST(vf)->click_fd = NULL;
486         vf->popup = NULL;
487 }
488
489
490 /*
491  *-----------------------------------------------------------------------------
492  * callbacks
493  *-----------------------------------------------------------------------------
494  */
495
496 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
497 {
498         ViewFile *vf = data;
499         gchar *new_path;
500
501         if (!new || !new[0]) return FALSE;
502
503         new_path = g_build_filename(vf->dir_fd->path, new, NULL);
504
505         if (strchr(new, G_DIR_SEPARATOR) != NULL)
506                 {
507                 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
508                 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
509                 g_free(text);
510                 }
511         else
512                 {
513                 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
514                 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
515                 file_util_rename_simple(fd, new_path, vf->listview);
516                 file_data_unref(fd);
517                 g_free(old_path);
518                 }
519         
520         g_free(new_path);
521
522         return FALSE;
523 }
524
525 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
526 {
527         ViewFile *vf = data;
528         GtkTreeModel *store;
529         GtkTreeIter iter;
530         GtkTreePath *tpath;
531         gint cw, ch;
532
533         if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
534         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
535         tpath = gtk_tree_model_get_path(store, &iter);
536         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
537         gtk_tree_path_free(tpath);
538         *y += ch;
539         popup_menu_position_clamp(menu, x, y, 0);
540 }
541
542 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
543 {
544         ViewFile *vf = data;
545         GtkTreePath *tpath;
546
547         if (event->keyval != GDK_Menu) return FALSE;
548
549         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
550         if (tpath)
551                 {
552                 GtkTreeModel *store;
553                 GtkTreeIter iter;
554
555                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
556                 gtk_tree_model_get_iter(store, &iter, tpath);
557                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
558                 gtk_tree_path_free(tpath);
559                 }
560         else
561                 {
562                 VFLIST(vf)->click_fd = NULL;
563                 }
564
565         vf->popup = vf_pop_menu(vf);
566         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
567
568         return TRUE;
569 }
570
571 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
572 {
573         ViewFile *vf = data;
574         GtkTreePath *tpath;
575         GtkTreeIter iter;
576         FileData *fd = NULL;
577         GtkTreeViewColumn *column;
578         
579         vf->clicked_mark = 0;
580
581         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
582                                           &tpath, &column, NULL, NULL))
583                 {
584                 GtkTreeModel *store;
585                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
586
587                 if (bevent->button == MOUSE_BUTTON_LEFT &&
588                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
589                         return FALSE;
590
591                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
592                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
593
594                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
595
596                 gtk_tree_model_get_iter(store, &iter, tpath);
597                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
598 #if 0
599                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
600 #endif
601                 gtk_tree_path_free(tpath);
602                 }
603
604         VFLIST(vf)->click_fd = fd;
605
606         if (bevent->button == MOUSE_BUTTON_RIGHT)
607                 {
608                 vf->popup = vf_pop_menu(vf);
609                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
610                                 bevent->button, bevent->time);
611                 return TRUE;
612                 }
613
614         if (!fd) return FALSE;
615
616         if (bevent->button == MOUSE_BUTTON_MIDDLE)
617                 {
618                 if (!vflist_row_is_selected(vf, fd))
619                         {
620                         vflist_color_set(vf, fd, TRUE);
621                         }
622                 return TRUE;
623                 }
624
625
626         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
627             !(bevent->state & GDK_SHIFT_MASK ) &&
628             !(bevent->state & GDK_CONTROL_MASK ) &&
629             vflist_row_is_selected(vf, fd))
630                 {
631                 GtkTreeSelection *selection;
632
633                 gtk_widget_grab_focus(widget);
634
635
636                 /* returning FALSE and further processing of the event is needed for
637                    correct operation of the expander, to show the sidecar files.
638                    It however resets the selection of multiple files. With this condition
639                    it should work for both cases */
640                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
641                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
642                 }
643
644 #if 1
645         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
646                 {
647                 if (vf->layout) layout_image_full_screen_start(vf->layout);
648                 }
649 #endif
650
651         return FALSE;
652 }
653
654 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
655 {
656         ViewFile *vf = data;
657         GtkTreePath *tpath;
658         GtkTreeIter iter;
659         FileData *fd = NULL;
660
661         if (bevent->button == MOUSE_BUTTON_MIDDLE)
662                 {
663                 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
664                 }
665
666         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
667                 {
668                 return TRUE;
669                 }
670
671         if ((bevent->x != 0 || bevent->y != 0) &&
672             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
673                                           &tpath, NULL, NULL, NULL))
674                 {
675                 GtkTreeModel *store;
676
677                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
678                 gtk_tree_model_get_iter(store, &iter, tpath);
679                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
680                 gtk_tree_path_free(tpath);
681                 }
682
683         if (bevent->button == MOUSE_BUTTON_MIDDLE)
684                 {
685                 if (fd && VFLIST(vf)->click_fd == fd)
686                         {
687                         GtkTreeSelection *selection;
688
689                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
690                         if (vflist_row_is_selected(vf, fd))
691                                 {
692                                 gtk_tree_selection_unselect_iter(selection, &iter);
693                                 }
694                         else
695                                 {
696                                 gtk_tree_selection_select_iter(selection, &iter);
697                                 }
698                         }
699                 return TRUE;
700                 }
701
702         if (fd && VFLIST(vf)->click_fd == fd &&
703             !(bevent->state & GDK_SHIFT_MASK ) &&
704             !(bevent->state & GDK_CONTROL_MASK ) &&
705             vflist_row_is_selected(vf, fd))
706                 {
707                 GtkTreeSelection *selection;
708
709                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
710                 gtk_tree_selection_unselect_all(selection);
711                 gtk_tree_selection_select_iter(selection, &iter);
712                 vflist_move_cursor(vf, &iter);
713 //              return TRUE;// FIXME - expand
714                 }
715
716         return FALSE;
717 }
718
719 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
720 {
721         FileData *read_ahead_fd = NULL;
722         gint row;
723         FileData *cur_fd;
724
725         if (!sel_fd) return;
726
727         cur_fd = layout_image_get_fd(vf->layout);
728         if (sel_fd == cur_fd) return; /* no change */
729
730         row = g_list_index(vf->list, sel_fd);
731         // FIXME sidecar data
732
733         if (sel_fd && options->image.enable_read_ahead && row >= 0)
734                 {
735                 if (row > g_list_index(vf->list, cur_fd) &&
736                     (guint) (row + 1) < vf_count(vf, NULL))
737                         {
738                         read_ahead_fd = vf_index_get_data(vf, row + 1);
739                         }
740                 else if (row > 0)
741                         {
742                         read_ahead_fd = vf_index_get_data(vf, row - 1);
743                         }
744                 }
745
746         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
747 }
748
749 static gboolean vflist_select_idle_cb(gpointer data)
750 {
751         ViewFile *vf = data;
752
753         if (!vf->layout)
754                 {
755                 VFLIST(vf)->select_idle_id = 0;
756                 return FALSE;
757                 }
758
759         vf_send_update(vf);
760
761         if (VFLIST(vf)->select_fd)
762                 {
763                 vflist_select_image(vf, VFLIST(vf)->select_fd);
764                 VFLIST(vf)->select_fd = NULL;
765                 }
766
767         VFLIST(vf)->select_idle_id = 0;
768         return FALSE;
769 }
770
771 static void vflist_select_idle_cancel(ViewFile *vf)
772 {
773         if (VFLIST(vf)->select_idle_id)
774                 {
775                 g_source_remove(VFLIST(vf)->select_idle_id);
776                 VFLIST(vf)->select_idle_id = 0;
777                 }
778 }
779
780 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
781                                  gboolean path_currently_selected, gpointer data)
782 {
783         ViewFile *vf = data;
784         GtkTreeIter iter;
785
786         if (!path_currently_selected &&
787             gtk_tree_model_get_iter(store, &iter, tpath))
788                 {
789                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
790                 }
791         else
792                 {
793                 VFLIST(vf)->select_fd = NULL;
794                 }
795
796         if (vf->layout &&
797             !VFLIST(vf)->select_idle_id)
798                 {
799                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
800                 }
801
802         return TRUE;
803 }
804
805 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
806 {
807         ViewFile *vf = data;
808         vflist_set_expanded(vf, iter, TRUE);
809 }
810
811 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
812 {
813         ViewFile *vf = data;
814         vflist_set_expanded(vf, iter, FALSE);
815 }
816
817 /*
818  *-----------------------------------------------------------------------------
819  * misc
820  *-----------------------------------------------------------------------------
821  */
822
823 /*
824 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
825                                         gboolean path_currently_selected, gpointer data)
826 {
827         return TRUE;
828 }
829 */
830
831 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
832  {
833         gboolean multiline = vflist_is_multiline(vf);
834         gchar *text;
835
836         if (multiline)
837                 {
838                 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
839                 }
840         else
841                 {
842                 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
843                 }
844         return text;
845 }
846
847 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
848 {
849         GtkTreeStore *store;
850         gchar *name;
851         gchar *sidecars;
852         gchar *size;
853         gchar *time;
854         gchar *formatted;
855
856         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
857
858         gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
859                                         FILE_COLUMN_NAME, &name,
860                                         FILE_COLUMN_SIDECARS, &sidecars,
861                                         FILE_COLUMN_SIZE, &size,
862                                         FILE_COLUMN_DATE, &time,
863                                         -1);
864         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
865
866         gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
867                                         FILE_COLUMN_EXPANDED, expanded,
868                                         -1);
869         g_free(time);
870         g_free(size);
871         g_free(sidecars);
872         g_free(name);
873         g_free(formatted);
874 }
875
876 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
877 {
878         gchar *size;
879         gchar *sidecars = NULL;
880         gchar *name;
881         const gchar *time = text_from_time(fd->date);
882         gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
883         const gchar *disabled_grouping;
884         gchar *formatted;
885         gboolean expanded = FALSE;
886         
887         if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
888                 {
889                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
890                 }
891
892         sidecars = file_data_sc_list_to_string(fd);
893
894         disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
895         name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
896         size = text_from_size(fd->size);
897         
898         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
899         
900         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
901                                         FILE_COLUMN_VERSION, fd->version,
902                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
903                                         FILE_COLUMN_FORMATTED, formatted,
904                                         FILE_COLUMN_SIDECARS, sidecars,
905                                         FILE_COLUMN_NAME, name,
906                                         FILE_COLUMN_SIZE, size,
907                                         FILE_COLUMN_DATE, time,
908 #define STORE_SET_IS_SLOW 1
909 #if STORE_SET_IS_SLOW
910 /* this is 3x faster on a directory with 20000 files */
911                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
912                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
913                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
914                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
915                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
916                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
917 #if FILEDATA_MARKS_SIZE != 6
918 #error this needs to be updated
919 #endif
920 #endif
921                                         FILE_COLUMN_COLOR, FALSE, -1);
922
923 #if !STORE_SET_IS_SLOW
924         {
925         gint i;
926         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
927                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
928         }
929 #endif
930         g_free(size);
931         g_free(sidecars);
932         g_free(name);
933         g_free(formatted);
934 }
935
936 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
937 {
938         GList *work;
939         GtkTreeIter iter;
940         gboolean valid;
941         gint num_ordered = 0;
942         gint num_prepended = 0;
943
944         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
945
946         work = list;
947         while (work)
948                 {
949                 gint match;
950                 FileData *fd = work->data;
951                 gboolean done = FALSE;
952
953                 while (!done)
954                         {
955                         FileData *old_fd = NULL;
956                         gint old_version = 0;
957
958                         if (valid)
959                                 {
960                                 num_ordered++;
961                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
962                                                    FILE_COLUMN_POINTER, &old_fd,
963                                                    FILE_COLUMN_VERSION, &old_version,
964                                                    -1);
965
966                                 if (fd == old_fd)
967                                         {
968                                         match = 0;
969                                         }
970                                 else
971                                         {
972                                         if (parent_iter)
973                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
974                                         else
975                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
976
977                                         if (match == 0) g_warning("multiple fd for the same path");
978                                         }
979                                         
980                                 }
981                         else
982                                 {
983                                 match = -1;
984                                 }
985
986                         if (match < 0)
987                                 {
988                                 GtkTreeIter new;
989
990                                 if (valid)
991                                         {
992                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
993                                         }
994                                 else
995                                         {
996                                         /*
997                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
998                                             and it seems to be much faster to add new entries to the beginning and reorder later
999                                         */
1000                                         num_prepended++;
1001                                         gtk_tree_store_prepend(store, &new, parent_iter);
1002                                         }
1003
1004                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1005                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
1006                                 
1007                                 if (g_list_find(selected, fd))
1008                                         {
1009                                         /* renamed files - the same fd appears at different position - select it again*/
1010                                         GtkTreeSelection *selection;
1011                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1012                                         gtk_tree_selection_select_iter(selection, &new);
1013                                         }
1014
1015                                 done = TRUE;
1016                                 }
1017                         else if (match > 0)
1018                                 {
1019                                 file_data_unref(old_fd);
1020                                 valid = gtk_tree_store_remove(store, &iter);
1021                                 }
1022                         else
1023                                 {
1024                                 if (fd->version != old_version)
1025                                         {
1026                                         vflist_setup_iter(vf, store, &iter, fd);
1027                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
1028                                         }
1029
1030                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1031
1032                                 done = TRUE;
1033                                 }
1034                         }
1035                 work = work->next;
1036                 }
1037
1038         while (valid)
1039                 {
1040                 FileData *old_fd;
1041                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1042                 file_data_unref(old_fd);
1043
1044                 valid = gtk_tree_store_remove(store, &iter);
1045                 }
1046                 
1047         /* move the prepended entries to the correct position */
1048         if (num_prepended)
1049                 {
1050                 gint i;
1051                 gint num_total = num_prepended + num_ordered;
1052                 gint *new_order = g_malloc(num_total * sizeof(gint));
1053                 
1054                 for (i = 0; i < num_total; i++)
1055                         {
1056                         if (i < num_ordered)
1057                                 new_order[i] = num_prepended + i;
1058                         else
1059                                 new_order[i] = num_total - 1 - i;
1060                         }
1061                 gtk_tree_store_reorder(store, parent_iter, new_order);
1062
1063                 g_free(new_order);
1064                 }
1065 }
1066
1067 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1068 {
1069         gint i;
1070         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1071         gint *new_order;
1072         GtkTreeStore *store;
1073         GList *work;
1074
1075         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1076         if (!vf->list) return;
1077
1078         work = vf->list;
1079         i = 0;
1080         while (work)
1081                 {
1082                 FileData *fd = work->data;
1083                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1084                 i++;
1085                 work = work->next;
1086                 }
1087
1088         vf->sort_method = type;
1089         vf->sort_ascend = ascend;
1090
1091         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1092
1093         new_order = g_malloc(i * sizeof(gint));
1094
1095         work = vf->list;
1096         i = 0;
1097         while (work)
1098                 {
1099                 FileData *fd = work->data;
1100                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1101                 i++;
1102                 work = work->next;
1103                 }
1104
1105         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1106         gtk_tree_store_reorder(store, NULL, new_order);
1107
1108         g_free(new_order);
1109         g_hash_table_destroy(fd_idx_hash);
1110 }
1111
1112 /*
1113  *-----------------------------------------------------------------------------
1114  * thumb updates
1115  *-----------------------------------------------------------------------------
1116  */
1117
1118
1119 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1120 {
1121         GList *work = list;
1122         while (work)
1123                 {
1124                 FileData *fd = work->data;
1125                 work = work->next;
1126
1127                 if (fd->thumb_pixbuf) (*done)++;
1128                 
1129                 if (fd->sidecar_files)
1130                         {
1131                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
1132                         }
1133                 (*count)++;
1134                 }
1135 }
1136
1137 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1138 {
1139         GtkTreeStore *store;
1140         GtkTreeIter iter;
1141
1142         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1143
1144         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1145         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1146 }
1147
1148 FileData *vflist_thumb_next_fd(ViewFile *vf)
1149 {
1150         GtkTreePath *tpath;
1151         FileData *fd = NULL;
1152
1153         /* first check the visible files */
1154
1155         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1156                 {
1157                 GtkTreeModel *store;
1158                 GtkTreeIter iter;
1159                 gboolean valid = TRUE;
1160         
1161                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1162                 gtk_tree_model_get_iter(store, &iter, tpath);
1163                 gtk_tree_path_free(tpath);
1164
1165                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1166                         {
1167                         FileData *nfd;
1168
1169                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1170
1171                         if (!nfd->thumb_pixbuf) fd = nfd;
1172
1173                         valid = gtk_tree_model_iter_next(store, &iter);
1174                         }
1175                 }
1176
1177         /* then find first undone */
1178
1179         if (!fd)
1180                 {
1181                 GList *work = vf->list;
1182                 while (work && !fd)
1183                         {
1184                         FileData *fd_p = work->data;
1185                         if (!fd_p->thumb_pixbuf)
1186                                 fd = fd_p;
1187                         else
1188                                 {
1189                                 GList *work2 = fd_p->sidecar_files;
1190
1191                                 while (work2 && !fd)
1192                                         {
1193                                         fd_p = work2->data;
1194                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1195                                         work2 = work2->next;
1196                                         }
1197                                 }
1198                         work = work->next;
1199                         }
1200                 }
1201
1202         return fd;
1203 }
1204
1205
1206 void vflist_thumb_reset_all(ViewFile *vf)
1207 {
1208         GList *work = vf->list;
1209         while (work)
1210                 {
1211                 FileData *fd = work->data;
1212                 if (fd->thumb_pixbuf)
1213                         {
1214                         g_object_unref(fd->thumb_pixbuf);
1215                         fd->thumb_pixbuf = NULL;
1216                         }
1217                 work = work->next;
1218                 }
1219 }
1220
1221 /*
1222  *-----------------------------------------------------------------------------
1223  * row stuff
1224  *-----------------------------------------------------------------------------
1225  */
1226
1227 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1228 {
1229         return g_list_nth_data(vf->list, row);
1230 }
1231
1232 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1233 {
1234         gint p = 0;
1235         GList *work, *work2;
1236
1237         work = vf->list;
1238         while (work)
1239                 {
1240                 FileData *list_fd = work->data;
1241                 if (list_fd == fd) return p;
1242                 
1243                 work2 = list_fd->sidecar_files;
1244                 while (work2)
1245                         {
1246                         /* FIXME: return the same index also for sidecars
1247                            it is sufficient for next/prev navigation but it should be rewritten 
1248                            without using indexes at all
1249                         */
1250                         FileData *sidecar_fd = work2->data;
1251                         if (sidecar_fd == fd) return p;
1252                         work2 = work2->next;
1253                         }
1254                 
1255                 work = work->next;
1256                 p++;
1257                 }
1258
1259         return -1;
1260 }
1261
1262 guint vflist_count(ViewFile *vf, gint64 *bytes)
1263 {
1264         if (bytes)
1265                 {
1266                 gint64 b = 0;
1267                 GList *work;
1268
1269                 work = vf->list;
1270                 while (work)
1271                         {
1272                         FileData *fd = work->data;
1273                         work = work->next;
1274                         b += fd->size;
1275                         }
1276
1277                 *bytes = b;
1278                 }
1279
1280         return g_list_length(vf->list);
1281 }
1282
1283 GList *vflist_get_list(ViewFile *vf)
1284 {
1285         GList *list = NULL;
1286         GList *work;
1287
1288         work = vf->list;
1289         while (work)
1290                 {
1291                 FileData *fd = work->data;
1292                 work = work->next;
1293
1294                 list = g_list_prepend(list, file_data_ref(fd));
1295                 }
1296
1297         return g_list_reverse(list);
1298 }
1299
1300 /*
1301  *-----------------------------------------------------------------------------
1302  * selections
1303  *-----------------------------------------------------------------------------
1304  */
1305
1306 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1307 {
1308         GtkTreeModel *store;
1309         GtkTreeSelection *selection;
1310         GList *slist;
1311         GList *work;
1312         gboolean found = FALSE;
1313
1314         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1315         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1316         work = slist;
1317         while (!found && work)
1318                 {
1319                 GtkTreePath *tpath = work->data;
1320                 FileData *fd_n;
1321                 GtkTreeIter iter;
1322
1323                 gtk_tree_model_get_iter(store, &iter, tpath);
1324                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1325                 if (fd_n == fd) found = TRUE;
1326                 work = work->next;
1327                 }
1328         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1329         g_list_free(slist);
1330
1331         return found;
1332 }
1333
1334 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1335 {
1336         FileData *fd;
1337
1338         fd = vf_index_get_data(vf, row);
1339         return vflist_row_is_selected(vf, fd);
1340 }
1341
1342 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1343 {
1344         GtkTreeModel *store;
1345         GtkTreeSelection *selection;
1346         GList *slist;
1347         guint count;
1348
1349         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1350         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1351
1352         if (bytes)
1353                 {
1354                 gint64 b = 0;
1355                 GList *work;
1356
1357                 work = slist;
1358                 while (work)
1359                         {
1360                         GtkTreePath *tpath = work->data;
1361                         GtkTreeIter iter;
1362                         FileData *fd;
1363
1364                         gtk_tree_model_get_iter(store, &iter, tpath);
1365                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1366                         b += fd->size;
1367
1368                         work = work->next;
1369                         }
1370
1371                 *bytes = b;
1372                 }
1373
1374         count = g_list_length(slist);
1375         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1376         g_list_free(slist);
1377
1378         return count;
1379 }
1380
1381 GList *vflist_selection_get_list(ViewFile *vf)
1382 {
1383         GtkTreeModel *store;
1384         GtkTreeSelection *selection;
1385         GList *slist;
1386         GList *list = NULL;
1387         GList *work;
1388
1389         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1390         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1391         work = slist;
1392         while (work)
1393                 {
1394                 GtkTreePath *tpath = work->data;
1395                 FileData *fd;
1396                 GtkTreeIter iter;
1397
1398                 gtk_tree_model_get_iter(store, &iter, tpath);
1399                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1400
1401                 list = g_list_prepend(list, file_data_ref(fd));
1402                 
1403                 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1404                         {
1405                         /* unexpanded - add whole group */
1406                         GList *work2 = fd->sidecar_files;
1407                         while (work2)
1408                                 {
1409                                 FileData *sfd = work2->data;
1410                                 list = g_list_prepend(list, file_data_ref(sfd));
1411                                 work2 = work2->next;
1412                                 }
1413                         }
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                 /* the change has a very limited range and the standard notification would trigger
1647                    complete re-read of the directory - try to do only minimal update instead */
1648                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1649
1650                 switch (mode)
1651                         {
1652                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1653                                 break;
1654                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1655                                 break;
1656                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1657                                 break;
1658                         }
1659                 
1660                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1661                         {
1662                         vf_refresh_idle(vf);
1663                         }
1664                 else
1665                         {
1666                         /* mark functions can have various side effects - update all columns to be sure */
1667                         vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1668                         /* mark functions can change sidecars too */
1669                         vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1670                         }
1671
1672                 
1673                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1674
1675                 work = work->next;
1676                 }
1677         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1678         g_list_free(slist);
1679 }
1680
1681 /*
1682  *-----------------------------------------------------------------------------
1683  * core (population)
1684  *-----------------------------------------------------------------------------
1685  */
1686
1687 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1688 {
1689         GtkTreeViewColumn *column;
1690         GtkCellRenderer *cell;
1691         GList *list;
1692
1693         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1694         if (!column) return;
1695
1696         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1697
1698         list = gtk_tree_view_column_get_cell_renderers(column);
1699         if (!list) return;
1700         cell = list->data;
1701         g_list_free(list);
1702
1703         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1704         gtk_tree_view_column_set_visible(column, thumb);
1705
1706         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1707         if (!column) return;
1708         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_SIZE);
1711         if (!column) return;
1712         gtk_tree_view_column_set_visible(column, !multiline);
1713
1714         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1715         if (!column) return;
1716         gtk_tree_view_column_set_visible(column, !multiline);
1717
1718         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1719 }
1720
1721 static gboolean vflist_is_multiline(ViewFile *vf)
1722 {
1723         return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1724 }
1725
1726
1727 static void vflist_populate_view(ViewFile *vf)
1728 {
1729         GtkTreeStore *store;
1730         GList *selected;
1731
1732         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1733
1734         vf_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, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
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         vf_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         
1901         /* the change has a very limited range and the standard notification would trigger
1902            complete re-read of the directory - try to do only minimal update instead */
1903         file_data_unregister_notify_func(vf_notify_cb, vf);
1904         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1905         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1906                 {
1907                 vf_refresh_idle(vf);
1908                 }
1909         else
1910                 {
1911                 /* mark functions can have various side effects - update all columns to be sure */
1912                 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1913                 /* mark functions can change sidecars too */
1914                 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1915                 }
1916         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1917
1918         gtk_tree_path_free(path);
1919 }
1920
1921 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1922 {
1923         GtkTreeViewColumn *column;
1924         GtkCellRenderer *renderer;
1925         GtkTreeStore *store;
1926         gint index;
1927
1928         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1929
1930         renderer = gtk_cell_renderer_toggle_new();
1931         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1932
1933         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1934         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1935         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1936
1937         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1938         gtk_tree_view_column_set_fixed_width(column, 22);
1939         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1940
1941
1942         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1943 }
1944
1945 /*
1946  *-----------------------------------------------------------------------------
1947  * base
1948  *-----------------------------------------------------------------------------
1949  */
1950
1951 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1952 {
1953         if (!dir_fd) return FALSE;
1954         if (vf->dir_fd == dir_fd) return TRUE;
1955
1956         file_data_unref(vf->dir_fd);
1957         vf->dir_fd = file_data_ref(dir_fd);
1958
1959         /* force complete reload */
1960         vflist_store_clear(vf);
1961
1962         filelist_free(vf->list);
1963         vf->list = NULL;
1964
1965         return vf_refresh(vf);
1966 }
1967
1968 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1969 {
1970         ViewFile *vf = data;
1971
1972         file_data_unregister_notify_func(vf_notify_cb, vf);
1973
1974         vflist_select_idle_cancel(vf);
1975         vf_refresh_idle_cancel(vf);
1976         vf_thumb_stop(vf);
1977
1978         filelist_free(vf->list);
1979 }
1980
1981 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1982 {
1983         GtkTreeStore *store;
1984         GtkTreeSelection *selection;
1985         GType flist_types[FILE_COLUMN_COUNT];
1986         gint i;
1987         gint column;
1988
1989         vf->info = g_new0(ViewFileInfoList, 1);
1990         
1991         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1992         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1993         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1994         flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1995         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1996         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1997         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1998         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1999         flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2000         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2001         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2002                 flist_types[i] = G_TYPE_BOOLEAN;
2003
2004         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2005
2006         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2007         g_object_unref(store);
2008
2009         g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2010                          G_CALLBACK(vflist_expand_cb), vf);
2011
2012         g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2013                          G_CALLBACK(vflist_collapse_cb), vf);
2014
2015         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2016         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2017         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2018
2019         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2020         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2021
2022         column = 0;
2023
2024         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2025                 {
2026                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2027                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2028                 column++;
2029                 }
2030
2031         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2032         g_assert(column == FILE_VIEW_COLUMN_THUMB);
2033         column++;
2034         
2035         vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2036         g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2037         column++;
2038
2039         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2040         g_assert(column == FILE_VIEW_COLUMN_SIZE);
2041         column++;
2042
2043         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2044         g_assert(column == FILE_VIEW_COLUMN_DATE);
2045         column++;
2046
2047         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2048         return vf;
2049 }
2050
2051 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2052 {
2053         if (VFLIST(vf)->thumbs_enabled == enable) return;
2054
2055         VFLIST(vf)->thumbs_enabled = enable;
2056         if (vf->layout) vf_refresh(vf);
2057 }
2058
2059 void vflist_marks_set(ViewFile *vf, gboolean enable)
2060 {
2061         GList *columns, *work;
2062
2063         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2064
2065         work = columns;
2066         while (work)
2067                 {
2068                 GtkTreeViewColumn *column = work->data;
2069                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2070                 work = work->next;
2071
2072                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2073                         gtk_tree_view_column_set_visible(column, enable);
2074                 }
2075
2076         g_list_free(columns);
2077         //vf_refresh(vf);
2078 }
2079
2080 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */