use multiline description in vflist with large thumbnails
[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         if (fd->pixbuf) g_object_unref(fd->pixbuf);
1012         fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1013
1014         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1015         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1016
1017         vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1018 }
1019
1020 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1021 {
1022         ViewFile *vf = data;
1023
1024         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1025                 {
1026                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1027                 }
1028
1029         while (vflist_thumb_next(vf));
1030 }
1031
1032 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1033 {
1034         ViewFile *vf = data;
1035
1036         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1037                 {
1038                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1039                 }
1040
1041         while (vflist_thumb_next(vf));
1042 }
1043
1044 static gint vflist_thumb_next(ViewFile *vf)
1045 {
1046         GtkTreePath *tpath;
1047         FileData *fd = NULL;
1048
1049         /* first check the visible files */
1050
1051         if (GTK_WIDGET_REALIZED(vf->listview) &&
1052             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1053                 {
1054                 GtkTreeModel *store;
1055                 GtkTreeIter iter;
1056                 gint valid = TRUE;
1057
1058                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1059                 gtk_tree_model_get_iter(store, &iter, tpath);
1060                 gtk_tree_path_free(tpath);
1061
1062                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1063                         {
1064                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1065                         if (fd->pixbuf) fd = NULL;
1066
1067                         valid = gtk_tree_model_iter_next(store, &iter);
1068                         }
1069                 }
1070
1071         /* then find first undone */
1072
1073         if (!fd)
1074                 {
1075                 GList *work = vf->list;
1076                 while (work && !fd)
1077                         {
1078                         FileData *fd_p = work->data;
1079                         if (!fd_p->pixbuf)
1080                                 fd = fd_p;
1081                         else
1082                                 {
1083                                 GList *work2 = fd_p->sidecar_files;
1084
1085                                 while (work2 && !fd)
1086                                         {
1087                                         fd_p = work2->data;
1088                                         if (!fd_p->pixbuf) fd = fd_p;
1089                                         work2 = work2->next;
1090                                         }
1091                                 }
1092                         work = work->next;
1093                         }
1094                 }
1095
1096         if (!fd)
1097                 {
1098                 /* done */
1099                 vflist_thumb_cleanup(vf);
1100                 return FALSE;
1101                 }
1102
1103         vf->thumbs_count++;
1104
1105         vf->thumbs_filedata = fd;
1106
1107         thumb_loader_free(vf->thumbs_loader);
1108
1109         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1110         thumb_loader_set_callbacks(vf->thumbs_loader,
1111                                    vflist_thumb_done_cb,
1112                                    vflist_thumb_error_cb,
1113                                    NULL,
1114                                    vf);
1115
1116         if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1117                 {
1118                 /* set icon to unknown, continue */
1119                 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1120                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1121
1122                 return TRUE;
1123                 }
1124
1125         return FALSE;
1126 }
1127
1128 static void vflist_thumb_update(ViewFile *vf)
1129 {
1130         vflist_thumb_stop(vf);
1131         if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1132
1133         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1134         vf->thumbs_running = TRUE;
1135
1136         while (vflist_thumb_next(vf));
1137 }
1138
1139 /*
1140  *-----------------------------------------------------------------------------
1141  * row stuff
1142  *-----------------------------------------------------------------------------
1143  */
1144
1145 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1146 {
1147         return g_list_nth_data(vf->list, row);
1148 }
1149
1150 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1151 {
1152         gint p = 0;
1153         GList *work;
1154
1155         if (!path) return -1;
1156
1157         work = vf->list;
1158         while (work)
1159                 {
1160                 FileData *fd = work->data;
1161                 if (strcmp(path, fd->path) == 0) return p;
1162                 
1163                 work = work->next;
1164                 p++;
1165                 }
1166
1167         return -1;
1168 }
1169
1170 guint vflist_count(ViewFile *vf, gint64 *bytes)
1171 {
1172         if (bytes)
1173                 {
1174                 gint64 b = 0;
1175                 GList *work;
1176
1177                 work = vf->list;
1178                 while (work)
1179                         {
1180                         FileData *fd = work->data;
1181                         work = work->next;
1182                         b += fd->size;
1183                         }
1184
1185                 *bytes = b;
1186                 }
1187
1188         return g_list_length(vf->list);
1189 }
1190
1191 GList *vflist_get_list(ViewFile *vf)
1192 {
1193         GList *list = NULL;
1194         GList *work;
1195
1196         work = vf->list;
1197         while (work)
1198                 {
1199                 FileData *fd = work->data;
1200                 work = work->next;
1201
1202                 list = g_list_prepend(list, file_data_ref(fd));
1203                 }
1204
1205         return g_list_reverse(list);
1206 }
1207
1208 /*
1209  *-----------------------------------------------------------------------------
1210  * selections
1211  *-----------------------------------------------------------------------------
1212  */
1213
1214 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1215 {
1216         GtkTreeModel *store;
1217         GtkTreeSelection *selection;
1218         GList *slist;
1219         GList *work;
1220         gint found = FALSE;
1221
1222         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1223         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1224         work = slist;
1225         while (!found && work)
1226                 {
1227                 GtkTreePath *tpath = work->data;
1228                 FileData *fd_n;
1229                 GtkTreeIter iter;
1230
1231                 gtk_tree_model_get_iter(store, &iter, tpath);
1232                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1233                 if (fd_n == fd) found = TRUE;
1234                 work = work->next;
1235                 }
1236         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1237         g_list_free(slist);
1238
1239         return found;
1240 }
1241
1242 gint vflist_index_is_selected(ViewFile *vf, gint row)
1243 {
1244         FileData *fd;
1245
1246         fd = vf_index_get_data(vf, row);
1247         return vflist_row_is_selected(vf, fd);
1248 }
1249
1250 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1251 {
1252         GtkTreeModel *store;
1253         GtkTreeSelection *selection;
1254         GList *slist;
1255         guint count;
1256
1257         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1258         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1259
1260         if (bytes)
1261                 {
1262                 gint64 b = 0;
1263                 GList *work;
1264
1265                 work = slist;
1266                 while (work)
1267                         {
1268                         GtkTreePath *tpath = work->data;
1269                         GtkTreeIter iter;
1270                         FileData *fd;
1271
1272                         gtk_tree_model_get_iter(store, &iter, tpath);
1273                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1274                         b += fd->size;
1275
1276                         work = work->next;
1277                         }
1278
1279                 *bytes = b;
1280                 }
1281
1282         count = g_list_length(slist);
1283         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1284         g_list_free(slist);
1285
1286         return count;
1287 }
1288
1289 GList *vflist_selection_get_list(ViewFile *vf)
1290 {
1291         GtkTreeModel *store;
1292         GtkTreeSelection *selection;
1293         GList *slist;
1294         GList *list = NULL;
1295         GList *work;
1296
1297         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1298         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1299         work = slist;
1300         while (work)
1301                 {
1302                 GtkTreePath *tpath = work->data;
1303                 FileData *fd;
1304                 GtkTreeIter iter;
1305
1306                 gtk_tree_model_get_iter(store, &iter, tpath);
1307                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1308
1309                 list = g_list_prepend(list, file_data_ref(fd));
1310
1311                 work = work->next;
1312                 }
1313         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1314         g_list_free(slist);
1315
1316         return g_list_reverse(list);
1317 }
1318
1319 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1320 {
1321         GtkTreeModel *store;
1322         GtkTreeSelection *selection;
1323         GList *slist;
1324         GList *list = NULL;
1325         GList *work;
1326
1327         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1328         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1329         work = slist;
1330         while (work)
1331                 {
1332                 GtkTreePath *tpath = work->data;
1333                 FileData *fd;
1334                 GtkTreeIter iter;
1335
1336                 gtk_tree_model_get_iter(store, &iter, tpath);
1337                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1338
1339                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1340
1341                 work = work->next;
1342                 }
1343         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1344         g_list_free(slist);
1345
1346         return g_list_reverse(list);
1347 }
1348
1349 void vflist_select_all(ViewFile *vf)
1350 {
1351         GtkTreeSelection *selection;
1352
1353         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1354         gtk_tree_selection_select_all(selection);
1355
1356         VFLIST_INFO(vf, select_fd) = NULL;
1357 }
1358
1359 void vflist_select_none(ViewFile *vf)
1360 {
1361         GtkTreeSelection *selection;
1362
1363         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1364         gtk_tree_selection_unselect_all(selection);
1365 }
1366
1367 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1368 {
1369         GtkTreePath *tpath;
1370         gboolean result;
1371
1372         tpath = gtk_tree_model_get_path(store, iter);
1373         result = gtk_tree_path_prev(tpath);
1374         if (result)
1375                 gtk_tree_model_get_iter(store, iter, tpath);
1376
1377         gtk_tree_path_free(tpath);
1378
1379         return result;
1380 }
1381
1382 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1383 {
1384         if (!gtk_tree_model_get_iter_first(store, iter))
1385                 return FALSE;
1386
1387         while (TRUE)
1388                 {
1389                 GtkTreeIter next = *iter;
1390                 
1391                 if (gtk_tree_model_iter_next(store, &next))
1392                         *iter = next;
1393                 else
1394                         break;
1395                 }
1396         
1397         return TRUE;
1398 }
1399
1400 void vflist_select_invert(ViewFile *vf)
1401 {
1402         GtkTreeIter iter;
1403         GtkTreeSelection *selection;
1404         GtkTreeModel *store;
1405         gboolean valid;
1406
1407         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1408         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1409
1410         /* Backward iteration prevents scrolling to the end of the list,
1411          * it scrolls to the first selected row instead. */
1412         valid = tree_model_get_iter_last(store, &iter);
1413
1414         while (valid)
1415                 {
1416                 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1417
1418                 if (selected)
1419                         gtk_tree_selection_unselect_iter(selection, &iter);
1420                 else
1421                         gtk_tree_selection_select_iter(selection, &iter);
1422                                 
1423                 valid = tree_model_iter_prev(store, &iter);
1424                 }
1425 }
1426
1427 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1428 {
1429         GtkTreeIter iter;
1430
1431         if (vflist_find_row(vf, fd, &iter) < 0) return;
1432
1433         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1434
1435         if (!vflist_row_is_selected(vf, fd))
1436                 {
1437                 GtkTreeSelection *selection;
1438                 GtkTreeModel *store;
1439                 GtkTreePath *tpath;
1440
1441                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1442                 gtk_tree_selection_unselect_all(selection);
1443                 gtk_tree_selection_select_iter(selection, &iter);
1444                 vflist_move_cursor(vf, &iter);
1445
1446                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1447                 tpath = gtk_tree_model_get_path(store, &iter);
1448                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1449                 gtk_tree_path_free(tpath);
1450                 }
1451 }
1452
1453 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1454 {
1455         GList *work;
1456         
1457         if (sel_fd->parent) sel_fd = sel_fd->parent;
1458         work = vf->list;
1459         
1460         while (work)
1461                 {
1462                 gint match;
1463                 FileData *fd = work->data;
1464                 work = work->next;
1465                 
1466
1467                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1468                 
1469                 if (match >= 0)
1470                         {
1471                         vflist_select_by_fd(vf, fd);
1472                         break;
1473                         }
1474                 }
1475
1476 }
1477
1478 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1479 {
1480         GtkTreeModel *store;
1481         GtkTreeIter iter;
1482         GtkTreeSelection *selection;
1483         gint valid;
1484         gint n = mark - 1;
1485
1486         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1487
1488         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1489         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1490
1491         valid = gtk_tree_model_get_iter_first(store, &iter);
1492         while (valid)
1493                 {
1494                 FileData *fd;
1495                 gboolean mark_val, selected;
1496                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1497
1498                 mark_val = file_data_get_mark(fd, n);
1499                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1500
1501                 switch (mode)
1502                         {
1503                         case MTS_MODE_SET: selected = mark_val;
1504                                 break;
1505                         case MTS_MODE_OR: selected = mark_val | selected;
1506                                 break;
1507                         case MTS_MODE_AND: selected = mark_val & selected;
1508                                 break;
1509                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1510                                 break;
1511                         }
1512
1513                 if (selected)
1514                         gtk_tree_selection_select_iter(selection, &iter);
1515                 else
1516                         gtk_tree_selection_unselect_iter(selection, &iter);
1517
1518                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1519                 }
1520 }
1521
1522 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1523 {
1524         GtkTreeModel *store;
1525         GtkTreeSelection *selection;
1526         GList *slist;
1527         GList *work;
1528         gint n = mark - 1;
1529
1530         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1531
1532         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1533         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1534         work = slist;
1535         while (work)
1536                 {
1537                 GtkTreePath *tpath = work->data;
1538                 FileData *fd;
1539                 GtkTreeIter iter;
1540
1541                 gtk_tree_model_get_iter(store, &iter, tpath);
1542                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1543
1544                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1545
1546                 switch (mode)
1547                         {
1548                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1549                                 break;
1550                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1551                                 break;
1552                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1553                                 break;
1554                         }
1555                 
1556                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1557
1558                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1559
1560                 work = work->next;
1561                 }
1562         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1563         g_list_free(slist);
1564 }
1565
1566 /*
1567  *-----------------------------------------------------------------------------
1568  * core (population)
1569  *-----------------------------------------------------------------------------
1570  */
1571
1572 static void vflist_listview_set_columns(GtkWidget *listview, gint thumb)
1573 {
1574         GtkTreeViewColumn *column;
1575         GtkCellRenderer *cell;
1576         GList *list;
1577         gboolean multiline;
1578
1579         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1580         if (!column) return;
1581
1582         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1583
1584         list = gtk_tree_view_column_get_cell_renderers(column);
1585         if (!list) return;
1586         cell = list->data;
1587         g_list_free(list);
1588
1589         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1590         gtk_tree_view_column_set_visible(column, thumb);
1591
1592         multiline = (thumb && options->thumbnails.max_height >= 48);
1593         
1594         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1595         if (!column) return;
1596         gtk_tree_view_column_set_visible(column, multiline);
1597         if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1598
1599         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1600         if (!column) return;
1601         gtk_tree_view_column_set_visible(column, !multiline);
1602         if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1603
1604         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1605         if (!column) return;
1606         gtk_tree_view_column_set_visible(column, !multiline);
1607
1608         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1609         if (!column) return;
1610         gtk_tree_view_column_set_visible(column, !multiline);
1611
1612         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1613 }
1614
1615 static void vflist_populate_view(ViewFile *vf)
1616 {
1617         GtkTreeStore *store;
1618         gint thumbs;
1619         GtkTreeRowReference *visible_row = NULL;
1620         GtkTreePath *tpath;
1621         GList *selected;
1622
1623         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1624         thumbs = VFLIST_INFO(vf, thumbs_enabled);
1625
1626         vflist_thumb_stop(vf);
1627
1628         if (!vf->list)
1629                 {
1630                 vflist_store_clear(vf);
1631                 vf_send_update(vf);
1632                 return;
1633                 }
1634
1635         if (GTK_WIDGET_REALIZED(vf->listview) &&
1636             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1637                 {
1638                 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1639                 gtk_tree_path_free(tpath);
1640                 }
1641
1642         vflist_listview_set_columns(vf->listview, thumbs);
1643
1644         selected = vflist_selection_get_list(vf);
1645         
1646         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1647
1648         if (selected && vflist_selection_count(vf, NULL) == 0)
1649                 {
1650                 /* all selected files disappeared */
1651                 vflist_select_closest(vf, selected->data);
1652                 }       
1653
1654         filelist_free(selected);
1655         
1656         if (visible_row)
1657                 {
1658                 if (gtk_tree_row_reference_valid(visible_row))
1659                         {
1660                         tpath = gtk_tree_row_reference_get_path(visible_row);
1661                         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1662                         gtk_tree_path_free(tpath);
1663                         }
1664                 gtk_tree_row_reference_free(visible_row);
1665                 }
1666
1667         vf_send_update(vf);
1668         vflist_thumb_update(vf);
1669 }
1670
1671 gint vflist_refresh(ViewFile *vf)
1672 {
1673         GList *old_list;
1674         gint ret = TRUE;
1675
1676         old_list = vf->list;
1677         vf->list = NULL;
1678
1679         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1680         if (vf->dir_fd)
1681                 {
1682                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1683
1684                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1685
1686                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1687
1688                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1689                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1690                 }
1691
1692         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1693
1694         vflist_populate_view(vf);
1695
1696         filelist_free(old_list);
1697         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1698
1699         return ret;
1700 }
1701
1702
1703
1704 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1705
1706 #define CELL_HEIGHT_OVERRIDE 512
1707
1708 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1709 {
1710         GParamSpec *spec;
1711
1712         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1713         if (spec && G_IS_PARAM_SPEC_INT(spec))
1714                 {
1715                 GParamSpecInt *spec_int;
1716
1717                 spec_int = G_PARAM_SPEC_INT(spec);
1718                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1719                 }
1720 }
1721
1722 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1723 {
1724         static GdkColor color;
1725         static GtkWidget *done = NULL;
1726
1727         if (done != widget)
1728                 {
1729                 GtkStyle *style;
1730
1731                 style = gtk_widget_get_style(widget);
1732                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1733                 shift_color(&color, -1, 0);
1734                 done = widget;
1735                 }
1736
1737         return &color;
1738 }
1739
1740 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1741                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1742 {
1743         ViewFile *vf = data;
1744         gboolean set;
1745
1746         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1747         g_object_set(G_OBJECT(cell),
1748                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1749                      "cell-background-set", set, NULL);
1750 }
1751
1752 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1753 {
1754         GtkTreeViewColumn *column;
1755         GtkCellRenderer *renderer;
1756
1757         column = gtk_tree_view_column_new();
1758         gtk_tree_view_column_set_title(column, title);
1759         gtk_tree_view_column_set_min_width(column, 4);
1760
1761         if (!image)
1762                 {
1763                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1764                 renderer = gtk_cell_renderer_text_new();
1765                 if (right_justify)
1766                         {
1767                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1768                         }
1769                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1770                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1771                 if (expand)
1772                         gtk_tree_view_column_set_expand(column, TRUE);
1773                 }
1774         else
1775                 {
1776                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1777                 renderer = gtk_cell_renderer_pixbuf_new();
1778                 cell_renderer_height_override(renderer);
1779                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1780                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1781                 }
1782
1783         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1784         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1785         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1786
1787         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1788 }
1789
1790 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1791 {
1792         ViewFile *vf = data;
1793         GtkTreeStore *store;
1794         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1795         GtkTreeIter iter;
1796         FileData *fd;
1797         gboolean mark;
1798         guint col_idx;
1799
1800         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1801         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1802                 return;
1803
1804         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1805
1806         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1807
1808         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1809         mark = !mark;
1810         file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1811         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1812         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1813
1814         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1815         gtk_tree_path_free(path);
1816 }
1817
1818 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1819 {
1820         GtkTreeViewColumn *column;
1821         GtkCellRenderer *renderer;
1822         GtkTreeStore *store;
1823         gint index;
1824
1825         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1826
1827         renderer = gtk_cell_renderer_toggle_new();
1828         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1829
1830         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1831         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1832         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1833
1834         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1835         gtk_tree_view_column_set_fixed_width(column, 18);
1836         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1837
1838
1839         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1840 }
1841
1842 /*
1843  *-----------------------------------------------------------------------------
1844  * base
1845  *-----------------------------------------------------------------------------
1846  */
1847
1848 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1849 {
1850         if (!dir_fd) return FALSE;
1851         if (vf->dir_fd == dir_fd) return TRUE;
1852
1853         file_data_unref(vf->dir_fd);
1854         vf->dir_fd = file_data_ref(dir_fd);
1855
1856         /* force complete reload */
1857         vflist_store_clear(vf);
1858
1859         filelist_free(vf->list);
1860         vf->list = NULL;
1861
1862         return vf_refresh(vf);
1863 }
1864
1865 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1866 {
1867         ViewFile *vf = data;
1868
1869         file_data_unregister_notify_func(vf_notify_cb, vf);
1870
1871         vflist_select_idle_cancel(vf);
1872         vf_refresh_idle_cancel(vf);
1873         vflist_thumb_stop(vf);
1874
1875         filelist_free(vf->list);
1876 }
1877
1878 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1879 {
1880         GtkTreeStore *store;
1881         GtkTreeSelection *selection;
1882
1883         GType flist_types[FILE_COLUMN_COUNT];
1884         gint i;
1885         gint column;
1886
1887         vf->info = g_new0(ViewFileInfoList, 1);
1888         
1889         VFLIST_INFO(vf, click_fd) = NULL;
1890         VFLIST_INFO(vf, select_fd) = NULL;
1891         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1892
1893         VFLIST_INFO(vf, select_idle_id) = -1;
1894
1895         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1896         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1897         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1898         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1899         flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1900         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1901         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1902         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1903         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1904                 flist_types[i] = G_TYPE_BOOLEAN;
1905
1906         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1907
1908         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1909         g_object_unref(store);
1910
1911         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1912         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1913         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1914
1915         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1916         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1917
1918         column = 0;
1919
1920         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1921                 {
1922                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1923                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1924                 column++;
1925                 }
1926
1927         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1928         g_assert(column == FILE_VIEW_COLUMN_THUMB);
1929         column++;
1930         
1931         vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1932         g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1933         column++;
1934
1935         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1936         g_assert(column == FILE_VIEW_COLUMN_NAME);
1937         column++;
1938
1939         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1940         g_assert(column == FILE_VIEW_COLUMN_SIZE);
1941         column++;
1942
1943         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1944         g_assert(column == FILE_VIEW_COLUMN_DATE);
1945         column++;
1946
1947         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1948         return vf;
1949 }
1950
1951 void vflist_thumb_set(ViewFile *vf, gint enable)
1952 {
1953         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1954
1955         VFLIST_INFO(vf, thumbs_enabled) = enable;
1956         if (vf->layout) vf_refresh(vf);
1957 }
1958
1959 void vflist_marks_set(ViewFile *vf, gint enable)
1960 {
1961         GList *columns, *work;
1962
1963         if (vf->marks_enabled == enable) return;
1964
1965         vf->marks_enabled = enable;
1966
1967         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1968
1969         work = columns;
1970         while (work)
1971                 {
1972                 GtkTreeViewColumn *column = work->data;
1973                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1974                 work = work->next;
1975
1976                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1977                         gtk_tree_view_column_set_visible(column, enable);
1978                 }
1979
1980         g_list_free(columns);
1981         //vf_refresh(vf);
1982 }
1983