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