fixed in-place renaming
[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)->thumb_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)->thumb_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                 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
432                 file_util_rename_simple(fd, new_path, vf->listview);
433                 file_data_unref(fd);
434                 }
435         g_free(old_path);
436         g_free(new_path);
437
438         return FALSE;
439 }
440
441 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
442 {
443         ViewFile *vf = data;
444         GtkTreeModel *store;
445         GtkTreeIter iter;
446         GtkTreePath *tpath;
447         gint cw, ch;
448
449         if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
450         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
451         tpath = gtk_tree_model_get_path(store, &iter);
452         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
453         gtk_tree_path_free(tpath);
454         *y += ch;
455         popup_menu_position_clamp(menu, x, y, 0);
456 }
457
458 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
459 {
460         ViewFile *vf = data;
461         GtkTreePath *tpath;
462
463         if (event->keyval != GDK_Menu) return FALSE;
464
465         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
466         if (tpath)
467                 {
468                 GtkTreeModel *store;
469                 GtkTreeIter iter;
470
471                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
472                 gtk_tree_model_get_iter(store, &iter, tpath);
473                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
474                 gtk_tree_path_free(tpath);
475                 }
476         else
477                 {
478                 VFLIST_INFO(vf, click_fd) = NULL;
479                 }
480
481         vf->popup = vf_pop_menu(vf);
482         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
483
484         return TRUE;
485 }
486
487 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
488 {
489         ViewFile *vf = data;
490         GtkTreePath *tpath;
491         GtkTreeIter iter;
492         FileData *fd = NULL;
493         GtkTreeViewColumn *column;
494         
495         vf->clicked_mark = 0;
496
497         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
498                                           &tpath, &column, NULL, NULL))
499                 {
500                 GtkTreeModel *store;
501                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
502
503                 if (bevent->button == MOUSE_BUTTON_LEFT &&
504                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
505                         return FALSE;
506
507                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
508                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
509
510                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
511
512                 gtk_tree_model_get_iter(store, &iter, tpath);
513                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
514 #if 0
515                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
516 #endif
517                 gtk_tree_path_free(tpath);
518                 }
519
520         VFLIST_INFO(vf, click_fd) = fd;
521
522         if (bevent->button == MOUSE_BUTTON_RIGHT)
523                 {
524                 vf->popup = vf_pop_menu(vf);
525                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
526                                 bevent->button, bevent->time);
527                 return TRUE;
528                 }
529
530         if (!fd) return FALSE;
531
532         if (bevent->button == MOUSE_BUTTON_MIDDLE)
533                 {
534                 if (!vflist_row_is_selected(vf, fd))
535                         {
536                         vflist_color_set(vf, fd, TRUE);
537                         }
538                 return TRUE;
539                 }
540
541
542         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
543             !(bevent->state & GDK_SHIFT_MASK ) &&
544             !(bevent->state & GDK_CONTROL_MASK ) &&
545             vflist_row_is_selected(vf, fd))
546                 {
547                 GtkTreeSelection *selection;
548
549                 gtk_widget_grab_focus(widget);
550
551
552                 /* returning FALSE and further processing of the event is needed for 
553                    correct operation of the expander, to show the sidecar files.
554                    It however resets the selection of multiple files. With this condition
555                    it should work for both cases */
556                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
557                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
558                 }
559
560 #if 0
561         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
562                 {
563                 if (vf->layout) layout_image_full_screen_start(vf->layout);
564                 }
565 #endif
566
567         return FALSE;
568 }
569
570 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
571 {
572         ViewFile *vf = data;
573         GtkTreePath *tpath;
574         GtkTreeIter iter;
575         FileData *fd = NULL;
576
577         if (bevent->button == MOUSE_BUTTON_MIDDLE)
578                 {
579                 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
580                 }
581
582         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
583                 {
584                 return TRUE;
585                 }
586
587         if ((bevent->x != 0 || bevent->y != 0) &&
588             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
589                                           &tpath, NULL, NULL, NULL))
590                 {
591                 GtkTreeModel *store;
592
593                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
594                 gtk_tree_model_get_iter(store, &iter, tpath);
595                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
596                 gtk_tree_path_free(tpath);
597                 }
598
599         if (bevent->button == MOUSE_BUTTON_MIDDLE)
600                 {
601                 if (fd && VFLIST_INFO(vf, click_fd) == fd)
602                         {
603                         GtkTreeSelection *selection;
604
605                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
606                         if (vflist_row_is_selected(vf, fd))
607                                 {
608                                 gtk_tree_selection_unselect_iter(selection, &iter);
609                                 }
610                         else
611                                 {
612                                 gtk_tree_selection_select_iter(selection, &iter);
613                                 }
614                         }
615                 return TRUE;
616                 }
617
618         if (fd && VFLIST_INFO(vf, click_fd) == fd &&
619             !(bevent->state & GDK_SHIFT_MASK ) &&
620             !(bevent->state & GDK_CONTROL_MASK ) &&
621             vflist_row_is_selected(vf, fd))
622                 {
623                 GtkTreeSelection *selection;
624
625                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
626                 gtk_tree_selection_unselect_all(selection);
627                 gtk_tree_selection_select_iter(selection, &iter);
628                 vflist_move_cursor(vf, &iter);
629 //              return TRUE;// FIXME - expand
630                 }
631
632         return FALSE;
633 }
634
635 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
636 {
637         FileData *read_ahead_fd = NULL;
638         gint row;
639         FileData *cur_fd;
640         if (!sel_fd) return;
641
642         cur_fd = layout_image_get_fd(vf->layout);
643         if (sel_fd == cur_fd) return; /* no change */
644
645         row = g_list_index(vf->list, sel_fd);
646         // FIXME sidecar data
647
648         if (sel_fd && options->image.enable_read_ahead && row >= 0)
649                 {
650                 if (row > g_list_index(vf->list, cur_fd) &&
651                     (guint) (row + 1) < vf_count(vf, NULL))
652                         {
653                         read_ahead_fd = vf_index_get_data(vf, row + 1);
654                         }
655                 else if (row > 0)
656                         {
657                         read_ahead_fd = vf_index_get_data(vf, row - 1);
658                         }
659                 }
660
661         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
662 }
663
664 static gint vflist_select_idle_cb(gpointer data)
665 {
666         ViewFile *vf = data;
667
668         if (!vf->layout)
669                 {
670                 VFLIST_INFO(vf, select_idle_id) = -1;
671                 return FALSE;
672                 }
673
674         vf_send_update(vf);
675
676         if (VFLIST_INFO(vf, select_fd))
677                 {
678                 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
679                 VFLIST_INFO(vf, select_fd) = NULL;
680                 }
681
682         VFLIST_INFO(vf, select_idle_id) = -1;
683         return FALSE;
684 }
685
686 static void vflist_select_idle_cancel(ViewFile *vf)
687 {
688         if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
689         VFLIST_INFO(vf, select_idle_id) = -1;
690 }
691
692 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
693                                  gboolean path_currently_selected, gpointer data)
694 {
695         ViewFile *vf = data;
696         GtkTreeIter iter;
697
698         if (!path_currently_selected &&
699             gtk_tree_model_get_iter(store, &iter, tpath))
700                 {
701                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
702                 }
703         else
704                 {
705                 VFLIST_INFO(vf, select_fd) = NULL;
706                 }
707
708         if (vf->layout &&
709             VFLIST_INFO(vf, select_idle_id) == -1)
710                 {
711                 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
712                 }
713
714         return TRUE;
715 }
716
717 /*
718  *-----------------------------------------------------------------------------
719  * misc
720  *-----------------------------------------------------------------------------
721  */
722
723 /*
724 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
725                                         gboolean path_currently_selected, gpointer data)
726 {
727         return TRUE;
728 }
729 */
730
731 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
732 {
733         gchar *size;
734         gchar *sidecars = NULL;
735         gchar *name_sidecars;
736         gchar *multiline;
737         const gchar *time = text_from_time(fd->date);
738         name_sidecars = (gchar *)fd->name;
739
740         if (fd->sidecar_files)
741                 {
742                 sidecars = file_data_sc_list_to_string(fd);
743                 name_sidecars = g_strdup_printf("%s %s", fd->name, sidecars);
744                 }
745         size = text_from_size(fd->size);
746         
747         multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
748
749         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
750                                         FILE_COLUMN_VERSION, fd->version,
751                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
752                                         FILE_COLUMN_MULTILINE, multiline,
753                                         FILE_COLUMN_NAME, name_sidecars,
754                                         FILE_COLUMN_SIZE, size,
755                                         FILE_COLUMN_DATE, time,
756 #define STORE_SET_IS_SLOW 1
757 #if STORE_SET_IS_SLOW   
758 /* this is 3x faster on a directory with 20000 files */
759                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
760                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
761                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
762                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
763                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
764                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
765 #if FILEDATA_MARKS_SIZE != 6
766 #error this needs to be updated  
767 #endif
768 #endif
769                                         FILE_COLUMN_COLOR, FALSE, -1);
770
771 #if !STORE_SET_IS_SLOW                                  
772         {
773         gint i;
774         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
775                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
776         }
777 #endif
778         g_free(size);
779         if (sidecars)
780                 {
781                 g_free(sidecars);
782                 g_free(name_sidecars);
783                 }
784         g_free(multiline);
785 }
786
787 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
788 {
789         GList *work;
790         GtkTreeIter iter;
791         gint valid;
792         gint num_ordered = 0;
793         gint num_prepended = 0;
794
795         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
796
797         work = list;
798         while (work)
799                 {
800                 gint match;
801                 FileData *fd = work->data;
802                 gint done = FALSE;
803
804                 while (!done)
805                         {
806                         FileData *old_fd = NULL;
807                         gint old_version = 0;
808
809                         if (valid)
810                                 {
811                                 num_ordered++;
812                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
813                                                    FILE_COLUMN_POINTER, &old_fd,
814                                                    FILE_COLUMN_VERSION, &old_version,
815                                                    -1);
816
817                                 if (fd == old_fd)
818                                         {
819                                         match = 0;
820                                         }
821                                 else
822                                         {
823                                         if (parent_iter)
824                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
825                                         else
826                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
827
828                                         if (match == 0) g_warning("multiple fd for the same path");
829                                         }
830                                         
831                                 }
832                         else
833                                 {
834                                 match = -1;
835                                 }
836
837                         if (match < 0)
838                                 {
839                                 GtkTreeIter new;
840
841                                 if (valid)
842                                         {
843                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
844                                         }
845                                 else
846                                         {
847                                         /*
848                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
849                                             and it seems to be much faster to add new entries to the beginning and reorder later
850                                         */
851                                         num_prepended++;
852                                         gtk_tree_store_prepend(store, &new, parent_iter);
853                                         }
854
855                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
856                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
857                                 
858                                 if (g_list_find(selected, fd))
859                                         {
860                                         /* renamed files - the same fd appears at different position - select it again*/
861                                         GtkTreeSelection *selection;
862                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
863                                         gtk_tree_selection_select_iter(selection, &new);
864                                         }
865
866                                 done = TRUE;
867                                 }
868                         else if (match > 0)
869                                 {
870                                 file_data_unref(old_fd);
871                                 valid = gtk_tree_store_remove(store, &iter);
872                                 }
873                         else
874                                 {
875                                 if (fd->version != old_version)
876                                         {
877                                         vflist_setup_iter(vf, store, &iter, fd);
878                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
879                                         }
880
881                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
882
883                                 done = TRUE;
884                                 }
885                         }
886                 work = work->next;
887                 }
888
889         while (valid)
890                 {
891                 FileData *old_fd;
892                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
893                 file_data_unref(old_fd);
894
895                 valid = gtk_tree_store_remove(store, &iter);
896                 }
897                 
898         /* move the prepended entries to the correct position */
899         if (num_prepended)
900                 {
901                 gint i;
902                 gint num_total = num_prepended + num_ordered;
903                 gint *new_order = g_malloc(num_total * sizeof(gint));
904                 
905                 for (i = 0; i < num_total; i++)
906                         {
907                         if (i < num_ordered)
908                                 new_order[i] = num_prepended + i;
909                         else
910                                 new_order[i] = num_total - 1 - i;
911                         }
912                 gtk_tree_store_reorder(store, parent_iter, new_order);
913
914                 g_free(new_order);
915                 }
916 }
917
918 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
919 {
920         gint i;
921         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
922         gint *new_order;
923         GtkTreeStore *store;
924         GList *work;
925
926         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
927         if (!vf->list) return;
928
929         work = vf->list;
930         i = 0;
931         while (work)
932                 {
933                 FileData *fd = work->data;
934                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
935                 i++;
936                 work = work->next;
937                 }
938
939         vf->sort_method = type;
940         vf->sort_ascend = ascend;
941
942         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
943
944         new_order = g_malloc(i * sizeof(gint));
945
946         work = vf->list;
947         i = 0;
948         while (work)
949                 {
950                 FileData *fd = work->data;
951                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
952                 i++;
953                 work = work->next;
954                 }
955
956         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
957         gtk_tree_store_reorder(store, NULL, new_order);
958
959         g_free(new_order);
960         g_hash_table_destroy(fd_idx_hash);
961 }
962
963 /*
964  *-----------------------------------------------------------------------------
965  * thumb updates
966  *-----------------------------------------------------------------------------
967  */
968
969 static gint vflist_thumb_next(ViewFile *vf);
970
971 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
972 {
973         GList *work = list;
974         while (work)
975                 {
976                 FileData *fd = work->data;
977                 work = work->next;
978
979                 if (fd->thumb_pixbuf) (*done)++;
980                 
981                 if (fd->sidecar_files) 
982                         {
983                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
984                         }
985                 (*count)++;
986                 }
987 }
988
989 static gdouble vflist_thumb_progress(ViewFile *vf)
990 {
991         gint count = 0;
992         gint done = 0;
993         
994         vflist_thumb_progress_count(vf->list, &count, &done);
995
996         DEBUG_1("thumb progress: %d of %d", done, count);
997         return (gdouble)done / count;
998 }
999
1000
1001 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1002 {
1003         if (vf->func_thumb_status)
1004                 {
1005                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1006                 }
1007 }
1008
1009 static void vflist_thumb_cleanup(ViewFile *vf)
1010 {
1011         vflist_thumb_status(vf, 0.0, NULL);
1012
1013         vf->thumbs_running = FALSE;
1014
1015         thumb_loader_free(vf->thumbs_loader);
1016         vf->thumbs_loader = NULL;
1017
1018         vf->thumbs_filedata = NULL;
1019 }
1020
1021 static void vflist_thumb_stop(ViewFile *vf)
1022 {
1023         if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1024 }
1025
1026 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1027 {
1028         GtkTreeStore *store;
1029         GtkTreeIter iter;
1030
1031         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1032
1033         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1034         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1035
1036         vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1037 }
1038
1039 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1040 {
1041         ViewFile *vf = data;
1042
1043         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1044                 {
1045                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1046                 }
1047
1048         while (vflist_thumb_next(vf));
1049 }
1050
1051 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1052 {
1053         ViewFile *vf = data;
1054
1055         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1056                 {
1057                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1058                 }
1059
1060         while (vflist_thumb_next(vf));
1061 }
1062
1063 static gint vflist_thumb_next(ViewFile *vf)
1064 {
1065         GtkTreePath *tpath;
1066         FileData *fd = NULL;
1067
1068         /* first check the visible files */
1069
1070         if (GTK_WIDGET_REALIZED(vf->listview) &&
1071             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1072                 {
1073                 GtkTreeModel *store;
1074                 GtkTreeIter iter;
1075                 gint valid = TRUE;
1076
1077                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1078                 gtk_tree_model_get_iter(store, &iter, tpath);
1079                 gtk_tree_path_free(tpath);
1080
1081                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1082                         {
1083                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1084                         if (fd->thumb_pixbuf) fd = NULL;
1085
1086                         valid = gtk_tree_model_iter_next(store, &iter);
1087                         }
1088                 }
1089
1090         /* then find first undone */
1091
1092         if (!fd)
1093                 {
1094                 GList *work = vf->list;
1095                 while (work && !fd)
1096                         {
1097                         FileData *fd_p = work->data;
1098                         if (!fd_p->thumb_pixbuf)
1099                                 fd = fd_p;
1100                         else
1101                                 {
1102                                 GList *work2 = fd_p->sidecar_files;
1103
1104                                 while (work2 && !fd)
1105                                         {
1106                                         fd_p = work2->data;
1107                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1108                                         work2 = work2->next;
1109                                         }
1110                                 }
1111                         work = work->next;
1112                         }
1113                 }
1114
1115         if (!fd)
1116                 {
1117                 /* done */
1118                 vflist_thumb_cleanup(vf);
1119                 return FALSE;
1120                 }
1121
1122         vf->thumbs_filedata = fd;
1123
1124         thumb_loader_free(vf->thumbs_loader);
1125
1126         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1127         thumb_loader_set_callbacks(vf->thumbs_loader,
1128                                    vflist_thumb_done_cb,
1129                                    vflist_thumb_error_cb,
1130                                    NULL,
1131                                    vf);
1132
1133         if (!thumb_loader_start(vf->thumbs_loader, fd))
1134                 {
1135                 /* set icon to unknown, continue */
1136                 DEBUG_1("thumb loader start failed %s", fd->path);
1137                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1138
1139                 return TRUE;
1140                 }
1141
1142         return FALSE;
1143 }
1144
1145 static void vflist_thumb_update(ViewFile *vf)
1146 {
1147         vflist_thumb_stop(vf);
1148         if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1149
1150         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1151         vf->thumbs_running = TRUE;
1152
1153         while (vflist_thumb_next(vf));
1154 }
1155
1156 /*
1157  *-----------------------------------------------------------------------------
1158  * row stuff
1159  *-----------------------------------------------------------------------------
1160  */
1161
1162 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1163 {
1164         return g_list_nth_data(vf->list, row);
1165 }
1166
1167 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1168 {
1169         gint p = 0;
1170         GList *work;
1171
1172         if (!path) return -1;
1173
1174         work = vf->list;
1175         while (work)
1176                 {
1177                 FileData *fd = work->data;
1178                 if (strcmp(path, fd->path) == 0) return p;
1179                 
1180                 work = work->next;
1181                 p++;
1182                 }
1183
1184         return -1;
1185 }
1186
1187 guint vflist_count(ViewFile *vf, gint64 *bytes)
1188 {
1189         if (bytes)
1190                 {
1191                 gint64 b = 0;
1192                 GList *work;
1193
1194                 work = vf->list;
1195                 while (work)
1196                         {
1197                         FileData *fd = work->data;
1198                         work = work->next;
1199                         b += fd->size;
1200                         }
1201
1202                 *bytes = b;
1203                 }
1204
1205         return g_list_length(vf->list);
1206 }
1207
1208 GList *vflist_get_list(ViewFile *vf)
1209 {
1210         GList *list = NULL;
1211         GList *work;
1212
1213         work = vf->list;
1214         while (work)
1215                 {
1216                 FileData *fd = work->data;
1217                 work = work->next;
1218
1219                 list = g_list_prepend(list, file_data_ref(fd));
1220                 }
1221
1222         return g_list_reverse(list);
1223 }
1224
1225 /*
1226  *-----------------------------------------------------------------------------
1227  * selections
1228  *-----------------------------------------------------------------------------
1229  */
1230
1231 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1232 {
1233         GtkTreeModel *store;
1234         GtkTreeSelection *selection;
1235         GList *slist;
1236         GList *work;
1237         gint found = FALSE;
1238
1239         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1240         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1241         work = slist;
1242         while (!found && work)
1243                 {
1244                 GtkTreePath *tpath = work->data;
1245                 FileData *fd_n;
1246                 GtkTreeIter iter;
1247
1248                 gtk_tree_model_get_iter(store, &iter, tpath);
1249                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1250                 if (fd_n == fd) found = TRUE;
1251                 work = work->next;
1252                 }
1253         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1254         g_list_free(slist);
1255
1256         return found;
1257 }
1258
1259 gint vflist_index_is_selected(ViewFile *vf, gint row)
1260 {
1261         FileData *fd;
1262
1263         fd = vf_index_get_data(vf, row);
1264         return vflist_row_is_selected(vf, fd);
1265 }
1266
1267 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1268 {
1269         GtkTreeModel *store;
1270         GtkTreeSelection *selection;
1271         GList *slist;
1272         guint count;
1273
1274         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1275         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1276
1277         if (bytes)
1278                 {
1279                 gint64 b = 0;
1280                 GList *work;
1281
1282                 work = slist;
1283                 while (work)
1284                         {
1285                         GtkTreePath *tpath = work->data;
1286                         GtkTreeIter iter;
1287                         FileData *fd;
1288
1289                         gtk_tree_model_get_iter(store, &iter, tpath);
1290                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1291                         b += fd->size;
1292
1293                         work = work->next;
1294                         }
1295
1296                 *bytes = b;
1297                 }
1298
1299         count = g_list_length(slist);
1300         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1301         g_list_free(slist);
1302
1303         return count;
1304 }
1305
1306 GList *vflist_selection_get_list(ViewFile *vf)
1307 {
1308         GtkTreeModel *store;
1309         GtkTreeSelection *selection;
1310         GList *slist;
1311         GList *list = NULL;
1312         GList *work;
1313
1314         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1315         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1316         work = slist;
1317         while (work)
1318                 {
1319                 GtkTreePath *tpath = work->data;
1320                 FileData *fd;
1321                 GtkTreeIter iter;
1322
1323                 gtk_tree_model_get_iter(store, &iter, tpath);
1324                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1325
1326                 list = g_list_prepend(list, file_data_ref(fd));
1327
1328                 work = work->next;
1329                 }
1330         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1331         g_list_free(slist);
1332
1333         return g_list_reverse(list);
1334 }
1335
1336 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1337 {
1338         GtkTreeModel *store;
1339         GtkTreeSelection *selection;
1340         GList *slist;
1341         GList *list = NULL;
1342         GList *work;
1343
1344         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1345         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1346         work = slist;
1347         while (work)
1348                 {
1349                 GtkTreePath *tpath = work->data;
1350                 FileData *fd;
1351                 GtkTreeIter iter;
1352
1353                 gtk_tree_model_get_iter(store, &iter, tpath);
1354                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1355
1356                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1357
1358                 work = work->next;
1359                 }
1360         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1361         g_list_free(slist);
1362
1363         return g_list_reverse(list);
1364 }
1365
1366 void vflist_select_all(ViewFile *vf)
1367 {
1368         GtkTreeSelection *selection;
1369
1370         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1371         gtk_tree_selection_select_all(selection);
1372
1373         VFLIST_INFO(vf, select_fd) = NULL;
1374 }
1375
1376 void vflist_select_none(ViewFile *vf)
1377 {
1378         GtkTreeSelection *selection;
1379
1380         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1381         gtk_tree_selection_unselect_all(selection);
1382 }
1383
1384 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1385 {
1386         GtkTreePath *tpath;
1387         gboolean result;
1388
1389         tpath = gtk_tree_model_get_path(store, iter);
1390         result = gtk_tree_path_prev(tpath);
1391         if (result)
1392                 gtk_tree_model_get_iter(store, iter, tpath);
1393
1394         gtk_tree_path_free(tpath);
1395
1396         return result;
1397 }
1398
1399 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1400 {
1401         if (!gtk_tree_model_get_iter_first(store, iter))
1402                 return FALSE;
1403
1404         while (TRUE)
1405                 {
1406                 GtkTreeIter next = *iter;
1407                 
1408                 if (gtk_tree_model_iter_next(store, &next))
1409                         *iter = next;
1410                 else
1411                         break;
1412                 }
1413         
1414         return TRUE;
1415 }
1416
1417 void vflist_select_invert(ViewFile *vf)
1418 {
1419         GtkTreeIter iter;
1420         GtkTreeSelection *selection;
1421         GtkTreeModel *store;
1422         gboolean valid;
1423
1424         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1425         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1426
1427         /* Backward iteration prevents scrolling to the end of the list,
1428          * it scrolls to the first selected row instead. */
1429         valid = tree_model_get_iter_last(store, &iter);
1430
1431         while (valid)
1432                 {
1433                 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1434
1435                 if (selected)
1436                         gtk_tree_selection_unselect_iter(selection, &iter);
1437                 else
1438                         gtk_tree_selection_select_iter(selection, &iter);
1439                                 
1440                 valid = tree_model_iter_prev(store, &iter);
1441                 }
1442 }
1443
1444 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1445 {
1446         GtkTreeIter iter;
1447
1448         if (vflist_find_row(vf, fd, &iter) < 0) return;
1449
1450         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1451
1452         if (!vflist_row_is_selected(vf, fd))
1453                 {
1454                 GtkTreeSelection *selection;
1455                 GtkTreeModel *store;
1456                 GtkTreePath *tpath;
1457
1458                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1459                 gtk_tree_selection_unselect_all(selection);
1460                 gtk_tree_selection_select_iter(selection, &iter);
1461                 vflist_move_cursor(vf, &iter);
1462
1463                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1464                 tpath = gtk_tree_model_get_path(store, &iter);
1465                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1466                 gtk_tree_path_free(tpath);
1467                 }
1468 }
1469
1470 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1471 {
1472         GList *work;
1473         
1474         if (sel_fd->parent) sel_fd = sel_fd->parent;
1475         work = vf->list;
1476         
1477         while (work)
1478                 {
1479                 gint match;
1480                 FileData *fd = work->data;
1481                 work = work->next;
1482                 
1483
1484                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1485                 
1486                 if (match >= 0)
1487                         {
1488                         vflist_select_by_fd(vf, fd);
1489                         break;
1490                         }
1491                 }
1492
1493 }
1494
1495 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1496 {
1497         GtkTreeModel *store;
1498         GtkTreeIter iter;
1499         GtkTreeSelection *selection;
1500         gint valid;
1501         gint n = mark - 1;
1502
1503         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1504
1505         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1506         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1507
1508         valid = gtk_tree_model_get_iter_first(store, &iter);
1509         while (valid)
1510                 {
1511                 FileData *fd;
1512                 gboolean mark_val, selected;
1513                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1514
1515                 mark_val = file_data_get_mark(fd, n);
1516                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1517
1518                 switch (mode)
1519                         {
1520                         case MTS_MODE_SET: selected = mark_val;
1521                                 break;
1522                         case MTS_MODE_OR: selected = mark_val | selected;
1523                                 break;
1524                         case MTS_MODE_AND: selected = mark_val & selected;
1525                                 break;
1526                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1527                                 break;
1528                         }
1529
1530                 if (selected)
1531                         gtk_tree_selection_select_iter(selection, &iter);
1532                 else
1533                         gtk_tree_selection_unselect_iter(selection, &iter);
1534
1535                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1536                 }
1537 }
1538
1539 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1540 {
1541         GtkTreeModel *store;
1542         GtkTreeSelection *selection;
1543         GList *slist;
1544         GList *work;
1545         gint n = mark - 1;
1546
1547         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1548
1549         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1550         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1551         work = slist;
1552         while (work)
1553                 {
1554                 GtkTreePath *tpath = work->data;
1555                 FileData *fd;
1556                 GtkTreeIter iter;
1557
1558                 gtk_tree_model_get_iter(store, &iter, tpath);
1559                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1560
1561                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1562
1563                 switch (mode)
1564                         {
1565                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1566                                 break;
1567                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1568                                 break;
1569                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1570                                 break;
1571                         }
1572                 
1573                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1574
1575                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1576
1577                 work = work->next;
1578                 }
1579         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1580         g_list_free(slist);
1581 }
1582
1583 /*
1584  *-----------------------------------------------------------------------------
1585  * core (population)
1586  *-----------------------------------------------------------------------------
1587  */
1588
1589 static void vflist_listview_set_columns(GtkWidget *listview, gint thumb)
1590 {
1591         GtkTreeViewColumn *column;
1592         GtkCellRenderer *cell;
1593         GList *list;
1594         gboolean multiline;
1595
1596         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1597         if (!column) return;
1598
1599         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1600
1601         list = gtk_tree_view_column_get_cell_renderers(column);
1602         if (!list) return;
1603         cell = list->data;
1604         g_list_free(list);
1605
1606         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1607         gtk_tree_view_column_set_visible(column, thumb);
1608
1609         multiline = (thumb && options->thumbnails.max_height >= 48);
1610         
1611         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1612         if (!column) return;
1613         gtk_tree_view_column_set_visible(column, multiline);
1614         if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1615
1616         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1617         if (!column) return;
1618         gtk_tree_view_column_set_visible(column, !multiline);
1619         if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1620
1621         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1622         if (!column) return;
1623         gtk_tree_view_column_set_visible(column, !multiline);
1624
1625         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1626         if (!column) return;
1627         gtk_tree_view_column_set_visible(column, !multiline);
1628
1629         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1630 }
1631
1632 static void vflist_populate_view(ViewFile *vf)
1633 {
1634         GtkTreeStore *store;
1635         gint thumbs;
1636         GtkTreeRowReference *visible_row = NULL;
1637         GtkTreePath *tpath;
1638         GList *selected;
1639
1640         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1641         thumbs = VFLIST_INFO(vf, thumbs_enabled);
1642
1643         vflist_thumb_stop(vf);
1644
1645         if (!vf->list)
1646                 {
1647                 vflist_store_clear(vf);
1648                 vf_send_update(vf);
1649                 return;
1650                 }
1651
1652         if (GTK_WIDGET_REALIZED(vf->listview) &&
1653             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1654                 {
1655                 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1656                 gtk_tree_path_free(tpath);
1657                 }
1658
1659         vflist_listview_set_columns(vf->listview, thumbs);
1660
1661         selected = vflist_selection_get_list(vf);
1662         
1663         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1664
1665         if (selected && vflist_selection_count(vf, NULL) == 0)
1666                 {
1667                 /* all selected files disappeared */
1668                 vflist_select_closest(vf, selected->data);
1669                 }       
1670
1671         filelist_free(selected);
1672         
1673         if (visible_row)
1674                 {
1675                 if (gtk_tree_row_reference_valid(visible_row))
1676                         {
1677                         tpath = gtk_tree_row_reference_get_path(visible_row);
1678                         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1679                         gtk_tree_path_free(tpath);
1680                         }
1681                 gtk_tree_row_reference_free(visible_row);
1682                 }
1683
1684         vf_send_update(vf);
1685         vflist_thumb_update(vf);
1686 }
1687
1688 gint vflist_refresh(ViewFile *vf)
1689 {
1690         GList *old_list;
1691         gint ret = TRUE;
1692
1693         old_list = vf->list;
1694         vf->list = NULL;
1695
1696         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1697         if (vf->dir_fd)
1698                 {
1699                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1700
1701                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1702
1703                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1704
1705                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1706                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1707                 }
1708
1709         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1710
1711         vflist_populate_view(vf);
1712
1713         filelist_free(old_list);
1714         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1715
1716         return ret;
1717 }
1718
1719
1720
1721 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1722
1723 #define CELL_HEIGHT_OVERRIDE 512
1724
1725 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1726 {
1727         GParamSpec *spec;
1728
1729         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1730         if (spec && G_IS_PARAM_SPEC_INT(spec))
1731                 {
1732                 GParamSpecInt *spec_int;
1733
1734                 spec_int = G_PARAM_SPEC_INT(spec);
1735                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1736                 }
1737 }
1738
1739 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1740 {
1741         static GdkColor color;
1742         static GtkWidget *done = NULL;
1743
1744         if (done != widget)
1745                 {
1746                 GtkStyle *style;
1747
1748                 style = gtk_widget_get_style(widget);
1749                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1750                 shift_color(&color, -1, 0);
1751                 done = widget;
1752                 }
1753
1754         return &color;
1755 }
1756
1757 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1758                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1759 {
1760         ViewFile *vf = data;
1761         gboolean set;
1762
1763         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1764         g_object_set(G_OBJECT(cell),
1765                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1766                      "cell-background-set", set, NULL);
1767 }
1768
1769 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1770 {
1771         GtkTreeViewColumn *column;
1772         GtkCellRenderer *renderer;
1773
1774         column = gtk_tree_view_column_new();
1775         gtk_tree_view_column_set_title(column, title);
1776         gtk_tree_view_column_set_min_width(column, 4);
1777
1778         if (!image)
1779                 {
1780                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1781                 renderer = gtk_cell_renderer_text_new();
1782                 if (right_justify)
1783                         {
1784                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1785                         }
1786                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1787                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1788                 if (expand)
1789                         gtk_tree_view_column_set_expand(column, TRUE);
1790                 }
1791         else
1792                 {
1793                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1794                 renderer = gtk_cell_renderer_pixbuf_new();
1795                 cell_renderer_height_override(renderer);
1796                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1797                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1798                 }
1799
1800         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1801         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1802         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1803
1804         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1805 }
1806
1807 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1808 {
1809         ViewFile *vf = data;
1810         GtkTreeStore *store;
1811         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1812         GtkTreeIter iter;
1813         FileData *fd;
1814         gboolean mark;
1815         guint col_idx;
1816
1817         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1818         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1819                 return;
1820
1821         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1822
1823         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1824
1825         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1826         mark = !mark;
1827         file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1828         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1829         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1830
1831         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1832         gtk_tree_path_free(path);
1833 }
1834
1835 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1836 {
1837         GtkTreeViewColumn *column;
1838         GtkCellRenderer *renderer;
1839         GtkTreeStore *store;
1840         gint index;
1841
1842         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1843
1844         renderer = gtk_cell_renderer_toggle_new();
1845         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1846
1847         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1848         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1849         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1850
1851         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1852         gtk_tree_view_column_set_fixed_width(column, 18);
1853         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1854
1855
1856         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1857 }
1858
1859 /*
1860  *-----------------------------------------------------------------------------
1861  * base
1862  *-----------------------------------------------------------------------------
1863  */
1864
1865 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1866 {
1867         if (!dir_fd) return FALSE;
1868         if (vf->dir_fd == dir_fd) return TRUE;
1869
1870         file_data_unref(vf->dir_fd);
1871         vf->dir_fd = file_data_ref(dir_fd);
1872
1873         /* force complete reload */
1874         vflist_store_clear(vf);
1875
1876         filelist_free(vf->list);
1877         vf->list = NULL;
1878
1879         return vf_refresh(vf);
1880 }
1881
1882 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1883 {
1884         ViewFile *vf = data;
1885
1886         file_data_unregister_notify_func(vf_notify_cb, vf);
1887
1888         vflist_select_idle_cancel(vf);
1889         vf_refresh_idle_cancel(vf);
1890         vflist_thumb_stop(vf);
1891
1892         filelist_free(vf->list);
1893 }
1894
1895 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1896 {
1897         GtkTreeStore *store;
1898         GtkTreeSelection *selection;
1899
1900         GType flist_types[FILE_COLUMN_COUNT];
1901         gint i;
1902         gint column;
1903
1904         vf->info = g_new0(ViewFileInfoList, 1);
1905         
1906         VFLIST_INFO(vf, click_fd) = NULL;
1907         VFLIST_INFO(vf, select_fd) = NULL;
1908         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1909
1910         VFLIST_INFO(vf, select_idle_id) = -1;
1911
1912         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1913         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1914         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1915         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1916         flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1917         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1918         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1919         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1920         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1921                 flist_types[i] = G_TYPE_BOOLEAN;
1922
1923         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1924
1925         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1926         g_object_unref(store);
1927
1928         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1929         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1930         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1931
1932         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1933         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1934
1935         column = 0;
1936
1937         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1938                 {
1939                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1940                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1941                 column++;
1942                 }
1943
1944         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1945         g_assert(column == FILE_VIEW_COLUMN_THUMB);
1946         column++;
1947         
1948         vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1949         g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1950         column++;
1951
1952         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1953         g_assert(column == FILE_VIEW_COLUMN_NAME);
1954         column++;
1955
1956         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1957         g_assert(column == FILE_VIEW_COLUMN_SIZE);
1958         column++;
1959
1960         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1961         g_assert(column == FILE_VIEW_COLUMN_DATE);
1962         column++;
1963
1964         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1965         return vf;
1966 }
1967
1968 void vflist_thumb_set(ViewFile *vf, gint enable)
1969 {
1970         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1971
1972         VFLIST_INFO(vf, thumbs_enabled) = enable;
1973         if (vf->layout) vf_refresh(vf);
1974 }
1975
1976 void vflist_marks_set(ViewFile *vf, gint enable)
1977 {
1978         GList *columns, *work;
1979
1980         if (vf->marks_enabled == enable) return;
1981
1982         vf->marks_enabled = enable;
1983
1984         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1985
1986         work = columns;
1987         while (work)
1988                 {
1989                 GtkTreeViewColumn *column = work->data;
1990                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1991                 work = work->next;
1992
1993                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1994                         gtk_tree_view_column_set_visible(column, enable);
1995                 }
1996
1997         g_list_free(columns);
1998         //vf_refresh(vf);
1999 }
2000