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