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