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