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