call gtk_tree_view_columns_autosize only when reall needed
[geeqie.git] / src / view_file_list.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include "main.h"
14 #include "view_file_list.h"
15
16 #include "bar.h"
17 #include "cache_maint.h"
18 #include "dnd.h"
19 #include "editors.h"
20 #include "img-view.h"
21 #include "layout.h"
22 #include "layout_image.h"
23 #include "menu.h"
24 #include "metadata.h"
25 #include "thumb.h"
26 #include "utilops.h"
27 #include "ui_fileops.h"
28 #include "ui_menu.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
32
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
34
35 /* Index to tree store */
36 enum {
37         FILE_COLUMN_POINTER = 0,
38         FILE_COLUMN_VERSION,
39         FILE_COLUMN_THUMB,
40         FILE_COLUMN_FORMATTED,
41         FILE_COLUMN_NAME,
42         FILE_COLUMN_SIDECARS,
43         FILE_COLUMN_SIZE,
44         FILE_COLUMN_DATE,
45         FILE_COLUMN_EXPANDED,
46         FILE_COLUMN_COLOR,
47         FILE_COLUMN_MARKS,
48         FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
49         FILE_COLUMN_COUNT
50 };
51
52
53 /* Index to tree view */
54 enum {
55         FILE_VIEW_COLUMN_MARKS = 0,
56         FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
57         FILE_VIEW_COLUMN_THUMB,
58         FILE_VIEW_COLUMN_FORMATTED,
59         FILE_VIEW_COLUMN_SIZE,
60         FILE_VIEW_COLUMN_DATE,
61         FILE_VIEW_COLUMN_COUNT
62 };
63
64
65
66 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
71
72
73 /*
74  *-----------------------------------------------------------------------------
75  * misc
76  *-----------------------------------------------------------------------------
77  */
78 typedef struct {
79         FileData *fd;
80         GtkTreeIter *iter;
81         gboolean found;
82         gint row;
83 } ViewFileFindRowData;
84
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
86 {
87         ViewFileFindRowData *find = data;
88         FileData *fd;
89         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
90         if (fd == find->fd)
91                 {
92                 *find->iter = *iter;
93                 find->found = TRUE;
94                 return TRUE;
95                 }
96         find->row++;
97         return FALSE;
98 }
99
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
101 {
102         GtkTreeModel *store;
103         ViewFileFindRowData data = {fd, iter, FALSE, 0};
104
105         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106         gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
107
108         if (data.found)
109                 {
110                 return data.row;
111                 }
112
113         return -1;
114 }
115
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
117 {
118         GtkTreePath *tpath;
119         GtkTreeViewColumn *column;
120
121         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122                                           &tpath, &column, NULL, NULL))
123                 {
124                 GtkTreeModel *store;
125                 GtkTreeIter row;
126                 FileData *fd;
127
128                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129                 gtk_tree_model_get_iter(store, &row, tpath);
130                 gtk_tree_path_free(tpath);
131                 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
132
133                 return fd;
134                 }
135
136         return NULL;
137 }
138
139 #if 0
140 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
141 {
142         gint i = 0;
143         while (work)
144                 {
145                 FileData *fd_p = work->data;
146                 if (fd == fd_p) return i;
147
148                 i++;
149
150                 GList *work2 = fd_p->sidecar_files;
151                 while (work2)
152                         {
153                         fd_p = work2->data;
154                         if (fd == fd_p) return i;
155
156                         i++;
157                         work2 = work2->next;
158                         }
159                 work = work->next;
160                 }
161         return -1;
162 }
163
164 static gint vflist_sidecar_list_count(GList *work)
165 {
166         gint i = 0;
167         while (work)
168                 {
169                 FileData *fd = work->data;
170                 i++;
171
172                 GList *work2 = fd->sidecar_files;
173                 while (work2)
174                         {
175                         i++;
176                         work2 = work2->next;
177                         }
178                 work = work->next;
179                 }
180         return i;
181 }
182 #endif
183
184 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
185 {
186         FileData *fd;
187         gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
188         file_data_unref(fd);
189         return FALSE;
190 }
191
192 static void vflist_store_clear(ViewFile *vf)
193 {
194         GtkTreeModel *store;
195         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
196         gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
197         gtk_tree_store_clear(GTK_TREE_STORE(store));
198 }
199
200 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
201 {
202         GtkTreeModel *store;
203         GtkTreeIter iter;
204
205         if (vflist_find_row(vf, fd, &iter) < 0) return;
206         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
207         gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
208 }
209
210 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
211 {
212         GtkTreeModel *store;
213         GtkTreePath *tpath;
214
215         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
216
217         tpath = gtk_tree_model_get_path(store, iter);
218         gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
219         gtk_tree_path_free(tpath);
220 }
221
222
223 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
224 {
225         GList *columns, *work;
226         gint i = 0;
227
228         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
229         work = columns;
230         while (work)
231                 {
232                 GtkTreeViewColumn *column = work->data;
233                 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
234                         break;
235                 work = work->next;
236                 i++;
237                 }
238
239         g_list_free(columns);
240         return i;
241 }
242
243
244 /*
245  *-----------------------------------------------------------------------------
246  * dnd
247  *-----------------------------------------------------------------------------
248  */
249
250 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
251                            GtkSelectionData *selection_data, guint info,
252                            guint time, gpointer data)
253 {
254         ViewFile *vf = data;
255         GList *list = NULL;
256         gchar *uri_text = NULL;
257         gint total;
258
259         if (!VFLIST(vf)->click_fd) return;
260
261         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
262                 {
263                 list = vf_selection_get_list(vf);
264                 }
265         else
266                 {
267                 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
268                 }
269
270         if (!list) return;
271
272         uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
273         filelist_free(list);
274
275         DEBUG_1("%s", uri_text);
276
277         gtk_selection_data_set(selection_data, selection_data->target,
278                                8, (guchar *)uri_text, total);
279         g_free(uri_text);
280 }
281
282 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
283 {
284         ViewFile *vf = data;
285
286         vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
287
288         if (VFLIST(vf)->thumbs_enabled &&
289             VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
290                 {
291                 guint items;
292
293                 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
294                         items = vf_selection_count(vf, NULL);
295                 else
296                         items = 1;
297
298                 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
299                 }
300 }
301
302 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
303 {
304         ViewFile *vf = data;
305
306         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
307
308         if (context->action == GDK_ACTION_MOVE)
309                 {
310                 vf_refresh(vf);
311                 }
312 }
313
314 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
315                                       int x, int y, GtkSelectionData *selection,
316                                       guint info, guint time, gpointer data)
317 {
318         ViewFile *vf = data;
319
320         if (info == TARGET_TEXT_PLAIN) {
321                 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
322
323                 if (fd) {
324                         /* Add keywords to file */
325                         gchar *str = g_strndup((gchar *)selection->data, selection->length);
326                         GList *kw_list = string_to_keywords_list(str);
327                         
328                         metadata_append_list(fd, KEYWORD_KEY, kw_list);
329                         string_list_free(kw_list);
330                         g_free(str);
331 /*
332 file notification should handle this automatically
333                         if (vf->layout && vf->layout->bar_info) {
334                                 bar_set_fd(vf->layout->bar_info, fd);
335                         }
336 */
337                 }
338         }
339 }
340
341 void vflist_dnd_init(ViewFile *vf)
342 {
343         gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
344                             dnd_file_drag_types, dnd_file_drag_types_count,
345                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
346         gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
347                             dnd_file_drag_types, dnd_file_drag_types_count,
348                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
349
350         g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
351                          G_CALLBACK(vflist_dnd_get), vf);
352         g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
353                          G_CALLBACK(vflist_dnd_begin), vf);
354         g_signal_connect(G_OBJECT(vf->listview), "drag_end",
355                          G_CALLBACK(vflist_dnd_end), vf);
356         g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
357                          G_CALLBACK(vflist_drag_data_received), vf);
358 }
359
360 /*
361  *-----------------------------------------------------------------------------
362  * pop-up menu
363  *-----------------------------------------------------------------------------
364  */
365
366 GList *vflist_pop_menu_file_list(ViewFile *vf)
367 {
368         GList *list;
369         if (!VFLIST(vf)->click_fd) return NULL;
370
371         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
372                 {
373                 return vf_selection_get_list(vf);
374                 }
375
376         list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
377
378         if (VFLIST(vf)->click_fd->sidecar_files)
379                 {
380                 /* check if the row is expanded */
381                 GtkTreeModel *store;
382                 GtkTreeIter iter;
383                 
384                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
385                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
386                         {
387                         GtkTreePath *tpath;
388
389                         tpath = gtk_tree_model_get_path(store, &iter);
390                         if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
391                                 {
392                                 /* unexpanded - add whole group */
393                                 GList *work = VFLIST(vf)->click_fd->sidecar_files;
394                                 while (work)
395                                         {
396                                         FileData *sfd = work->data;
397                                         list = g_list_prepend(list, file_data_ref(sfd));
398                                         work = work->next;
399                                         }
400                                 }
401                         gtk_tree_path_free(tpath);
402                         }
403                 list = g_list_reverse(list);
404                 }
405
406         return list;
407 }
408
409 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
410 {
411         ViewFile *vf = data;
412
413         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
414                 {
415                 GList *list;
416
417                 list = vf_selection_get_list(vf);
418                 view_window_new_from_list(list);
419                 filelist_free(list);
420                 }
421         else
422                 {
423                 view_window_new(VFLIST(vf)->click_fd);
424                 }
425 }
426
427 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
428 {
429         ViewFile *vf = data;
430         GList *list;
431
432         list = vf_pop_menu_file_list(vf);
433         if (options->file_ops.enable_in_place_rename &&
434             list && !list->next && VFLIST(vf)->click_fd)
435                 {
436                 GtkTreeModel *store;
437                 GtkTreeIter iter;
438
439                 filelist_free(list);
440
441                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
442                 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
443                         {
444                         GtkTreePath *tpath;
445
446                         tpath = gtk_tree_model_get_path(store, &iter);
447                         tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
448                                           vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
449                                           vflist_row_rename_cb, vf);
450                         gtk_tree_path_free(tpath);
451                         }
452                 return;
453                 }
454
455         file_util_rename(NULL, list, vf->listview);
456 }
457
458 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
459 {
460         ViewFile *vf = data;
461
462         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
463         if (vf->layout)
464                 {
465                 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
466                 }
467         else
468                 {
469                 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
470                 }
471 }
472
473 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
474 {
475         ViewFile *vf = data;
476
477         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
478         vf_refresh(vf);
479         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
480 }
481
482 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
483 {
484         ViewFile *vf = data;
485         vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
486         VFLIST(vf)->click_fd = NULL;
487         vf->popup = NULL;
488 }
489
490
491 /*
492  *-----------------------------------------------------------------------------
493  * callbacks
494  *-----------------------------------------------------------------------------
495  */
496
497 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
498 {
499         ViewFile *vf = data;
500         gchar *new_path;
501
502         if (!new || !new[0]) return FALSE;
503
504         new_path = g_build_filename(vf->dir_fd->path, new, NULL);
505
506         if (strchr(new, G_DIR_SEPARATOR) != NULL)
507                 {
508                 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
509                 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
510                 g_free(text);
511                 }
512         else
513                 {
514                 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
515                 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
516                 file_util_rename_simple(fd, new_path, vf->listview);
517                 file_data_unref(fd);
518                 g_free(old_path);
519                 }
520         
521         g_free(new_path);
522
523         return FALSE;
524 }
525
526 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
527 {
528         ViewFile *vf = data;
529         GtkTreeModel *store;
530         GtkTreeIter iter;
531         GtkTreePath *tpath;
532         gint cw, ch;
533
534         if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
535         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
536         tpath = gtk_tree_model_get_path(store, &iter);
537         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
538         gtk_tree_path_free(tpath);
539         *y += ch;
540         popup_menu_position_clamp(menu, x, y, 0);
541 }
542
543 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
544 {
545         ViewFile *vf = data;
546         GtkTreePath *tpath;
547
548         if (event->keyval != GDK_Menu) return FALSE;
549
550         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
551         if (tpath)
552                 {
553                 GtkTreeModel *store;
554                 GtkTreeIter iter;
555
556                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
557                 gtk_tree_model_get_iter(store, &iter, tpath);
558                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
559                 gtk_tree_path_free(tpath);
560                 }
561         else
562                 {
563                 VFLIST(vf)->click_fd = NULL;
564                 }
565
566         vf->popup = vf_pop_menu(vf);
567         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
568
569         return TRUE;
570 }
571
572 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
573 {
574         ViewFile *vf = data;
575         GtkTreePath *tpath;
576         GtkTreeIter iter;
577         FileData *fd = NULL;
578         GtkTreeViewColumn *column;
579         
580         vf->clicked_mark = 0;
581
582         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
583                                           &tpath, &column, NULL, NULL))
584                 {
585                 GtkTreeModel *store;
586                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
587
588                 if (bevent->button == MOUSE_BUTTON_LEFT &&
589                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
590                         return FALSE;
591
592                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
593                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
594
595                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
596
597                 gtk_tree_model_get_iter(store, &iter, tpath);
598                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
599 #if 0
600                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
601 #endif
602                 gtk_tree_path_free(tpath);
603                 }
604
605         VFLIST(vf)->click_fd = fd;
606
607         if (bevent->button == MOUSE_BUTTON_RIGHT)
608                 {
609                 vf->popup = vf_pop_menu(vf);
610                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
611                                 bevent->button, bevent->time);
612                 return TRUE;
613                 }
614
615         if (!fd) return FALSE;
616
617         if (bevent->button == MOUSE_BUTTON_MIDDLE)
618                 {
619                 if (!vflist_row_is_selected(vf, fd))
620                         {
621                         vflist_color_set(vf, fd, TRUE);
622                         }
623                 return TRUE;
624                 }
625
626
627         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
628             !(bevent->state & GDK_SHIFT_MASK ) &&
629             !(bevent->state & GDK_CONTROL_MASK ) &&
630             vflist_row_is_selected(vf, fd))
631                 {
632                 GtkTreeSelection *selection;
633
634                 gtk_widget_grab_focus(widget);
635
636
637                 /* returning FALSE and further processing of the event is needed for
638                    correct operation of the expander, to show the sidecar files.
639                    It however resets the selection of multiple files. With this condition
640                    it should work for both cases */
641                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
642                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
643                 }
644
645 #if 1
646         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
647                 {
648                 if (vf->layout) layout_image_full_screen_start(vf->layout);
649                 }
650 #endif
651
652         return FALSE;
653 }
654
655 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
656 {
657         ViewFile *vf = data;
658         GtkTreePath *tpath;
659         GtkTreeIter iter;
660         FileData *fd = NULL;
661
662         if (bevent->button == MOUSE_BUTTON_MIDDLE)
663                 {
664                 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
665                 }
666
667         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
668                 {
669                 return TRUE;
670                 }
671
672         if ((bevent->x != 0 || bevent->y != 0) &&
673             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
674                                           &tpath, NULL, NULL, NULL))
675                 {
676                 GtkTreeModel *store;
677
678                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
679                 gtk_tree_model_get_iter(store, &iter, tpath);
680                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
681                 gtk_tree_path_free(tpath);
682                 }
683
684         if (bevent->button == MOUSE_BUTTON_MIDDLE)
685                 {
686                 if (fd && VFLIST(vf)->click_fd == fd)
687                         {
688                         GtkTreeSelection *selection;
689
690                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
691                         if (vflist_row_is_selected(vf, fd))
692                                 {
693                                 gtk_tree_selection_unselect_iter(selection, &iter);
694                                 }
695                         else
696                                 {
697                                 gtk_tree_selection_select_iter(selection, &iter);
698                                 }
699                         }
700                 return TRUE;
701                 }
702
703         if (fd && VFLIST(vf)->click_fd == fd &&
704             !(bevent->state & GDK_SHIFT_MASK ) &&
705             !(bevent->state & GDK_CONTROL_MASK ) &&
706             vflist_row_is_selected(vf, fd))
707                 {
708                 GtkTreeSelection *selection;
709
710                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
711                 gtk_tree_selection_unselect_all(selection);
712                 gtk_tree_selection_select_iter(selection, &iter);
713                 vflist_move_cursor(vf, &iter);
714 //              return TRUE;// FIXME - expand
715                 }
716
717         return FALSE;
718 }
719
720 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
721 {
722         FileData *read_ahead_fd = NULL;
723         gint row;
724         FileData *cur_fd;
725
726         if (!sel_fd) return;
727
728         cur_fd = layout_image_get_fd(vf->layout);
729         if (sel_fd == cur_fd) return; /* no change */
730
731         row = g_list_index(vf->list, sel_fd);
732         // FIXME sidecar data
733
734         if (sel_fd && options->image.enable_read_ahead && row >= 0)
735                 {
736                 if (row > g_list_index(vf->list, cur_fd) &&
737                     (guint) (row + 1) < vf_count(vf, NULL))
738                         {
739                         read_ahead_fd = vf_index_get_data(vf, row + 1);
740                         }
741                 else if (row > 0)
742                         {
743                         read_ahead_fd = vf_index_get_data(vf, row - 1);
744                         }
745                 }
746
747         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
748 }
749
750 static gboolean vflist_select_idle_cb(gpointer data)
751 {
752         ViewFile *vf = data;
753
754         if (!vf->layout)
755                 {
756                 VFLIST(vf)->select_idle_id = 0;
757                 return FALSE;
758                 }
759
760         vf_send_update(vf);
761
762         if (VFLIST(vf)->select_fd)
763                 {
764                 vflist_select_image(vf, VFLIST(vf)->select_fd);
765                 VFLIST(vf)->select_fd = NULL;
766                 }
767
768         VFLIST(vf)->select_idle_id = 0;
769         return FALSE;
770 }
771
772 static void vflist_select_idle_cancel(ViewFile *vf)
773 {
774         if (VFLIST(vf)->select_idle_id)
775                 {
776                 g_source_remove(VFLIST(vf)->select_idle_id);
777                 VFLIST(vf)->select_idle_id = 0;
778                 }
779 }
780
781 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
782                                  gboolean path_currently_selected, gpointer data)
783 {
784         ViewFile *vf = data;
785         GtkTreeIter iter;
786
787         if (!path_currently_selected &&
788             gtk_tree_model_get_iter(store, &iter, tpath))
789                 {
790                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
791                 }
792         else
793                 {
794                 VFLIST(vf)->select_fd = NULL;
795                 }
796
797         if (vf->layout &&
798             !VFLIST(vf)->select_idle_id)
799                 {
800                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
801                 }
802
803         return TRUE;
804 }
805
806 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
807 {
808         ViewFile *vf = data;
809         vflist_set_expanded(vf, iter, TRUE);
810 }
811
812 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
813 {
814         ViewFile *vf = data;
815         vflist_set_expanded(vf, iter, FALSE);
816 }
817
818 /*
819  *-----------------------------------------------------------------------------
820  * misc
821  *-----------------------------------------------------------------------------
822  */
823
824 /*
825 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
826                                         gboolean path_currently_selected, gpointer data)
827 {
828         return TRUE;
829 }
830 */
831
832 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
833  {
834         gboolean multiline = vflist_is_multiline(vf);
835         gchar *text;
836
837         if (multiline)
838                 {
839                 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
840                 }
841         else
842                 {
843                 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
844                 }
845         return text;
846 }
847
848 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
849 {
850         GtkTreeStore *store;
851         gchar *name;
852         gchar *sidecars;
853         gchar *size;
854         gchar *time;
855         gchar *formatted;
856
857         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
858
859         gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
860                                         FILE_COLUMN_NAME, &name,
861                                         FILE_COLUMN_SIDECARS, &sidecars,
862                                         FILE_COLUMN_SIZE, &size,
863                                         FILE_COLUMN_DATE, &time,
864                                         -1);
865         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
866
867         gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
868                                         FILE_COLUMN_EXPANDED, expanded,
869                                         -1);
870         g_free(time);
871         g_free(size);
872         g_free(sidecars);
873         g_free(name);
874         g_free(formatted);
875 }
876
877 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
878 {
879         gchar *size;
880         gchar *sidecars = NULL;
881         gchar *name;
882         const gchar *time = text_from_time(fd->date);
883         gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
884         const gchar *disabled_grouping;
885         gchar *formatted;
886         gboolean expanded = FALSE;
887         
888         if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
889                 {
890                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
891                 }
892
893         sidecars = file_data_sc_list_to_string(fd);
894
895         disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
896         name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
897         size = text_from_size(fd->size);
898         
899         formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
900         
901         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
902                                         FILE_COLUMN_VERSION, fd->version,
903                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
904                                         FILE_COLUMN_FORMATTED, formatted,
905                                         FILE_COLUMN_SIDECARS, sidecars,
906                                         FILE_COLUMN_NAME, name,
907                                         FILE_COLUMN_SIZE, size,
908                                         FILE_COLUMN_DATE, time,
909 #define STORE_SET_IS_SLOW 1
910 #if STORE_SET_IS_SLOW
911 /* this is 3x faster on a directory with 20000 files */
912                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
913                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
914                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
915                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
916                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
917                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
918 #if FILEDATA_MARKS_SIZE != 6
919 #error this needs to be updated
920 #endif
921 #endif
922                                         FILE_COLUMN_COLOR, FALSE, -1);
923
924 #if !STORE_SET_IS_SLOW
925         {
926         gint i;
927         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
928                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
929         }
930 #endif
931         g_free(size);
932         g_free(sidecars);
933         g_free(name);
934         g_free(formatted);
935 }
936
937 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
938 {
939         GList *work;
940         GtkTreeIter iter;
941         gboolean valid;
942         gint num_ordered = 0;
943         gint num_prepended = 0;
944
945         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
946
947         work = list;
948         while (work)
949                 {
950                 gint match;
951                 FileData *fd = work->data;
952                 gboolean done = FALSE;
953
954                 while (!done)
955                         {
956                         FileData *old_fd = NULL;
957                         gint old_version = 0;
958
959                         if (valid)
960                                 {
961                                 num_ordered++;
962                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
963                                                    FILE_COLUMN_POINTER, &old_fd,
964                                                    FILE_COLUMN_VERSION, &old_version,
965                                                    -1);
966
967                                 if (fd == old_fd)
968                                         {
969                                         match = 0;
970                                         }
971                                 else
972                                         {
973                                         if (parent_iter)
974                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
975                                         else
976                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
977
978                                         if (match == 0) g_warning("multiple fd for the same path");
979                                         }
980                                         
981                                 }
982                         else
983                                 {
984                                 match = -1;
985                                 }
986
987                         if (match < 0)
988                                 {
989                                 GtkTreeIter new;
990
991                                 if (valid)
992                                         {
993                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
994                                         }
995                                 else
996                                         {
997                                         /*
998                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
999                                             and it seems to be much faster to add new entries to the beginning and reorder later
1000                                         */
1001                                         num_prepended++;
1002                                         gtk_tree_store_prepend(store, &new, parent_iter);
1003                                         }
1004
1005                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1006                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
1007                                 
1008                                 if (g_list_find(selected, fd))
1009                                         {
1010                                         /* renamed files - the same fd appears at different position - select it again*/
1011                                         GtkTreeSelection *selection;
1012                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1013                                         gtk_tree_selection_select_iter(selection, &new);
1014                                         }
1015
1016                                 done = TRUE;
1017                                 }
1018                         else if (match > 0)
1019                                 {
1020                                 file_data_unref(old_fd);
1021                                 valid = gtk_tree_store_remove(store, &iter);
1022                                 }
1023                         else
1024                                 {
1025                                 if (fd->version != old_version)
1026                                         {
1027                                         vflist_setup_iter(vf, store, &iter, fd);
1028                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
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);
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_tree_view_column_get_cell_renderers(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)
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);
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);
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);
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         if (vf->layout) vf_refresh(vf);
2059 }
2060
2061 void vflist_marks_set(ViewFile *vf, gboolean enable)
2062 {
2063         GList *columns, *work;
2064
2065         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2066
2067         work = columns;
2068         while (work)
2069                 {
2070                 GtkTreeViewColumn *column = work->data;
2071                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2072                 work = work->next;
2073
2074                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2075                         gtk_tree_view_column_set_visible(column, enable);
2076                 }
2077
2078         g_list_free(columns);
2079         //vf_refresh(vf);
2080 }
2081
2082 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */