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