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