consider sidecars in layout_image popup menu
[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, gboolean force);
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 #if 0
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 #endif
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_selection_get_one(ViewFile *vf, FileData *fd)
367 {
368         GList *list = g_list_append(NULL, file_data_ref(fd));
369
370         if (fd->sidecar_files)
371                 {
372                 /* check if the row is expanded */
373                 GtkTreeModel *store;
374                 GtkTreeIter iter;
375                 
376                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
377                 if (vflist_find_row(vf, fd, &iter) >= 0)
378                         {
379                         GtkTreePath *tpath;
380
381                         tpath = gtk_tree_model_get_path(store, &iter);
382                         if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
383                                 {
384                                 /* unexpanded - add whole group */
385                                 GList *work = fd->sidecar_files;
386                                 while (work)
387                                         {
388                                         FileData *sfd = work->data;
389                                         list = g_list_prepend(list, file_data_ref(sfd));
390                                         work = work->next;
391                                         }
392                                 }
393                         gtk_tree_path_free(tpath);
394                         }
395                 list = g_list_reverse(list);
396                 }
397
398         return list;
399 }
400
401 GList *vflist_pop_menu_file_list(ViewFile *vf)
402 {
403         if (!VFLIST(vf)->click_fd) return NULL;
404
405         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
406                 {
407                 return vf_selection_get_list(vf);
408                 }
409         return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
410 }
411
412
413 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
414 {
415         ViewFile *vf = data;
416
417         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
418                 {
419                 GList *list;
420
421                 list = vf_selection_get_list(vf);
422                 view_window_new_from_list(list);
423                 filelist_free(list);
424                 }
425         else
426                 {
427                 view_window_new(VFLIST(vf)->click_fd);
428                 }
429 }
430
431 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
432 {
433         ViewFile *vf = data;
434         GList *list;
435
436         list = vf_pop_menu_file_list(vf);
437         if (options->file_ops.enable_in_place_rename &&
438             list && !list->next && VFLIST(vf)->click_fd)
439                 {
440                 GtkTreeModel *store;
441                 GtkTreeIter iter;
442
443                 filelist_free(list);
444
445                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
446                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
447                         {
448                         GtkTreePath *tpath;
449
450                         tpath = gtk_tree_model_get_path(store, &iter);
451                         tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
452                                           FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
453                                           vflist_row_rename_cb, vf);
454                         gtk_tree_path_free(tpath);
455                         }
456                 return;
457                 }
458
459         file_util_rename(NULL, list, vf->listview);
460 }
461
462 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
463 {
464         ViewFile *vf = data;
465
466         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
467         if (vf->layout)
468                 {
469                 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
470                 }
471         else
472                 {
473                 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
474                 }
475 }
476
477 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
478 {
479         ViewFile *vf = data;
480
481         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
482         vf_refresh(vf);
483         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
484 }
485
486 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
487 {
488         ViewFile *vf = data;
489         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
490         VFLIST(vf)->click_fd = NULL;
491         vf->popup = NULL;
492 }
493
494
495 /*
496  *-----------------------------------------------------------------------------
497  * callbacks
498  *-----------------------------------------------------------------------------
499  */
500
501 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
502 {
503         ViewFile *vf = data;
504         gchar *new_path;
505
506         if (!new || !new[0]) return FALSE;
507
508         new_path = g_build_filename(vf->dir_fd->path, new, NULL);
509
510         if (strchr(new, G_DIR_SEPARATOR) != NULL)
511                 {
512                 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
513                 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
514                 g_free(text);
515                 }
516         else
517                 {
518                 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
519                 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
520                 file_util_rename_simple(fd, new_path, vf->listview);
521                 file_data_unref(fd);
522                 g_free(old_path);
523                 }
524         
525         g_free(new_path);
526
527         return FALSE;
528 }
529
530 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
531 {
532         ViewFile *vf = data;
533         GtkTreeModel *store;
534         GtkTreeIter iter;
535         GtkTreePath *tpath;
536         gint cw, ch;
537
538         if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
539         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
540         tpath = gtk_tree_model_get_path(store, &iter);
541         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
542         gtk_tree_path_free(tpath);
543         *y += ch;
544         popup_menu_position_clamp(menu, x, y, 0);
545 }
546
547 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
548 {
549         ViewFile *vf = data;
550         GtkTreePath *tpath;
551
552         if (event->keyval != GDK_Menu) return FALSE;
553
554         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
555         if (tpath)
556                 {
557                 GtkTreeModel *store;
558                 GtkTreeIter iter;
559
560                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
561                 gtk_tree_model_get_iter(store, &iter, tpath);
562                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
563                 gtk_tree_path_free(tpath);
564                 }
565         else
566                 {
567                 VFLIST(vf)->click_fd = NULL;
568                 }
569
570         vf->popup = vf_pop_menu(vf);
571         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
572
573         return TRUE;
574 }
575
576 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
577 {
578         ViewFile *vf = data;
579         GtkTreePath *tpath;
580         GtkTreeIter iter;
581         FileData *fd = NULL;
582         GtkTreeViewColumn *column;
583         
584         vf->clicked_mark = 0;
585
586         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
587                                           &tpath, &column, NULL, NULL))
588                 {
589                 GtkTreeModel *store;
590                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
591
592                 if (bevent->button == MOUSE_BUTTON_LEFT &&
593                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
594                         return FALSE;
595
596                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
597                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
598
599                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
600
601                 gtk_tree_model_get_iter(store, &iter, tpath);
602                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
603 #if 0
604                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
605 #endif
606                 gtk_tree_path_free(tpath);
607                 }
608
609         VFLIST(vf)->click_fd = fd;
610
611         if (bevent->button == MOUSE_BUTTON_RIGHT)
612                 {
613                 vf->popup = vf_pop_menu(vf);
614                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
615                                 bevent->button, bevent->time);
616                 return TRUE;
617                 }
618
619         if (!fd) return FALSE;
620
621         if (bevent->button == MOUSE_BUTTON_MIDDLE)
622                 {
623                 if (!vflist_row_is_selected(vf, fd))
624                         {
625                         vflist_color_set(vf, fd, TRUE);
626                         }
627                 return TRUE;
628                 }
629
630
631         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
632             !(bevent->state & GDK_SHIFT_MASK ) &&
633             !(bevent->state & GDK_CONTROL_MASK ) &&
634             vflist_row_is_selected(vf, fd))
635                 {
636                 GtkTreeSelection *selection;
637
638                 gtk_widget_grab_focus(widget);
639
640
641                 /* returning FALSE and further processing of the event is needed for
642                    correct operation of the expander, to show the sidecar files.
643                    It however resets the selection of multiple files. With this condition
644                    it should work for both cases */
645                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
646                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
647                 }
648
649 #if 1
650         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
651                 {
652                 if (vf->layout) layout_image_full_screen_start(vf->layout);
653                 }
654 #endif
655
656         return FALSE;
657 }
658
659 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
660 {
661         ViewFile *vf = data;
662         GtkTreePath *tpath;
663         GtkTreeIter iter;
664         FileData *fd = NULL;
665
666         if (bevent->button == MOUSE_BUTTON_MIDDLE)
667                 {
668                 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
669                 }
670
671         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
672                 {
673                 return TRUE;
674                 }
675
676         if ((bevent->x != 0 || bevent->y != 0) &&
677             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
678                                           &tpath, NULL, NULL, NULL))
679                 {
680                 GtkTreeModel *store;
681
682                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
683                 gtk_tree_model_get_iter(store, &iter, tpath);
684                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
685                 gtk_tree_path_free(tpath);
686                 }
687
688         if (bevent->button == MOUSE_BUTTON_MIDDLE)
689                 {
690                 if (fd && VFLIST(vf)->click_fd == fd)
691                         {
692                         GtkTreeSelection *selection;
693
694                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
695                         if (vflist_row_is_selected(vf, fd))
696                                 {
697                                 gtk_tree_selection_unselect_iter(selection, &iter);
698                                 }
699                         else
700                                 {
701                                 gtk_tree_selection_select_iter(selection, &iter);
702                                 }
703                         }
704                 return TRUE;
705                 }
706
707         if (fd && VFLIST(vf)->click_fd == fd &&
708             !(bevent->state & GDK_SHIFT_MASK ) &&
709             !(bevent->state & GDK_CONTROL_MASK ) &&
710             vflist_row_is_selected(vf, fd))
711                 {
712                 GtkTreeSelection *selection;
713
714                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
715                 gtk_tree_selection_unselect_all(selection);
716                 gtk_tree_selection_select_iter(selection, &iter);
717                 vflist_move_cursor(vf, &iter);
718 //              return TRUE;// FIXME - expand
719                 }
720
721         return FALSE;
722 }
723
724 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
725 {
726         FileData *read_ahead_fd = NULL;
727         gint row;
728         FileData *cur_fd;
729
730         if (!sel_fd) return;
731
732         cur_fd = layout_image_get_fd(vf->layout);
733         if (sel_fd == cur_fd) return; /* no change */
734
735         row = g_list_index(vf->list, sel_fd);
736         // FIXME sidecar data
737
738         if (sel_fd && options->image.enable_read_ahead && row >= 0)
739                 {
740                 if (row > g_list_index(vf->list, cur_fd) &&
741                     (guint) (row + 1) < vf_count(vf, NULL))
742                         {
743                         read_ahead_fd = vf_index_get_data(vf, row + 1);
744                         }
745                 else if (row > 0)
746                         {
747                         read_ahead_fd = vf_index_get_data(vf, row - 1);
748                         }
749                 }
750
751         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
752 }
753
754 static gboolean vflist_select_idle_cb(gpointer data)
755 {
756         ViewFile *vf = data;
757
758         if (!vf->layout)
759                 {
760                 VFLIST(vf)->select_idle_id = 0;
761                 return FALSE;
762                 }
763
764         vf_send_update(vf);
765
766         if (VFLIST(vf)->select_fd)
767                 {
768                 vflist_select_image(vf, VFLIST(vf)->select_fd);
769                 VFLIST(vf)->select_fd = NULL;
770                 }
771
772         VFLIST(vf)->select_idle_id = 0;
773         return FALSE;
774 }
775
776 static void vflist_select_idle_cancel(ViewFile *vf)
777 {
778         if (VFLIST(vf)->select_idle_id)
779                 {
780                 g_source_remove(VFLIST(vf)->select_idle_id);
781                 VFLIST(vf)->select_idle_id = 0;
782                 }
783 }
784
785 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
786                                  gboolean path_currently_selected, gpointer data)
787 {
788         ViewFile *vf = data;
789         GtkTreeIter iter;
790
791         if (!path_currently_selected &&
792             gtk_tree_model_get_iter(store, &iter, tpath))
793                 {
794                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
795                 }
796         else
797                 {
798                 VFLIST(vf)->select_fd = NULL;
799                 }
800
801         if (vf->layout &&
802             !VFLIST(vf)->select_idle_id)
803                 {
804                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
805                 }
806
807         return TRUE;
808 }
809
810 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
811 {
812         ViewFile *vf = data;
813         vflist_set_expanded(vf, iter, TRUE);
814 }
815
816 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
817 {
818         ViewFile *vf = data;
819         vflist_set_expanded(vf, iter, FALSE);
820 }
821
822 /*
823  *-----------------------------------------------------------------------------
824  * misc
825  *-----------------------------------------------------------------------------
826  */
827
828 /*
829 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
830                                         gboolean path_currently_selected, gpointer data)
831 {
832         return TRUE;
833 }
834 */
835
836 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
837  {
838         gboolean multiline = vflist_is_multiline(vf);
839         gchar *text;
840
841         if (multiline)
842                 {
843                 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
844                 }
845         else
846                 {
847                 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
848                 }
849         return text;
850 }
851
852 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
853 {
854         GtkTreeStore *store;
855         gchar *name;
856         gchar *sidecars;
857         gchar *size;
858         gchar *time;
859         gchar *formatted;
860
861         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
862
863         gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
864                                         FILE_COLUMN_NAME, &name,
865                                         FILE_COLUMN_SIDECARS, &sidecars,
866                                         FILE_COLUMN_SIZE, &size,
867                                         FILE_COLUMN_DATE, &time,
868                                         -1);
869         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
870
871         gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
872                                         FILE_COLUMN_EXPANDED, expanded,
873                                         -1);
874         g_free(time);
875         g_free(size);
876         g_free(sidecars);
877         g_free(name);
878         g_free(formatted);
879 }
880
881 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
882 {
883         gchar *size;
884         gchar *sidecars = NULL;
885         gchar *name;
886         const gchar *time = text_from_time(fd->date);
887         gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
888         const gchar *disabled_grouping;
889         gchar *formatted;
890         gboolean expanded = FALSE;
891         
892         if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
893                 {
894                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
895                 }
896
897         sidecars = file_data_sc_list_to_string(fd);
898
899         disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
900         name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
901         size = text_from_size(fd->size);
902         
903         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
904         
905         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
906                                         FILE_COLUMN_VERSION, fd->version,
907                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
908                                         FILE_COLUMN_FORMATTED, formatted,
909                                         FILE_COLUMN_SIDECARS, sidecars,
910                                         FILE_COLUMN_NAME, name,
911                                         FILE_COLUMN_SIZE, size,
912                                         FILE_COLUMN_DATE, time,
913 #define STORE_SET_IS_SLOW 1
914 #if STORE_SET_IS_SLOW
915 /* this is 3x faster on a directory with 20000 files */
916                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
917                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
918                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
919                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
920                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
921                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
922 #if FILEDATA_MARKS_SIZE != 6
923 #error this needs to be updated
924 #endif
925 #endif
926                                         FILE_COLUMN_COLOR, FALSE, -1);
927
928 #if !STORE_SET_IS_SLOW
929         {
930         gint i;
931         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
932                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
933         }
934 #endif
935         g_free(size);
936         g_free(sidecars);
937         g_free(name);
938         g_free(formatted);
939 }
940
941 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
942 {
943         GList *work;
944         GtkTreeIter iter;
945         gboolean valid;
946         gint num_ordered = 0;
947         gint num_prepended = 0;
948
949         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
950
951         work = list;
952         while (work)
953                 {
954                 gint match;
955                 FileData *fd = work->data;
956                 gboolean done = FALSE;
957
958                 while (!done)
959                         {
960                         FileData *old_fd = NULL;
961                         gint old_version = 0;
962
963                         if (valid)
964                                 {
965                                 num_ordered++;
966                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
967                                                    FILE_COLUMN_POINTER, &old_fd,
968                                                    FILE_COLUMN_VERSION, &old_version,
969                                                    -1);
970
971                                 if (fd == old_fd)
972                                         {
973                                         match = 0;
974                                         }
975                                 else
976                                         {
977                                         if (parent_iter)
978                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
979                                         else
980                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
981
982                                         if (match == 0) g_warning("multiple fd for the same path");
983                                         }
984                                         
985                                 }
986                         else
987                                 {
988                                 match = -1;
989                                 }
990
991                         if (match < 0)
992                                 {
993                                 GtkTreeIter new;
994
995                                 if (valid)
996                                         {
997                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
998                                         }
999                                 else
1000                                         {
1001                                         /*
1002                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
1003                                             and it seems to be much faster to add new entries to the beginning and reorder later
1004                                         */
1005                                         num_prepended++;
1006                                         gtk_tree_store_prepend(store, &new, parent_iter);
1007                                         }
1008
1009                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1010                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1011                                 
1012                                 if (g_list_find(selected, fd))
1013                                         {
1014                                         /* renamed files - the same fd appears at different position - select it again*/
1015                                         GtkTreeSelection *selection;
1016                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1017                                         gtk_tree_selection_select_iter(selection, &new);
1018                                         }
1019
1020                                 done = TRUE;
1021                                 }
1022                         else if (match > 0)
1023                                 {
1024                                 file_data_unref(old_fd);
1025                                 valid = gtk_tree_store_remove(store, &iter);
1026                                 }
1027                         else
1028                                 {
1029                                 if (fd->version != old_version || force)
1030                                         {
1031                                         vflist_setup_iter(vf, store, &iter, fd);
1032                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1033                                         }
1034
1035                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1036
1037                                 done = TRUE;
1038                                 }
1039                         }
1040                 work = work->next;
1041                 }
1042
1043         while (valid)
1044                 {
1045                 FileData *old_fd;
1046                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1047                 file_data_unref(old_fd);
1048
1049                 valid = gtk_tree_store_remove(store, &iter);
1050                 }
1051                 
1052         /* move the prepended entries to the correct position */
1053         if (num_prepended)
1054                 {
1055                 gint i;
1056                 gint num_total = num_prepended + num_ordered;
1057                 gint *new_order = g_malloc(num_total * sizeof(gint));
1058                 
1059                 for (i = 0; i < num_total; i++)
1060                         {
1061                         if (i < num_ordered)
1062                                 new_order[i] = num_prepended + i;
1063                         else
1064                                 new_order[i] = num_total - 1 - i;
1065                         }
1066                 gtk_tree_store_reorder(store, parent_iter, new_order);
1067
1068                 g_free(new_order);
1069                 }
1070 }
1071
1072 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1073 {
1074         gint i;
1075         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1076         gint *new_order;
1077         GtkTreeStore *store;
1078         GList *work;
1079
1080         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1081         if (!vf->list) return;
1082
1083         work = vf->list;
1084         i = 0;
1085         while (work)
1086                 {
1087                 FileData *fd = work->data;
1088                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1089                 i++;
1090                 work = work->next;
1091                 }
1092
1093         vf->sort_method = type;
1094         vf->sort_ascend = ascend;
1095
1096         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1097
1098         new_order = g_malloc(i * sizeof(gint));
1099
1100         work = vf->list;
1101         i = 0;
1102         while (work)
1103                 {
1104                 FileData *fd = work->data;
1105                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1106                 i++;
1107                 work = work->next;
1108                 }
1109
1110         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1111         gtk_tree_store_reorder(store, NULL, new_order);
1112
1113         g_free(new_order);
1114         g_hash_table_destroy(fd_idx_hash);
1115 }
1116
1117 /*
1118  *-----------------------------------------------------------------------------
1119  * thumb updates
1120  *-----------------------------------------------------------------------------
1121  */
1122
1123
1124 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1125 {
1126         GList *work = list;
1127         while (work)
1128                 {
1129                 FileData *fd = work->data;
1130                 work = work->next;
1131
1132                 if (fd->thumb_pixbuf) (*done)++;
1133                 
1134                 if (fd->sidecar_files)
1135                         {
1136                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
1137                         }
1138                 (*count)++;
1139                 }
1140 }
1141
1142 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1143 {
1144         GtkTreeStore *store;
1145         GtkTreeIter iter;
1146
1147         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1148
1149         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1150         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1151 }
1152
1153 FileData *vflist_thumb_next_fd(ViewFile *vf)
1154 {
1155         GtkTreePath *tpath;
1156         FileData *fd = NULL;
1157
1158         /* first check the visible files */
1159
1160         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1161                 {
1162                 GtkTreeModel *store;
1163                 GtkTreeIter iter;
1164                 gboolean valid = TRUE;
1165         
1166                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1167                 gtk_tree_model_get_iter(store, &iter, tpath);
1168                 gtk_tree_path_free(tpath);
1169
1170                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1171                         {
1172                         FileData *nfd;
1173
1174                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1175
1176                         if (!nfd->thumb_pixbuf) fd = nfd;
1177
1178                         valid = gtk_tree_model_iter_next(store, &iter);
1179                         }
1180                 }
1181
1182         /* then find first undone */
1183
1184         if (!fd)
1185                 {
1186                 GList *work = vf->list;
1187                 while (work && !fd)
1188                         {
1189                         FileData *fd_p = work->data;
1190                         if (!fd_p->thumb_pixbuf)
1191                                 fd = fd_p;
1192                         else
1193                                 {
1194                                 GList *work2 = fd_p->sidecar_files;
1195
1196                                 while (work2 && !fd)
1197                                         {
1198                                         fd_p = work2->data;
1199                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1200                                         work2 = work2->next;
1201                                         }
1202                                 }
1203                         work = work->next;
1204                         }
1205                 }
1206
1207         return fd;
1208 }
1209
1210
1211 void vflist_thumb_reset_all(ViewFile *vf)
1212 {
1213         GList *work = vf->list;
1214         while (work)
1215                 {
1216                 FileData *fd = work->data;
1217                 if (fd->thumb_pixbuf)
1218                         {
1219                         g_object_unref(fd->thumb_pixbuf);
1220                         fd->thumb_pixbuf = NULL;
1221                         }
1222                 work = work->next;
1223                 }
1224 }
1225
1226 /*
1227  *-----------------------------------------------------------------------------
1228  * row stuff
1229  *-----------------------------------------------------------------------------
1230  */
1231
1232 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1233 {
1234         return g_list_nth_data(vf->list, row);
1235 }
1236
1237 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1238 {
1239         gint p = 0;
1240         GList *work, *work2;
1241
1242         work = vf->list;
1243         while (work)
1244                 {
1245                 FileData *list_fd = work->data;
1246                 if (list_fd == fd) return p;
1247                 
1248                 work2 = list_fd->sidecar_files;
1249                 while (work2)
1250                         {
1251                         /* FIXME: return the same index also for sidecars
1252                            it is sufficient for next/prev navigation but it should be rewritten 
1253                            without using indexes at all
1254                         */
1255                         FileData *sidecar_fd = work2->data;
1256                         if (sidecar_fd == fd) return p;
1257                         work2 = work2->next;
1258                         }
1259                 
1260                 work = work->next;
1261                 p++;
1262                 }
1263
1264         return -1;
1265 }
1266
1267 guint vflist_count(ViewFile *vf, gint64 *bytes)
1268 {
1269         if (bytes)
1270                 {
1271                 gint64 b = 0;
1272                 GList *work;
1273
1274                 work = vf->list;
1275                 while (work)
1276                         {
1277                         FileData *fd = work->data;
1278                         work = work->next;
1279                         b += fd->size;
1280                         }
1281
1282                 *bytes = b;
1283                 }
1284
1285         return g_list_length(vf->list);
1286 }
1287
1288 GList *vflist_get_list(ViewFile *vf)
1289 {
1290         GList *list = NULL;
1291         GList *work;
1292
1293         work = vf->list;
1294         while (work)
1295                 {
1296                 FileData *fd = work->data;
1297                 work = work->next;
1298
1299                 list = g_list_prepend(list, file_data_ref(fd));
1300                 }
1301
1302         return g_list_reverse(list);
1303 }
1304
1305 /*
1306  *-----------------------------------------------------------------------------
1307  * selections
1308  *-----------------------------------------------------------------------------
1309  */
1310
1311 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1312 {
1313         GtkTreeModel *store;
1314         GtkTreeSelection *selection;
1315         GList *slist;
1316         GList *work;
1317         gboolean found = FALSE;
1318
1319         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1320         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1321         work = slist;
1322         while (!found && work)
1323                 {
1324                 GtkTreePath *tpath = work->data;
1325                 FileData *fd_n;
1326                 GtkTreeIter iter;
1327
1328                 gtk_tree_model_get_iter(store, &iter, tpath);
1329                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1330                 if (fd_n == fd) found = TRUE;
1331                 work = work->next;
1332                 }
1333         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1334         g_list_free(slist);
1335
1336         return found;
1337 }
1338
1339 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1340 {
1341         FileData *fd;
1342
1343         fd = vf_index_get_data(vf, row);
1344         return vflist_row_is_selected(vf, fd);
1345 }
1346
1347 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1348 {
1349         GtkTreeModel *store;
1350         GtkTreeSelection *selection;
1351         GList *slist;
1352         guint count;
1353
1354         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1355         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1356
1357         if (bytes)
1358                 {
1359                 gint64 b = 0;
1360                 GList *work;
1361
1362                 work = slist;
1363                 while (work)
1364                         {
1365                         GtkTreePath *tpath = work->data;
1366                         GtkTreeIter iter;
1367                         FileData *fd;
1368
1369                         gtk_tree_model_get_iter(store, &iter, tpath);
1370                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1371                         b += fd->size;
1372
1373                         work = work->next;
1374                         }
1375
1376                 *bytes = b;
1377                 }
1378
1379         count = g_list_length(slist);
1380         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1381         g_list_free(slist);
1382
1383         return count;
1384 }
1385
1386 GList *vflist_selection_get_list(ViewFile *vf)
1387 {
1388         GtkTreeModel *store;
1389         GtkTreeSelection *selection;
1390         GList *slist;
1391         GList *list = NULL;
1392         GList *work;
1393
1394         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1395         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1396         work = slist;
1397         while (work)
1398                 {
1399                 GtkTreePath *tpath = work->data;
1400                 FileData *fd;
1401                 GtkTreeIter iter;
1402
1403                 gtk_tree_model_get_iter(store, &iter, tpath);
1404                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1405
1406                 list = g_list_prepend(list, file_data_ref(fd));
1407                 
1408                 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1409                         {
1410                         /* unexpanded - add whole group */
1411                         GList *work2 = fd->sidecar_files;
1412                         while (work2)
1413                                 {
1414                                 FileData *sfd = work2->data;
1415                                 list = g_list_prepend(list, file_data_ref(sfd));
1416                                 work2 = work2->next;
1417                                 }
1418                         }
1419
1420                 work = work->next;
1421                 }
1422         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1423         g_list_free(slist);
1424
1425         return g_list_reverse(list);
1426 }
1427
1428 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1429 {
1430         GtkTreeModel *store;
1431         GtkTreeSelection *selection;
1432         GList *slist;
1433         GList *list = NULL;
1434         GList *work;
1435
1436         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1437         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1438         work = slist;
1439         while (work)
1440                 {
1441                 GtkTreePath *tpath = work->data;
1442                 FileData *fd;
1443                 GtkTreeIter iter;
1444
1445                 gtk_tree_model_get_iter(store, &iter, tpath);
1446                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1447
1448                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1449
1450                 work = work->next;
1451                 }
1452         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1453         g_list_free(slist);
1454
1455         return g_list_reverse(list);
1456 }
1457
1458 void vflist_select_all(ViewFile *vf)
1459 {
1460         GtkTreeSelection *selection;
1461
1462         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1463         gtk_tree_selection_select_all(selection);
1464
1465         VFLIST(vf)->select_fd = NULL;
1466 }
1467
1468 void vflist_select_none(ViewFile *vf)
1469 {
1470         GtkTreeSelection *selection;
1471
1472         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1473         gtk_tree_selection_unselect_all(selection);
1474 }
1475
1476 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1477 {
1478         GtkTreePath *tpath;
1479         gboolean result;
1480
1481         tpath = gtk_tree_model_get_path(store, iter);
1482         result = gtk_tree_path_prev(tpath);
1483         if (result)
1484                 gtk_tree_model_get_iter(store, iter, tpath);
1485
1486         gtk_tree_path_free(tpath);
1487
1488         return result;
1489 }
1490
1491 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1492 {
1493         if (!gtk_tree_model_get_iter_first(store, iter))
1494                 return FALSE;
1495
1496         while (TRUE)
1497                 {
1498                 GtkTreeIter next = *iter;
1499                 
1500                 if (gtk_tree_model_iter_next(store, &next))
1501                         *iter = next;
1502                 else
1503                         break;
1504                 }
1505         
1506         return TRUE;
1507 }
1508
1509 void vflist_select_invert(ViewFile *vf)
1510 {
1511         GtkTreeIter iter;
1512         GtkTreeSelection *selection;
1513         GtkTreeModel *store;
1514         gboolean valid;
1515
1516         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1517         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1518
1519         /* Backward iteration prevents scrolling to the end of the list,
1520          * it scrolls to the first selected row instead. */
1521         valid = tree_model_get_iter_last(store, &iter);
1522
1523         while (valid)
1524                 {
1525                 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1526
1527                 if (selected)
1528                         gtk_tree_selection_unselect_iter(selection, &iter);
1529                 else
1530                         gtk_tree_selection_select_iter(selection, &iter);
1531                                 
1532                 valid = tree_model_iter_prev(store, &iter);
1533                 }
1534 }
1535
1536 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1537 {
1538         GtkTreeIter iter;
1539
1540         if (vflist_find_row(vf, fd, &iter) < 0) return;
1541
1542         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1543
1544         if (!vflist_row_is_selected(vf, fd))
1545                 {
1546                 GtkTreeSelection *selection;
1547                 GtkTreeModel *store;
1548                 GtkTreePath *tpath;
1549
1550                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551                 gtk_tree_selection_unselect_all(selection);
1552                 gtk_tree_selection_select_iter(selection, &iter);
1553                 vflist_move_cursor(vf, &iter);
1554
1555                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1556                 tpath = gtk_tree_model_get_path(store, &iter);
1557                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1558                 gtk_tree_path_free(tpath);
1559                 }
1560 }
1561
1562 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1563 {
1564         GList *work;
1565         FileData *fd = NULL;
1566         
1567         if (sel_fd->parent) sel_fd = sel_fd->parent;
1568         work = vf->list;
1569         
1570         while (work)
1571                 {
1572                 gint match;
1573                 fd = work->data;
1574                 work = work->next;
1575
1576                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1577                 
1578                 if (match >= 0) break;
1579                 }
1580
1581         if (fd) vflist_select_by_fd(vf, fd);
1582
1583 }
1584
1585 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1586 {
1587         GtkTreeModel *store;
1588         GtkTreeIter iter;
1589         GtkTreeSelection *selection;
1590         gboolean valid;
1591         gint n = mark - 1;
1592
1593         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1594
1595         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1596         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1597
1598         valid = gtk_tree_model_get_iter_first(store, &iter);
1599         while (valid)
1600                 {
1601                 FileData *fd;
1602                 gboolean mark_val, selected;
1603                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1604
1605                 mark_val = file_data_get_mark(fd, n);
1606                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1607
1608                 switch (mode)
1609                         {
1610                         case MTS_MODE_SET: selected = mark_val;
1611                                 break;
1612                         case MTS_MODE_OR: selected = mark_val | selected;
1613                                 break;
1614                         case MTS_MODE_AND: selected = mark_val & selected;
1615                                 break;
1616                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1617                                 break;
1618                         }
1619
1620                 if (selected)
1621                         gtk_tree_selection_select_iter(selection, &iter);
1622                 else
1623                         gtk_tree_selection_unselect_iter(selection, &iter);
1624
1625                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1626                 }
1627 }
1628
1629 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1630 {
1631         GtkTreeModel *store;
1632         GtkTreeSelection *selection;
1633         GList *slist;
1634         GList *work;
1635         gint n = mark - 1;
1636
1637         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1638
1639         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1640         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1641         work = slist;
1642         while (work)
1643                 {
1644                 GtkTreePath *tpath = work->data;
1645                 FileData *fd;
1646                 GtkTreeIter iter;
1647
1648                 gtk_tree_model_get_iter(store, &iter, tpath);
1649                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1650
1651                 /* the change has a very limited range and the standard notification would trigger
1652                    complete re-read of the directory - try to do only minimal update instead */
1653                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1654
1655                 switch (mode)
1656                         {
1657                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1658                                 break;
1659                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1660                                 break;
1661                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1662                                 break;
1663                         }
1664                 
1665                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1666                         {
1667                         vf_refresh_idle(vf);
1668                         }
1669                 else
1670                         {
1671                         /* mark functions can have various side effects - update all columns to be sure */
1672                         vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1673                         /* mark functions can change sidecars too */
1674                         vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1675                         }
1676
1677                 
1678                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1679
1680                 work = work->next;
1681                 }
1682         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1683         g_list_free(slist);
1684 }
1685
1686 /*
1687  *-----------------------------------------------------------------------------
1688  * core (population)
1689  *-----------------------------------------------------------------------------
1690  */
1691
1692 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1693 {
1694         GtkTreeViewColumn *column;
1695         GtkCellRenderer *cell;
1696         GList *list;
1697
1698         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1699         if (!column) return;
1700
1701         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1702
1703         list = gtk_tree_view_column_get_cell_renderers(column);
1704         if (!list) return;
1705         cell = list->data;
1706         g_list_free(list);
1707
1708         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1709         gtk_tree_view_column_set_visible(column, thumb);
1710
1711         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1712         if (!column) return;
1713         gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1714
1715         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1716         if (!column) return;
1717         gtk_tree_view_column_set_visible(column, !multiline);
1718
1719         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1720         if (!column) return;
1721         gtk_tree_view_column_set_visible(column, !multiline);
1722 }
1723
1724 static gboolean vflist_is_multiline(ViewFile *vf)
1725 {
1726         return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1727 }
1728
1729
1730 static void vflist_populate_view(ViewFile *vf, gboolean force)
1731 {
1732         GtkTreeStore *store;
1733         GList *selected;
1734
1735         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1736
1737         vf_thumb_stop(vf);
1738
1739         if (!vf->list)
1740                 {
1741                 vflist_store_clear(vf);
1742                 vf_send_update(vf);
1743                 return;
1744                 }
1745
1746         vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1747
1748         selected = vflist_selection_get_list(vf);
1749         
1750         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1751
1752         if (selected && vflist_selection_count(vf, NULL) == 0)
1753                 {
1754                 /* all selected files disappeared */
1755                 vflist_select_closest(vf, selected->data);
1756                 }
1757
1758         filelist_free(selected);
1759         
1760         vf_send_update(vf);
1761         vf_thumb_update(vf);
1762 }
1763
1764 gboolean vflist_refresh(ViewFile *vf)
1765 {
1766         GList *old_list;
1767         gboolean ret = TRUE;
1768
1769         old_list = vf->list;
1770         vf->list = NULL;
1771
1772         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1773         if (vf->dir_fd)
1774                 {
1775                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1776
1777                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1778                 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1779                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1780
1781                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1782                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1783                 }
1784
1785         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1786
1787         vflist_populate_view(vf, FALSE);
1788
1789         filelist_free(old_list);
1790         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1791
1792         return ret;
1793 }
1794
1795
1796
1797 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1798
1799 #define CELL_HEIGHT_OVERRIDE 512
1800
1801 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1802 {
1803         GParamSpec *spec;
1804
1805         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1806         if (spec && G_IS_PARAM_SPEC_INT(spec))
1807                 {
1808                 GParamSpecInt *spec_int;
1809
1810                 spec_int = G_PARAM_SPEC_INT(spec);
1811                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1812                 }
1813 }
1814
1815 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1816 {
1817         static GdkColor color;
1818         static GtkWidget *done = NULL;
1819
1820         if (done != widget)
1821                 {
1822                 GtkStyle *style;
1823
1824                 style = gtk_widget_get_style(widget);
1825                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1826                 shift_color(&color, -1, 0);
1827                 done = widget;
1828                 }
1829
1830         return &color;
1831 }
1832
1833 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1834                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1835 {
1836         ViewFile *vf = data;
1837         gboolean set;
1838
1839         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1840         g_object_set(G_OBJECT(cell),
1841                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1842                      "cell-background-set", set, NULL);
1843 }
1844
1845 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1846 {
1847         GtkTreeViewColumn *column;
1848         GtkCellRenderer *renderer;
1849
1850         column = gtk_tree_view_column_new();
1851         gtk_tree_view_column_set_title(column, title);
1852         gtk_tree_view_column_set_min_width(column, 4);
1853
1854         if (!image)
1855                 {
1856                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1857                 renderer = gtk_cell_renderer_text_new();
1858                 if (right_justify)
1859                         {
1860                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1861                         }
1862                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1863                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1864                 if (expand)
1865                         gtk_tree_view_column_set_expand(column, TRUE);
1866                 }
1867         else
1868                 {
1869                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1870                 renderer = gtk_cell_renderer_pixbuf_new();
1871                 cell_renderer_height_override(renderer);
1872                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1873                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1874                 }
1875
1876         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1877         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1878         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1879
1880         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1881 }
1882
1883 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1884 {
1885         ViewFile *vf = data;
1886         GtkTreeStore *store;
1887         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1888         GtkTreeIter iter;
1889         FileData *fd;
1890         gboolean marked;
1891         guint col_idx;
1892
1893         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1894         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1895                 return;
1896
1897         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1898
1899         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1900
1901         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1902         marked = !marked;
1903         
1904         /* the change has a very limited range and the standard notification would trigger
1905            complete re-read of the directory - try to do only minimal update instead */
1906         file_data_unregister_notify_func(vf_notify_cb, vf);
1907         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1908         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1909                 {
1910                 vf_refresh_idle(vf);
1911                 }
1912         else
1913                 {
1914                 /* mark functions can have various side effects - update all columns to be sure */
1915                 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1916                 /* mark functions can change sidecars too */
1917                 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1918                 }
1919         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1920
1921         gtk_tree_path_free(path);
1922 }
1923
1924 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1925 {
1926         GtkTreeViewColumn *column;
1927         GtkCellRenderer *renderer;
1928         GtkTreeStore *store;
1929         gint index;
1930
1931         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1932
1933         renderer = gtk_cell_renderer_toggle_new();
1934         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1935
1936         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1937         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1938         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1939
1940         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1941         gtk_tree_view_column_set_fixed_width(column, 22);
1942         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1943
1944
1945         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1946 }
1947
1948 /*
1949  *-----------------------------------------------------------------------------
1950  * base
1951  *-----------------------------------------------------------------------------
1952  */
1953
1954 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1955 {
1956         gboolean ret;
1957         if (!dir_fd) return FALSE;
1958         if (vf->dir_fd == dir_fd) return TRUE;
1959
1960         file_data_unref(vf->dir_fd);
1961         vf->dir_fd = file_data_ref(dir_fd);
1962
1963         /* force complete reload */
1964         vflist_store_clear(vf);
1965
1966         filelist_free(vf->list);
1967         vf->list = NULL;
1968
1969         ret = vf_refresh(vf);
1970         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1971         return ret;
1972 }
1973
1974 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1975 {
1976         ViewFile *vf = data;
1977
1978         file_data_unregister_notify_func(vf_notify_cb, vf);
1979
1980         vflist_select_idle_cancel(vf);
1981         vf_refresh_idle_cancel(vf);
1982         vf_thumb_stop(vf);
1983
1984         filelist_free(vf->list);
1985 }
1986
1987 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1988 {
1989         GtkTreeStore *store;
1990         GtkTreeSelection *selection;
1991         GType flist_types[FILE_COLUMN_COUNT];
1992         gint i;
1993         gint column;
1994
1995         vf->info = g_new0(ViewFileInfoList, 1);
1996         
1997         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1998         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1999         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2000         flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2001         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2002         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2003         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2004         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2005         flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2006         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2007         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2008                 flist_types[i] = G_TYPE_BOOLEAN;
2009
2010         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2011
2012         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2013         g_object_unref(store);
2014
2015         g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2016                          G_CALLBACK(vflist_expand_cb), vf);
2017
2018         g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2019                          G_CALLBACK(vflist_collapse_cb), vf);
2020
2021         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2022         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2023         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2024
2025         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2026         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2027
2028         column = 0;
2029
2030         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2031                 {
2032                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2033                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2034                 column++;
2035                 }
2036
2037         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2038         g_assert(column == FILE_VIEW_COLUMN_THUMB);
2039         column++;
2040         
2041         vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2042         g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2043         column++;
2044
2045         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2046         g_assert(column == FILE_VIEW_COLUMN_SIZE);
2047         column++;
2048
2049         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2050         g_assert(column == FILE_VIEW_COLUMN_DATE);
2051         column++;
2052
2053         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2054         return vf;
2055 }
2056
2057 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2058 {
2059         if (VFLIST(vf)->thumbs_enabled == enable) return;
2060
2061         VFLIST(vf)->thumbs_enabled = enable;
2062         
2063         /* vflist_populate_view is better than vf_refresh:
2064            - no need to re-read the directory
2065            - force update because the formatted string has changed
2066         */
2067         if (vf->layout) 
2068                 {
2069                 vflist_populate_view(vf, TRUE);
2070                 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2071                 }
2072 }
2073
2074 void vflist_marks_set(ViewFile *vf, gboolean enable)
2075 {
2076         GList *columns, *work;
2077
2078         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2079
2080         work = columns;
2081         while (work)
2082                 {
2083                 GtkTreeViewColumn *column = work->data;
2084                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2085                 work = work->next;
2086
2087                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2088                         gtk_tree_view_column_set_visible(column, enable);
2089                 }
2090
2091         g_list_free(columns);
2092         //vf_refresh(vf);
2093 }
2094
2095 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */