Silent unused variable warning.
[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         gchar *size;
727         gchar *sidecars = NULL;
728
729         if (fd->sidecar_files)
730                 sidecars = file_data_sc_list_to_string(fd);
731         size = text_from_size(fd->size);
732
733         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
734                                         FILE_COLUMN_VERSION, fd->version,
735                                         FILE_COLUMN_THUMB, fd->pixbuf,
736                                         FILE_COLUMN_NAME, fd->name,
737                                         FILE_COLUMN_SIDECARS, sidecars,
738                                         FILE_COLUMN_SIZE, size,
739                                         FILE_COLUMN_DATE, text_from_time(fd->date),
740 #define STORE_SET_IS_SLOW 1
741 #if STORE_SET_IS_SLOW   
742 /* this is 3x faster on a directory with 20000 files */
743                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
744                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
745                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
746                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
747                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
748                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
749 #if FILEDATA_MARKS_SIZE != 6
750 #error this needs to be updated  
751 #endif
752 #endif
753                                         FILE_COLUMN_COLOR, FALSE, -1);
754
755 #if !STORE_SET_IS_SLOW                                  
756         {
757         gint i;
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         }
761 #endif
762         g_free(size);
763         if (sidecars)
764                 g_free(sidecars);
765 }
766
767 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
768 {
769         GList *work;
770         GtkTreeIter iter;
771         gint valid;
772
773         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
774
775         work = list;
776         while (work)
777                 {
778                 gint match;
779                 FileData *fd = work->data;
780                 gint done = FALSE;
781
782                 while (!done)
783                         {
784                         FileData *old_fd = NULL;
785                         gint old_version = 0;
786
787                         if (valid)
788                                 {
789                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
790                                                    FILE_COLUMN_POINTER, &old_fd,
791                                                    FILE_COLUMN_VERSION, &old_version,
792                                                    -1);
793
794                                 if (fd == old_fd)
795                                         {
796                                         match = 0;
797                                         }
798                                 else
799                                         {
800                                         if (parent_iter)
801                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
802                                         else
803                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
804
805                                         if (match == 0) g_warning("multiple fd for the same path");
806                                         }
807                                         
808                                 }
809                         else
810                                 {
811                                 match = -1;
812                                 }
813
814                         if (match < 0)
815                                 {
816                                 GtkTreeIter new;
817
818                                 if (valid)
819                                         {
820                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
821                                         }
822                                 else
823                                         {
824                                         gtk_tree_store_append(store, &new, parent_iter);
825                                         }
826
827                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
828                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
829                                 
830                                 if (g_list_find(selected, fd))
831                                         {
832                                         /* renamed files - the same fd appears at different position - select it again*/
833                                         GtkTreeSelection *selection;
834                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
835                                         gtk_tree_selection_select_iter(selection, &new);
836                                         }
837
838                                 done = TRUE;
839                                 }
840                         else if (match > 0)
841                                 {
842                                 file_data_unref(old_fd);
843                                 valid = gtk_tree_store_remove(store, &iter);
844                                 }
845                         else
846                                 {
847                                 if (fd->version != old_version)
848                                         {
849                                         vflist_setup_iter(vf, store, &iter, fd);
850                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
851                                         }
852
853                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
854
855                                 done = TRUE;
856                                 }
857                         }
858                 work = work->next;
859                 }
860
861         while (valid)
862                 {
863                 FileData *old_fd;
864                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
865                 file_data_unref(old_fd);
866
867                 valid = gtk_tree_store_remove(store, &iter);
868                 }
869 }
870
871 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
872 {
873         gint i;
874         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
875         gint *new_order;
876         GtkTreeStore *store;
877         GList *work;
878
879         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
880         if (!vf->list) return;
881
882         work = vf->list;
883         i = 0;
884         while (work)
885                 {
886                 FileData *fd = work->data;
887                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
888                 i++;
889                 work = work->next;
890                 }
891
892         vf->sort_method = type;
893         vf->sort_ascend = ascend;
894
895         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
896
897         new_order = g_malloc(i * sizeof(gint));
898
899         work = vf->list;
900         i = 0;
901         while (work)
902                 {
903                 FileData *fd = work->data;
904                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
905                 i++;
906                 work = work->next;
907                 }
908
909         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
910         gtk_tree_store_reorder(store, NULL, new_order);
911
912         g_free(new_order);
913         g_hash_table_destroy(fd_idx_hash);
914 }
915
916 /*
917  *-----------------------------------------------------------------------------
918  * thumb updates
919  *-----------------------------------------------------------------------------
920  */
921
922 static gint vflist_thumb_next(ViewFile *vf);
923
924 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
925 {
926         if (vf->func_thumb_status)
927                 {
928                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
929                 }
930 }
931
932 static void vflist_thumb_cleanup(ViewFile *vf)
933 {
934         vflist_thumb_status(vf, 0.0, NULL);
935
936         vf->thumbs_count = 0;
937         vf->thumbs_running = FALSE;
938
939         thumb_loader_free(vf->thumbs_loader);
940         vf->thumbs_loader = NULL;
941
942         vf->thumbs_filedata = NULL;
943 }
944
945 static void vflist_thumb_stop(ViewFile *vf)
946 {
947         if (vf->thumbs_running) vflist_thumb_cleanup(vf);
948 }
949
950 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
951 {
952         GtkTreeStore *store;
953         GtkTreeIter iter;
954
955         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
956
957         if (fd->pixbuf) g_object_unref(fd->pixbuf);
958         fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
959
960         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
961         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
962
963         vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
964 }
965
966 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
967 {
968         ViewFile *vf = data;
969
970         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
971                 {
972                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
973                 }
974
975         while (vflist_thumb_next(vf));
976 }
977
978 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
979 {
980         ViewFile *vf = data;
981
982         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
983                 {
984                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
985                 }
986
987         while (vflist_thumb_next(vf));
988 }
989
990 static gint vflist_thumb_next(ViewFile *vf)
991 {
992         GtkTreePath *tpath;
993         FileData *fd = NULL;
994
995         /* first check the visible files */
996
997         if (GTK_WIDGET_REALIZED(vf->listview) &&
998             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
999                 {
1000                 GtkTreeModel *store;
1001                 GtkTreeIter iter;
1002                 gint valid = TRUE;
1003
1004                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1005                 gtk_tree_model_get_iter(store, &iter, tpath);
1006                 gtk_tree_path_free(tpath);
1007
1008                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1009                         {
1010                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1011                         if (fd->pixbuf) fd = NULL;
1012
1013                         valid = gtk_tree_model_iter_next(store, &iter);
1014                         }
1015                 }
1016
1017         /* then find first undone */
1018
1019         if (!fd)
1020                 {
1021                 GList *work = vf->list;
1022                 while (work && !fd)
1023                         {
1024                         FileData *fd_p = work->data;
1025                         if (!fd_p->pixbuf)
1026                                 fd = fd_p;
1027                         else
1028                                 {
1029                                 GList *work2 = fd_p->sidecar_files;
1030
1031                                 while (work2 && !fd)
1032                                         {
1033                                         fd_p = work2->data;
1034                                         if (!fd_p->pixbuf) fd = fd_p;
1035                                         work2 = work2->next;
1036                                         }
1037                                 }
1038                         work = work->next;
1039                         }
1040                 }
1041
1042         if (!fd)
1043                 {
1044                 /* done */
1045                 vflist_thumb_cleanup(vf);
1046                 return FALSE;
1047                 }
1048
1049         vf->thumbs_count++;
1050
1051         vf->thumbs_filedata = fd;
1052
1053         thumb_loader_free(vf->thumbs_loader);
1054
1055         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1056         thumb_loader_set_callbacks(vf->thumbs_loader,
1057                                    vflist_thumb_done_cb,
1058                                    vflist_thumb_error_cb,
1059                                    NULL,
1060                                    vf);
1061
1062         if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1063                 {
1064                 /* set icon to unknown, continue */
1065                 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1066                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1067
1068                 return TRUE;
1069                 }
1070
1071         return FALSE;
1072 }
1073
1074 static void vflist_thumb_update(ViewFile *vf)
1075 {
1076         vflist_thumb_stop(vf);
1077         if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1078
1079         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1080         vf->thumbs_running = TRUE;
1081
1082         while (vflist_thumb_next(vf));
1083 }
1084
1085 /*
1086  *-----------------------------------------------------------------------------
1087  * row stuff
1088  *-----------------------------------------------------------------------------
1089  */
1090
1091 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1092 {
1093         return g_list_nth_data(vf->list, row);
1094 }
1095
1096 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1097 {
1098         gint p = 0;
1099         GList *work;
1100
1101         if (!path) return -1;
1102
1103         work = vf->list;
1104         while (work)
1105                 {
1106                 FileData *fd = work->data;
1107                 if (strcmp(path, fd->path) == 0) return p;
1108                 
1109                 work = work->next;
1110                 p++;
1111                 }
1112
1113         return -1;
1114 }
1115
1116 guint vflist_count(ViewFile *vf, gint64 *bytes)
1117 {
1118         if (bytes)
1119                 {
1120                 gint64 b = 0;
1121                 GList *work;
1122
1123                 work = vf->list;
1124                 while (work)
1125                         {
1126                         FileData *fd = work->data;
1127                         work = work->next;
1128                         b += fd->size;
1129                         }
1130
1131                 *bytes = b;
1132                 }
1133
1134         return g_list_length(vf->list);
1135 }
1136
1137 GList *vflist_get_list(ViewFile *vf)
1138 {
1139         GList *list = NULL;
1140         GList *work;
1141
1142         work = vf->list;
1143         while (work)
1144                 {
1145                 FileData *fd = work->data;
1146                 work = work->next;
1147
1148                 list = g_list_prepend(list, file_data_ref(fd));
1149                 }
1150
1151         return g_list_reverse(list);
1152 }
1153
1154 /*
1155  *-----------------------------------------------------------------------------
1156  * selections
1157  *-----------------------------------------------------------------------------
1158  */
1159
1160 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1161 {
1162         GtkTreeModel *store;
1163         GtkTreeSelection *selection;
1164         GList *slist;
1165         GList *work;
1166         gint found = FALSE;
1167
1168         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1169         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1170         work = slist;
1171         while (!found && work)
1172                 {
1173                 GtkTreePath *tpath = work->data;
1174                 FileData *fd_n;
1175                 GtkTreeIter iter;
1176
1177                 gtk_tree_model_get_iter(store, &iter, tpath);
1178                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1179                 if (fd_n == fd) found = TRUE;
1180                 work = work->next;
1181                 }
1182         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1183         g_list_free(slist);
1184
1185         return found;
1186 }
1187
1188 gint vflist_index_is_selected(ViewFile *vf, gint row)
1189 {
1190         FileData *fd;
1191
1192         fd = vf_index_get_data(vf, row);
1193         return vflist_row_is_selected(vf, fd);
1194 }
1195
1196 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1197 {
1198         GtkTreeModel *store;
1199         GtkTreeSelection *selection;
1200         GList *slist;
1201         guint count;
1202
1203         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1204         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1205
1206         if (bytes)
1207                 {
1208                 gint64 b = 0;
1209                 GList *work;
1210
1211                 work = slist;
1212                 while (work)
1213                         {
1214                         GtkTreePath *tpath = work->data;
1215                         GtkTreeIter iter;
1216                         FileData *fd;
1217
1218                         gtk_tree_model_get_iter(store, &iter, tpath);
1219                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1220                         b += fd->size;
1221
1222                         work = work->next;
1223                         }
1224
1225                 *bytes = b;
1226                 }
1227
1228         count = g_list_length(slist);
1229         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1230         g_list_free(slist);
1231
1232         return count;
1233 }
1234
1235 GList *vflist_selection_get_list(ViewFile *vf)
1236 {
1237         GtkTreeModel *store;
1238         GtkTreeSelection *selection;
1239         GList *slist;
1240         GList *list = NULL;
1241         GList *work;
1242
1243         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1244         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1245         work = slist;
1246         while (work)
1247                 {
1248                 GtkTreePath *tpath = work->data;
1249                 FileData *fd;
1250                 GtkTreeIter iter;
1251
1252                 gtk_tree_model_get_iter(store, &iter, tpath);
1253                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1254
1255                 list = g_list_prepend(list, file_data_ref(fd));
1256
1257                 work = work->next;
1258                 }
1259         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1260         g_list_free(slist);
1261
1262         return g_list_reverse(list);
1263 }
1264
1265 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1266 {
1267         GtkTreeModel *store;
1268         GtkTreeSelection *selection;
1269         GList *slist;
1270         GList *list = NULL;
1271         GList *work;
1272
1273         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1274         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1275         work = slist;
1276         while (work)
1277                 {
1278                 GtkTreePath *tpath = work->data;
1279                 FileData *fd;
1280                 GtkTreeIter iter;
1281
1282                 gtk_tree_model_get_iter(store, &iter, tpath);
1283                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1284
1285                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1286
1287                 work = work->next;
1288                 }
1289         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1290         g_list_free(slist);
1291
1292         return g_list_reverse(list);
1293 }
1294
1295 void vflist_select_all(ViewFile *vf)
1296 {
1297         GtkTreeSelection *selection;
1298
1299         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1300         gtk_tree_selection_select_all(selection);
1301
1302         VFLIST_INFO(vf, select_fd) = NULL;
1303 }
1304
1305 void vflist_select_none(ViewFile *vf)
1306 {
1307         GtkTreeSelection *selection;
1308
1309         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1310         gtk_tree_selection_unselect_all(selection);
1311 }
1312
1313 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1314 {
1315         GtkTreePath *tpath;
1316         gboolean result;
1317
1318         tpath = gtk_tree_model_get_path(store, iter);
1319         result = gtk_tree_path_prev(tpath);
1320         if (result)
1321                 gtk_tree_model_get_iter(store, iter, tpath);
1322
1323         gtk_tree_path_free(tpath);
1324
1325         return result;
1326 }
1327
1328 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1329 {
1330         if (!gtk_tree_model_get_iter_first(store, iter))
1331                 return FALSE;
1332
1333         while (TRUE)
1334                 {
1335                 GtkTreeIter next = *iter;
1336                 
1337                 if (gtk_tree_model_iter_next(store, &next))
1338                         *iter = next;
1339                 else
1340                         break;
1341                 }
1342         
1343         return TRUE;
1344 }
1345
1346 void vflist_select_invert(ViewFile *vf)
1347 {
1348         GtkTreeIter iter;
1349         GtkTreeSelection *selection;
1350         GtkTreeModel *store;
1351         gboolean valid;
1352
1353         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1354         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1355
1356         /* Backward iteration prevents scrolling to the end of the list,
1357          * it scrolls to the first selected row instead. */
1358         valid = tree_model_get_iter_last(store, &iter);
1359
1360         while (valid)
1361                 {
1362                 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1363
1364                 if (selected)
1365                         gtk_tree_selection_unselect_iter(selection, &iter);
1366                 else
1367                         gtk_tree_selection_select_iter(selection, &iter);
1368                                 
1369                 valid = tree_model_iter_prev(store, &iter);
1370                 }
1371 }
1372
1373 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1374 {
1375         GtkTreeIter iter;
1376
1377         if (vflist_find_row(vf, fd, &iter) < 0) return;
1378
1379         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1380
1381         if (!vflist_row_is_selected(vf, fd))
1382                 {
1383                 GtkTreeSelection *selection;
1384                 GtkTreeModel *store;
1385                 GtkTreePath *tpath;
1386
1387                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1388                 gtk_tree_selection_unselect_all(selection);
1389                 gtk_tree_selection_select_iter(selection, &iter);
1390                 vflist_move_cursor(vf, &iter);
1391
1392                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1393                 tpath = gtk_tree_model_get_path(store, &iter);
1394                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1395                 gtk_tree_path_free(tpath);
1396                 }
1397 }
1398
1399 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1400 {
1401         GList *work;
1402         
1403         if (sel_fd->parent) sel_fd = sel_fd->parent;
1404         work = vf->list;
1405         
1406         while (work)
1407                 {
1408                 gint match;
1409                 FileData *fd = work->data;
1410                 work = work->next;
1411                 
1412
1413                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1414                 
1415                 if (match >= 0)
1416                         {
1417                         vflist_select_by_fd(vf, fd);
1418                         break;
1419                         }
1420                 }
1421
1422 }
1423
1424 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1425 {
1426         GtkTreeModel *store;
1427         GtkTreeIter iter;
1428         GtkTreeSelection *selection;
1429         gint valid;
1430         gint n = mark - 1;
1431
1432         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1433
1434         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1435         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1436
1437         valid = gtk_tree_model_get_iter_first(store, &iter);
1438         while (valid)
1439                 {
1440                 FileData *fd;
1441                 gboolean mark_val, selected;
1442                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1443
1444                 mark_val = file_data_get_mark(fd, n);
1445                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1446
1447                 switch (mode)
1448                         {
1449                         case MTS_MODE_SET: selected = mark_val;
1450                                 break;
1451                         case MTS_MODE_OR: selected = mark_val | selected;
1452                                 break;
1453                         case MTS_MODE_AND: selected = mark_val & selected;
1454                                 break;
1455                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1456                                 break;
1457                         }
1458
1459                 if (selected)
1460                         gtk_tree_selection_select_iter(selection, &iter);
1461                 else
1462                         gtk_tree_selection_unselect_iter(selection, &iter);
1463
1464                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1465                 }
1466 }
1467
1468 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1469 {
1470         GtkTreeModel *store;
1471         GtkTreeSelection *selection;
1472         GList *slist;
1473         GList *work;
1474         gint n = mark - 1;
1475
1476         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1477
1478         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1479         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1480         work = slist;
1481         while (work)
1482                 {
1483                 GtkTreePath *tpath = work->data;
1484                 FileData *fd;
1485                 GtkTreeIter iter;
1486
1487                 gtk_tree_model_get_iter(store, &iter, tpath);
1488                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1489
1490                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1491
1492                 switch (mode)
1493                         {
1494                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1495                                 break;
1496                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1497                                 break;
1498                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1499                                 break;
1500                         }
1501                 
1502                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1503
1504                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1505
1506                 work = work->next;
1507                 }
1508         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1509         g_list_free(slist);
1510 }
1511
1512 /*
1513  *-----------------------------------------------------------------------------
1514  * core (population)
1515  *-----------------------------------------------------------------------------
1516  */
1517
1518 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1519 {
1520         GtkTreeViewColumn *column;
1521         GtkCellRenderer *cell;
1522         GList *list;
1523
1524         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), 0); /* first column is thumbnail */
1525         if (!column) return;
1526
1527         gtk_tree_view_column_set_visible(column, thumb);
1528
1529         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 40);
1530
1531         list = gtk_tree_view_column_get_cell_renderers(column);
1532         if (!list) return;
1533         cell = list->data;
1534         g_list_free(list);
1535
1536         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1537         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1538 }
1539
1540 static void vflist_populate_view(ViewFile *vf)
1541 {
1542         GtkTreeStore *store;
1543         gint thumbs;
1544         GtkTreeRowReference *visible_row = NULL;
1545         GtkTreePath *tpath;
1546         GList *selected;
1547
1548         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1549         thumbs = VFLIST_INFO(vf, thumbs_enabled);
1550
1551         vflist_thumb_stop(vf);
1552
1553         if (!vf->list)
1554                 {
1555                 vflist_store_clear(vf);
1556                 vf_send_update(vf);
1557                 return;
1558                 }
1559
1560         if (GTK_WIDGET_REALIZED(vf->listview) &&
1561             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1562                 {
1563                 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1564                 gtk_tree_path_free(tpath);
1565                 }
1566
1567         vflist_listview_set_height(vf->listview, thumbs);
1568
1569         selected = vflist_selection_get_list(vf);
1570         
1571         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1572
1573         if (selected && vflist_selection_count(vf, NULL) == 0)
1574                 {
1575                 /* all selected files disappeared */
1576                 vflist_select_closest(vf, selected->data);
1577                 }       
1578
1579         filelist_free(selected);
1580         
1581         if (visible_row)
1582                 {
1583                 if (gtk_tree_row_reference_valid(visible_row))
1584                         {
1585                         tpath = gtk_tree_row_reference_get_path(visible_row);
1586                         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1587                         gtk_tree_path_free(tpath);
1588                         }
1589                 gtk_tree_row_reference_free(visible_row);
1590                 }
1591
1592         vf_send_update(vf);
1593         vflist_thumb_update(vf);
1594 }
1595
1596 gint vflist_refresh(ViewFile *vf)
1597 {
1598         GList *old_list;
1599         gint ret = TRUE;
1600
1601         old_list = vf->list;
1602         vf->list = NULL;
1603
1604         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1605         if (vf->dir_fd)
1606                 {
1607                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1608
1609                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1610
1611                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1612
1613                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1614                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1615                 }
1616
1617         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1618
1619         vflist_populate_view(vf);
1620
1621         filelist_free(old_list);
1622         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1623
1624         return ret;
1625 }
1626
1627
1628
1629 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1630
1631 #define CELL_HEIGHT_OVERRIDE 512
1632
1633 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1634 {
1635         GParamSpec *spec;
1636
1637         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1638         if (spec && G_IS_PARAM_SPEC_INT(spec))
1639                 {
1640                 GParamSpecInt *spec_int;
1641
1642                 spec_int = G_PARAM_SPEC_INT(spec);
1643                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1644                 }
1645 }
1646
1647 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1648 {
1649         static GdkColor color;
1650         static GtkWidget *done = NULL;
1651
1652         if (done != widget)
1653                 {
1654                 GtkStyle *style;
1655
1656                 style = gtk_widget_get_style(widget);
1657                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1658                 shift_color(&color, -1, 0);
1659                 done = widget;
1660                 }
1661
1662         return &color;
1663 }
1664
1665 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1666                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1667 {
1668         ViewFile *vf = data;
1669         gboolean set;
1670
1671         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1672         g_object_set(G_OBJECT(cell),
1673                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1674                      "cell-background-set", set, NULL);
1675 }
1676
1677 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1678 {
1679         GtkTreeViewColumn *column;
1680         GtkCellRenderer *renderer;
1681
1682         column = gtk_tree_view_column_new();
1683         gtk_tree_view_column_set_title(column, title);
1684         gtk_tree_view_column_set_min_width(column, 4);
1685
1686         if (!image)
1687                 {
1688                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1689                 renderer = gtk_cell_renderer_text_new();
1690                 if (right_justify)
1691                         {
1692                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1693                         }
1694                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1695                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1696                 if (expand)
1697                         gtk_tree_view_column_set_expand(column, TRUE);
1698                 }
1699         else
1700                 {
1701                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1702                 renderer = gtk_cell_renderer_pixbuf_new();
1703                 cell_renderer_height_override(renderer);
1704                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1705                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1706                 }
1707
1708         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1709         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1710         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1711
1712         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1713 }
1714
1715 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1716 {
1717         ViewFile *vf = data;
1718         GtkTreeStore *store;
1719         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1720         GtkTreeIter iter;
1721         FileData *fd;
1722         gboolean mark;
1723         guint col_idx;
1724
1725         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1726         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1727                 return;
1728
1729         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1730
1731         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1732
1733         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1734         mark = !mark;
1735         file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1736         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1737         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1738
1739         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1740         gtk_tree_path_free(path);
1741 }
1742
1743 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1744 {
1745         GtkTreeViewColumn *column;
1746         GtkCellRenderer *renderer;
1747         GtkTreeStore *store;
1748         gint index;
1749
1750         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1751
1752         renderer = gtk_cell_renderer_toggle_new();
1753         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1754
1755         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1756         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1757         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1758
1759         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1760         gtk_tree_view_column_set_fixed_width(column, 16);
1761         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1762
1763
1764         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1765 }
1766
1767 /*
1768  *-----------------------------------------------------------------------------
1769  * base
1770  *-----------------------------------------------------------------------------
1771  */
1772
1773 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1774 {
1775         if (!dir_fd) return FALSE;
1776         if (vf->dir_fd == dir_fd) return TRUE;
1777
1778         file_data_unref(vf->dir_fd);
1779         vf->dir_fd = file_data_ref(dir_fd);
1780
1781         /* force complete reload */
1782         vflist_store_clear(vf);
1783
1784         filelist_free(vf->list);
1785         vf->list = NULL;
1786
1787         return vf_refresh(vf);
1788 }
1789
1790 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1791 {
1792         ViewFile *vf = data;
1793
1794         file_data_unregister_notify_func(vf_notify_cb, vf);
1795
1796         vflist_select_idle_cancel(vf);
1797         vf_refresh_idle_cancel(vf);
1798         vflist_thumb_stop(vf);
1799
1800         filelist_free(vf->list);
1801 }
1802
1803 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1804 {
1805         GtkTreeStore *store;
1806         GtkTreeSelection *selection;
1807
1808         GType flist_types[FILE_COLUMN_COUNT];
1809         int i;
1810
1811         vf->info = g_new0(ViewFileInfoList, 1);
1812         
1813         VFLIST_INFO(vf, click_fd) = NULL;
1814         VFLIST_INFO(vf, select_fd) = NULL;
1815         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1816
1817         VFLIST_INFO(vf, select_idle_id) = -1;
1818
1819         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1820         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1821         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1822         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1823         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1824         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1825         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1826         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1827         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1828                 flist_types[i] = G_TYPE_BOOLEAN;
1829
1830         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1831
1832         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1833         g_object_unref(store);
1834
1835         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1836         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1837         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1838
1839         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1840         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1841
1842         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1843
1844         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1845                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1846
1847         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1848         vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1849
1850         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1851         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1852
1853         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1854         return vf;
1855 }
1856
1857 void vflist_thumb_set(ViewFile *vf, gint enable)
1858 {
1859         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1860
1861         VFLIST_INFO(vf, thumbs_enabled) = enable;
1862         if (vf->layout) vf_refresh(vf);
1863 }
1864
1865 void vflist_marks_set(ViewFile *vf, gint enable)
1866 {
1867         GList *columns, *work;
1868
1869         if (vf->marks_enabled == enable) return;
1870
1871         vf->marks_enabled = enable;
1872
1873         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1874
1875         work = columns;
1876         while (work)
1877                 {
1878                 GtkTreeViewColumn *column = work->data;
1879                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1880                 work = work->next;
1881
1882                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1883                         gtk_tree_view_column_set_visible(column, enable);
1884                 }
1885
1886         g_list_free(columns);
1887         //vf_refresh(vf);
1888 }
1889