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