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