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