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