Comment out unused functions.
[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_fileops.h"
27 #include "ui_menu.h"
28 #include "ui_tree_edit.h"
29 #include "uri_utils.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 #if 0
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 static gint vflist_sidecar_list_count(GList *work)
139 {
140         gint i = 0;
141         while (work)
142                 {
143                 FileData *fd = work->data;
144                 i++;
145
146                 GList *work2 = fd->sidecar_files;
147                 while (work2)
148                         {
149                         i++;
150                         work2 = work2->next;
151                         }
152                 work = work->next;
153                 }
154         return i;
155 }
156 #endif
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(vf)->click_fd) return;
234
235         if (vflist_row_is_selected(vf, VFLIST(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(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(vf)->click_fd, TRUE);
261
262         if (VFLIST(vf)->thumbs_enabled &&
263             VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
264                 {
265                 guint items;
266
267                 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
268                         items = vf_selection_count(vf, NULL);
269                 else
270                         items = 1;
271
272                 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_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(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(vf)->click_fd) return NULL;
310
311         if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
312                 {
313                 return vf_selection_get_list(vf);
314                 }
315
316         return g_list_append(NULL, file_data_ref(VFLIST(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(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(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(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(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(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(vf)->click_fd, FALSE);
373         if (vf->layout)
374                 {
375                 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
376                 }
377         else
378                 {
379                 vflist_thumb_set(vf, !VFLIST(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(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(vf)->click_fd, FALSE);
395         VFLIST(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
424                 {
425                 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
426                 file_util_rename_simple(fd, new_path, vf->listview);
427                 file_data_unref(fd);
428                 }
429         g_free(old_path);
430         g_free(new_path);
431
432         return FALSE;
433 }
434
435 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
436 {
437         ViewFile *vf = data;
438         GtkTreeModel *store;
439         GtkTreeIter iter;
440         GtkTreePath *tpath;
441         gint cw, ch;
442
443         if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
444         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
445         tpath = gtk_tree_model_get_path(store, &iter);
446         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
447         gtk_tree_path_free(tpath);
448         *y += ch;
449         popup_menu_position_clamp(menu, x, y, 0);
450 }
451
452 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
453 {
454         ViewFile *vf = data;
455         GtkTreePath *tpath;
456
457         if (event->keyval != GDK_Menu) return FALSE;
458
459         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
460         if (tpath)
461                 {
462                 GtkTreeModel *store;
463                 GtkTreeIter iter;
464
465                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
466                 gtk_tree_model_get_iter(store, &iter, tpath);
467                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
468                 gtk_tree_path_free(tpath);
469                 }
470         else
471                 {
472                 VFLIST(vf)->click_fd = NULL;
473                 }
474
475         vf->popup = vf_pop_menu(vf);
476         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
477
478         return TRUE;
479 }
480
481 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
482 {
483         ViewFile *vf = data;
484         GtkTreePath *tpath;
485         GtkTreeIter iter;
486         FileData *fd = NULL;
487         GtkTreeViewColumn *column;
488         
489         vf->clicked_mark = 0;
490
491         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
492                                           &tpath, &column, NULL, NULL))
493                 {
494                 GtkTreeModel *store;
495                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
496
497                 if (bevent->button == MOUSE_BUTTON_LEFT &&
498                     col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
499                         return FALSE;
500
501                 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
502                         vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
503
504                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
505
506                 gtk_tree_model_get_iter(store, &iter, tpath);
507                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
508 #if 0
509                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
510 #endif
511                 gtk_tree_path_free(tpath);
512                 }
513
514         VFLIST(vf)->click_fd = fd;
515
516         if (bevent->button == MOUSE_BUTTON_RIGHT)
517                 {
518                 vf->popup = vf_pop_menu(vf);
519                 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
520                                 bevent->button, bevent->time);
521                 return TRUE;
522                 }
523
524         if (!fd) return FALSE;
525
526         if (bevent->button == MOUSE_BUTTON_MIDDLE)
527                 {
528                 if (!vflist_row_is_selected(vf, fd))
529                         {
530                         vflist_color_set(vf, fd, TRUE);
531                         }
532                 return TRUE;
533                 }
534
535
536         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
537             !(bevent->state & GDK_SHIFT_MASK ) &&
538             !(bevent->state & GDK_CONTROL_MASK ) &&
539             vflist_row_is_selected(vf, fd))
540                 {
541                 GtkTreeSelection *selection;
542
543                 gtk_widget_grab_focus(widget);
544
545
546                 /* returning FALSE and further processing of the event is needed for
547                    correct operation of the expander, to show the sidecar files.
548                    It however resets the selection of multiple files. With this condition
549                    it should work for both cases */
550                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
551                 return (gtk_tree_selection_count_selected_rows(selection) > 1);
552                 }
553
554 #if 0
555         if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
556                 {
557                 if (vf->layout) layout_image_full_screen_start(vf->layout);
558                 }
559 #endif
560
561         return FALSE;
562 }
563
564 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
565 {
566         ViewFile *vf = data;
567         GtkTreePath *tpath;
568         GtkTreeIter iter;
569         FileData *fd = NULL;
570
571         if (bevent->button == MOUSE_BUTTON_MIDDLE)
572                 {
573                 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
574                 }
575
576         if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
577                 {
578                 return TRUE;
579                 }
580
581         if ((bevent->x != 0 || bevent->y != 0) &&
582             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
583                                           &tpath, NULL, NULL, NULL))
584                 {
585                 GtkTreeModel *store;
586
587                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
588                 gtk_tree_model_get_iter(store, &iter, tpath);
589                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
590                 gtk_tree_path_free(tpath);
591                 }
592
593         if (bevent->button == MOUSE_BUTTON_MIDDLE)
594                 {
595                 if (fd && VFLIST(vf)->click_fd == fd)
596                         {
597                         GtkTreeSelection *selection;
598
599                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
600                         if (vflist_row_is_selected(vf, fd))
601                                 {
602                                 gtk_tree_selection_unselect_iter(selection, &iter);
603                                 }
604                         else
605                                 {
606                                 gtk_tree_selection_select_iter(selection, &iter);
607                                 }
608                         }
609                 return TRUE;
610                 }
611
612         if (fd && VFLIST(vf)->click_fd == fd &&
613             !(bevent->state & GDK_SHIFT_MASK ) &&
614             !(bevent->state & GDK_CONTROL_MASK ) &&
615             vflist_row_is_selected(vf, fd))
616                 {
617                 GtkTreeSelection *selection;
618
619                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
620                 gtk_tree_selection_unselect_all(selection);
621                 gtk_tree_selection_select_iter(selection, &iter);
622                 vflist_move_cursor(vf, &iter);
623 //              return TRUE;// FIXME - expand
624                 }
625
626         return FALSE;
627 }
628
629 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
630 {
631         FileData *read_ahead_fd = NULL;
632         gint row;
633         FileData *cur_fd;
634         if (!sel_fd) return;
635
636         cur_fd = layout_image_get_fd(vf->layout);
637         if (sel_fd == cur_fd) return; /* no change */
638
639         row = g_list_index(vf->list, sel_fd);
640         // FIXME sidecar data
641
642         if (sel_fd && options->image.enable_read_ahead && row >= 0)
643                 {
644                 if (row > g_list_index(vf->list, cur_fd) &&
645                     (guint) (row + 1) < vf_count(vf, NULL))
646                         {
647                         read_ahead_fd = vf_index_get_data(vf, row + 1);
648                         }
649                 else if (row > 0)
650                         {
651                         read_ahead_fd = vf_index_get_data(vf, row - 1);
652                         }
653                 }
654
655         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
656 }
657
658 static gint vflist_select_idle_cb(gpointer data)
659 {
660         ViewFile *vf = data;
661
662         if (!vf->layout)
663                 {
664                 VFLIST(vf)->select_idle_id = -1;
665                 return FALSE;
666                 }
667
668         vf_send_update(vf);
669
670         if (VFLIST(vf)->select_fd)
671                 {
672                 vflist_select_image(vf, VFLIST(vf)->select_fd);
673                 VFLIST(vf)->select_fd = NULL;
674                 }
675
676         VFLIST(vf)->select_idle_id = -1;
677         return FALSE;
678 }
679
680 static void vflist_select_idle_cancel(ViewFile *vf)
681 {
682         if (VFLIST(vf)->select_idle_id != -1) g_source_remove(VFLIST(vf)->select_idle_id);
683         VFLIST(vf)->select_idle_id = -1;
684 }
685
686 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
687                                  gboolean path_currently_selected, gpointer data)
688 {
689         ViewFile *vf = data;
690         GtkTreeIter iter;
691
692         if (!path_currently_selected &&
693             gtk_tree_model_get_iter(store, &iter, tpath))
694                 {
695                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
696                 }
697         else
698                 {
699                 VFLIST(vf)->select_fd = NULL;
700                 }
701
702         if (vf->layout &&
703             VFLIST(vf)->select_idle_id == -1)
704                 {
705                 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
706                 }
707
708         return TRUE;
709 }
710
711 /*
712  *-----------------------------------------------------------------------------
713  * misc
714  *-----------------------------------------------------------------------------
715  */
716
717 /*
718 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
719                                         gboolean path_currently_selected, gpointer data)
720 {
721         return TRUE;
722 }
723 */
724
725 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
726 {
727         gchar *size;
728         gchar *sidecars = NULL;
729         gchar *name_sidecars;
730         gchar *multiline;
731         const gchar *time = text_from_time(fd->date);
732         name_sidecars = (gchar *)fd->name;
733
734         if (fd->sidecar_files)
735                 {
736                 sidecars = file_data_sc_list_to_string(fd);
737                 name_sidecars = g_strdup_printf("%s %s", fd->name, sidecars);
738                 }
739         size = text_from_size(fd->size);
740         
741         multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
742
743         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
744                                         FILE_COLUMN_VERSION, fd->version,
745                                         FILE_COLUMN_THUMB, fd->thumb_pixbuf,
746                                         FILE_COLUMN_MULTILINE, multiline,
747                                         FILE_COLUMN_NAME, name_sidecars,
748                                         FILE_COLUMN_SIZE, size,
749                                         FILE_COLUMN_DATE, time,
750 #define STORE_SET_IS_SLOW 1
751 #if STORE_SET_IS_SLOW
752 /* this is 3x faster on a directory with 20000 files */
753                                         FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
754                                         FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
755                                         FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
756                                         FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
757                                         FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
758                                         FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
759 #if FILEDATA_MARKS_SIZE != 6
760 #error this needs to be updated
761 #endif
762 #endif
763                                         FILE_COLUMN_COLOR, FALSE, -1);
764
765 #if !STORE_SET_IS_SLOW
766         {
767         gint i;
768         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
769                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
770         }
771 #endif
772         g_free(size);
773         if (sidecars)
774                 {
775                 g_free(sidecars);
776                 g_free(name_sidecars);
777                 }
778         g_free(multiline);
779 }
780
781 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
782 {
783         GList *work;
784         GtkTreeIter iter;
785         gint valid;
786         gint num_ordered = 0;
787         gint num_prepended = 0;
788
789         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
790
791         work = list;
792         while (work)
793                 {
794                 gint match;
795                 FileData *fd = work->data;
796                 gint done = FALSE;
797
798                 while (!done)
799                         {
800                         FileData *old_fd = NULL;
801                         gint old_version = 0;
802
803                         if (valid)
804                                 {
805                                 num_ordered++;
806                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
807                                                    FILE_COLUMN_POINTER, &old_fd,
808                                                    FILE_COLUMN_VERSION, &old_version,
809                                                    -1);
810
811                                 if (fd == old_fd)
812                                         {
813                                         match = 0;
814                                         }
815                                 else
816                                         {
817                                         if (parent_iter)
818                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
819                                         else
820                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
821
822                                         if (match == 0) g_warning("multiple fd for the same path");
823                                         }
824                                         
825                                 }
826                         else
827                                 {
828                                 match = -1;
829                                 }
830
831                         if (match < 0)
832                                 {
833                                 GtkTreeIter new;
834
835                                 if (valid)
836                                         {
837                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
838                                         }
839                                 else
840                                         {
841                                         /*
842                                             here should be used gtk_tree_store_append, but this function seems to be O(n)
843                                             and it seems to be much faster to add new entries to the beginning and reorder later
844                                         */
845                                         num_prepended++;
846                                         gtk_tree_store_prepend(store, &new, parent_iter);
847                                         }
848
849                                 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
850                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
851                                 
852                                 if (g_list_find(selected, fd))
853                                         {
854                                         /* renamed files - the same fd appears at different position - select it again*/
855                                         GtkTreeSelection *selection;
856                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
857                                         gtk_tree_selection_select_iter(selection, &new);
858                                         }
859
860                                 done = TRUE;
861                                 }
862                         else if (match > 0)
863                                 {
864                                 file_data_unref(old_fd);
865                                 valid = gtk_tree_store_remove(store, &iter);
866                                 }
867                         else
868                                 {
869                                 if (fd->version != old_version)
870                                         {
871                                         vflist_setup_iter(vf, store, &iter, fd);
872                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
873                                         }
874
875                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
876
877                                 done = TRUE;
878                                 }
879                         }
880                 work = work->next;
881                 }
882
883         while (valid)
884                 {
885                 FileData *old_fd;
886                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
887                 file_data_unref(old_fd);
888
889                 valid = gtk_tree_store_remove(store, &iter);
890                 }
891                 
892         /* move the prepended entries to the correct position */
893         if (num_prepended)
894                 {
895                 gint i;
896                 gint num_total = num_prepended + num_ordered;
897                 gint *new_order = g_malloc(num_total * sizeof(gint));
898                 
899                 for (i = 0; i < num_total; i++)
900                         {
901                         if (i < num_ordered)
902                                 new_order[i] = num_prepended + i;
903                         else
904                                 new_order[i] = num_total - 1 - i;
905                         }
906                 gtk_tree_store_reorder(store, parent_iter, new_order);
907
908                 g_free(new_order);
909                 }
910 }
911
912 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
913 {
914         gint i;
915         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
916         gint *new_order;
917         GtkTreeStore *store;
918         GList *work;
919
920         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
921         if (!vf->list) return;
922
923         work = vf->list;
924         i = 0;
925         while (work)
926                 {
927                 FileData *fd = work->data;
928                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
929                 i++;
930                 work = work->next;
931                 }
932
933         vf->sort_method = type;
934         vf->sort_ascend = ascend;
935
936         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
937
938         new_order = g_malloc(i * sizeof(gint));
939
940         work = vf->list;
941         i = 0;
942         while (work)
943                 {
944                 FileData *fd = work->data;
945                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
946                 i++;
947                 work = work->next;
948                 }
949
950         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
951         gtk_tree_store_reorder(store, NULL, new_order);
952
953         g_free(new_order);
954         g_hash_table_destroy(fd_idx_hash);
955 }
956
957 /*
958  *-----------------------------------------------------------------------------
959  * thumb updates
960  *-----------------------------------------------------------------------------
961  */
962
963 static gint vflist_thumb_next(ViewFile *vf);
964
965 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
966 {
967         GList *work = list;
968         while (work)
969                 {
970                 FileData *fd = work->data;
971                 work = work->next;
972
973                 if (fd->thumb_pixbuf) (*done)++;
974                 
975                 if (fd->sidecar_files)
976                         {
977                         vflist_thumb_progress_count(fd->sidecar_files, count, done);
978                         }
979                 (*count)++;
980                 }
981 }
982
983 static gdouble vflist_thumb_progress(ViewFile *vf)
984 {
985         gint count = 0;
986         gint done = 0;
987         
988         vflist_thumb_progress_count(vf->list, &count, &done);
989
990         DEBUG_1("thumb progress: %d of %d", done, count);
991         return (gdouble)done / count;
992 }
993
994
995 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
996 {
997         if (vf->func_thumb_status)
998                 {
999                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1000                 }
1001 }
1002
1003 static void vflist_thumb_cleanup(ViewFile *vf)
1004 {
1005         vflist_thumb_status(vf, 0.0, NULL);
1006
1007         vf->thumbs_running = FALSE;
1008
1009         thumb_loader_free(vf->thumbs_loader);
1010         vf->thumbs_loader = NULL;
1011
1012         vf->thumbs_filedata = NULL;
1013 }
1014
1015 static void vflist_thumb_stop(ViewFile *vf)
1016 {
1017         if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1018 }
1019
1020 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1021 {
1022         GtkTreeStore *store;
1023         GtkTreeIter iter;
1024
1025         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1026
1027         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1028         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1029
1030         vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1031 }
1032
1033 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1034 {
1035         ViewFile *vf = data;
1036
1037         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1038                 {
1039                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1040                 }
1041
1042         while (vflist_thumb_next(vf));
1043 }
1044
1045 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1046 {
1047         ViewFile *vf = data;
1048
1049         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1050                 {
1051                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1052                 }
1053
1054         while (vflist_thumb_next(vf));
1055 }
1056
1057 static gint vflist_thumb_next(ViewFile *vf)
1058 {
1059         GtkTreePath *tpath;
1060         FileData *fd = NULL;
1061
1062         /* first check the visible files */
1063
1064         if (GTK_WIDGET_REALIZED(vf->listview) &&
1065             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1066                 {
1067                 GtkTreeModel *store;
1068                 GtkTreeIter iter;
1069                 gint valid = TRUE;
1070
1071                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1072                 gtk_tree_model_get_iter(store, &iter, tpath);
1073                 gtk_tree_path_free(tpath);
1074
1075                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1076                         {
1077                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1078                         if (fd->thumb_pixbuf) fd = NULL;
1079
1080                         valid = gtk_tree_model_iter_next(store, &iter);
1081                         }
1082                 }
1083
1084         /* then find first undone */
1085
1086         if (!fd)
1087                 {
1088                 GList *work = vf->list;
1089                 while (work && !fd)
1090                         {
1091                         FileData *fd_p = work->data;
1092                         if (!fd_p->thumb_pixbuf)
1093                                 fd = fd_p;
1094                         else
1095                                 {
1096                                 GList *work2 = fd_p->sidecar_files;
1097
1098                                 while (work2 && !fd)
1099                                         {
1100                                         fd_p = work2->data;
1101                                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1102                                         work2 = work2->next;
1103                                         }
1104                                 }
1105                         work = work->next;
1106                         }
1107                 }
1108
1109         if (!fd)
1110                 {
1111                 /* done */
1112                 vflist_thumb_cleanup(vf);
1113                 return FALSE;
1114                 }
1115
1116         vf->thumbs_filedata = fd;
1117
1118         thumb_loader_free(vf->thumbs_loader);
1119
1120         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1121         thumb_loader_set_callbacks(vf->thumbs_loader,
1122                                    vflist_thumb_done_cb,
1123                                    vflist_thumb_error_cb,
1124                                    NULL,
1125                                    vf);
1126
1127         if (!thumb_loader_start(vf->thumbs_loader, fd))
1128                 {
1129                 /* set icon to unknown, continue */
1130                 DEBUG_1("thumb loader start failed %s", fd->path);
1131                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1132
1133                 return TRUE;
1134                 }
1135
1136         return FALSE;
1137 }
1138
1139 static void vflist_thumb_update(ViewFile *vf)
1140 {
1141         vflist_thumb_stop(vf);
1142         if (!VFLIST(vf)->thumbs_enabled) return;
1143
1144         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1145         vf->thumbs_running = TRUE;
1146
1147         while (vflist_thumb_next(vf));
1148 }
1149
1150 /*
1151  *-----------------------------------------------------------------------------
1152  * row stuff
1153  *-----------------------------------------------------------------------------
1154  */
1155
1156 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1157 {
1158         return g_list_nth_data(vf->list, row);
1159 }
1160
1161 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1162 {
1163         gint p = 0;
1164         GList *work;
1165
1166         if (!path) return -1;
1167
1168         work = vf->list;
1169         while (work)
1170                 {
1171                 FileData *fd = work->data;
1172                 if (strcmp(path, fd->path) == 0) return p;
1173                 
1174                 work = work->next;
1175                 p++;
1176                 }
1177
1178         return -1;
1179 }
1180
1181 guint vflist_count(ViewFile *vf, gint64 *bytes)
1182 {
1183         if (bytes)
1184                 {
1185                 gint64 b = 0;
1186                 GList *work;
1187
1188                 work = vf->list;
1189                 while (work)
1190                         {
1191                         FileData *fd = work->data;
1192                         work = work->next;
1193                         b += fd->size;
1194                         }
1195
1196                 *bytes = b;
1197                 }
1198
1199         return g_list_length(vf->list);
1200 }
1201
1202 GList *vflist_get_list(ViewFile *vf)
1203 {
1204         GList *list = NULL;
1205         GList *work;
1206
1207         work = vf->list;
1208         while (work)
1209                 {
1210                 FileData *fd = work->data;
1211                 work = work->next;
1212
1213                 list = g_list_prepend(list, file_data_ref(fd));
1214                 }
1215
1216         return g_list_reverse(list);
1217 }
1218
1219 /*
1220  *-----------------------------------------------------------------------------
1221  * selections
1222  *-----------------------------------------------------------------------------
1223  */
1224
1225 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1226 {
1227         GtkTreeModel *store;
1228         GtkTreeSelection *selection;
1229         GList *slist;
1230         GList *work;
1231         gint found = FALSE;
1232
1233         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1234         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1235         work = slist;
1236         while (!found && work)
1237                 {
1238                 GtkTreePath *tpath = work->data;
1239                 FileData *fd_n;
1240                 GtkTreeIter iter;
1241
1242                 gtk_tree_model_get_iter(store, &iter, tpath);
1243                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1244                 if (fd_n == fd) found = TRUE;
1245                 work = work->next;
1246                 }
1247         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1248         g_list_free(slist);
1249
1250         return found;
1251 }
1252
1253 gint vflist_index_is_selected(ViewFile *vf, gint row)
1254 {
1255         FileData *fd;
1256
1257         fd = vf_index_get_data(vf, row);
1258         return vflist_row_is_selected(vf, fd);
1259 }
1260
1261 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1262 {
1263         GtkTreeModel *store;
1264         GtkTreeSelection *selection;
1265         GList *slist;
1266         guint count;
1267
1268         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1269         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1270
1271         if (bytes)
1272                 {
1273                 gint64 b = 0;
1274                 GList *work;
1275
1276                 work = slist;
1277                 while (work)
1278                         {
1279                         GtkTreePath *tpath = work->data;
1280                         GtkTreeIter iter;
1281                         FileData *fd;
1282
1283                         gtk_tree_model_get_iter(store, &iter, tpath);
1284                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1285                         b += fd->size;
1286
1287                         work = work->next;
1288                         }
1289
1290                 *bytes = b;
1291                 }
1292
1293         count = g_list_length(slist);
1294         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1295         g_list_free(slist);
1296
1297         return count;
1298 }
1299
1300 GList *vflist_selection_get_list(ViewFile *vf)
1301 {
1302         GtkTreeModel *store;
1303         GtkTreeSelection *selection;
1304         GList *slist;
1305         GList *list = NULL;
1306         GList *work;
1307
1308         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1309         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1310         work = slist;
1311         while (work)
1312                 {
1313                 GtkTreePath *tpath = work->data;
1314                 FileData *fd;
1315                 GtkTreeIter iter;
1316
1317                 gtk_tree_model_get_iter(store, &iter, tpath);
1318                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1319
1320                 list = g_list_prepend(list, file_data_ref(fd));
1321
1322                 work = work->next;
1323                 }
1324         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1325         g_list_free(slist);
1326
1327         return g_list_reverse(list);
1328 }
1329
1330 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1331 {
1332         GtkTreeModel *store;
1333         GtkTreeSelection *selection;
1334         GList *slist;
1335         GList *list = NULL;
1336         GList *work;
1337
1338         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1339         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1340         work = slist;
1341         while (work)
1342                 {
1343                 GtkTreePath *tpath = work->data;
1344                 FileData *fd;
1345                 GtkTreeIter iter;
1346
1347                 gtk_tree_model_get_iter(store, &iter, tpath);
1348                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1349
1350                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1351
1352                 work = work->next;
1353                 }
1354         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1355         g_list_free(slist);
1356
1357         return g_list_reverse(list);
1358 }
1359
1360 void vflist_select_all(ViewFile *vf)
1361 {
1362         GtkTreeSelection *selection;
1363
1364         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1365         gtk_tree_selection_select_all(selection);
1366
1367         VFLIST(vf)->select_fd = NULL;
1368 }
1369
1370 void vflist_select_none(ViewFile *vf)
1371 {
1372         GtkTreeSelection *selection;
1373
1374         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1375         gtk_tree_selection_unselect_all(selection);
1376 }
1377
1378 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1379 {
1380         GtkTreePath *tpath;
1381         gboolean result;
1382
1383         tpath = gtk_tree_model_get_path(store, iter);
1384         result = gtk_tree_path_prev(tpath);
1385         if (result)
1386                 gtk_tree_model_get_iter(store, iter, tpath);
1387
1388         gtk_tree_path_free(tpath);
1389
1390         return result;
1391 }
1392
1393 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1394 {
1395         if (!gtk_tree_model_get_iter_first(store, iter))
1396                 return FALSE;
1397
1398         while (TRUE)
1399                 {
1400                 GtkTreeIter next = *iter;
1401                 
1402                 if (gtk_tree_model_iter_next(store, &next))
1403                         *iter = next;
1404                 else
1405                         break;
1406                 }
1407         
1408         return TRUE;
1409 }
1410
1411 void vflist_select_invert(ViewFile *vf)
1412 {
1413         GtkTreeIter iter;
1414         GtkTreeSelection *selection;
1415         GtkTreeModel *store;
1416         gboolean valid;
1417
1418         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1419         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1420
1421         /* Backward iteration prevents scrolling to the end of the list,
1422          * it scrolls to the first selected row instead. */
1423         valid = tree_model_get_iter_last(store, &iter);
1424
1425         while (valid)
1426                 {
1427                 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1428
1429                 if (selected)
1430                         gtk_tree_selection_unselect_iter(selection, &iter);
1431                 else
1432                         gtk_tree_selection_select_iter(selection, &iter);
1433                                 
1434                 valid = tree_model_iter_prev(store, &iter);
1435                 }
1436 }
1437
1438 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1439 {
1440         GtkTreeIter iter;
1441
1442         if (vflist_find_row(vf, fd, &iter) < 0) return;
1443
1444         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1445
1446         if (!vflist_row_is_selected(vf, fd))
1447                 {
1448                 GtkTreeSelection *selection;
1449                 GtkTreeModel *store;
1450                 GtkTreePath *tpath;
1451
1452                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1453                 gtk_tree_selection_unselect_all(selection);
1454                 gtk_tree_selection_select_iter(selection, &iter);
1455                 vflist_move_cursor(vf, &iter);
1456
1457                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1458                 tpath = gtk_tree_model_get_path(store, &iter);
1459                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1460                 gtk_tree_path_free(tpath);
1461                 }
1462 }
1463
1464 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1465 {
1466         GList *work;
1467         
1468         if (sel_fd->parent) sel_fd = sel_fd->parent;
1469         work = vf->list;
1470         
1471         while (work)
1472                 {
1473                 gint match;
1474                 FileData *fd = work->data;
1475                 work = work->next;
1476                 
1477
1478                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1479                 
1480                 if (match >= 0)
1481                         {
1482                         vflist_select_by_fd(vf, fd);
1483                         break;
1484                         }
1485                 }
1486
1487 }
1488
1489 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1490 {
1491         GtkTreeModel *store;
1492         GtkTreeIter iter;
1493         GtkTreeSelection *selection;
1494         gint valid;
1495         gint n = mark - 1;
1496
1497         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1498
1499         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1500         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1501
1502         valid = gtk_tree_model_get_iter_first(store, &iter);
1503         while (valid)
1504                 {
1505                 FileData *fd;
1506                 gboolean mark_val, selected;
1507                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1508
1509                 mark_val = file_data_get_mark(fd, n);
1510                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1511
1512                 switch (mode)
1513                         {
1514                         case MTS_MODE_SET: selected = mark_val;
1515                                 break;
1516                         case MTS_MODE_OR: selected = mark_val | selected;
1517                                 break;
1518                         case MTS_MODE_AND: selected = mark_val & selected;
1519                                 break;
1520                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1521                                 break;
1522                         }
1523
1524                 if (selected)
1525                         gtk_tree_selection_select_iter(selection, &iter);
1526                 else
1527                         gtk_tree_selection_unselect_iter(selection, &iter);
1528
1529                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1530                 }
1531 }
1532
1533 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1534 {
1535         GtkTreeModel *store;
1536         GtkTreeSelection *selection;
1537         GList *slist;
1538         GList *work;
1539         gint n = mark - 1;
1540
1541         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1542
1543         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1544         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1545         work = slist;
1546         while (work)
1547                 {
1548                 GtkTreePath *tpath = work->data;
1549                 FileData *fd;
1550                 GtkTreeIter iter;
1551
1552                 gtk_tree_model_get_iter(store, &iter, tpath);
1553                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1554
1555                 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1556
1557                 switch (mode)
1558                         {
1559                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1560                                 break;
1561                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1562                                 break;
1563                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1564                                 break;
1565                         }
1566                 
1567                 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1568                         {
1569                         vf_refresh_idle(vf);
1570                         }
1571
1572                 
1573                 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1574
1575                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1576
1577                 work = work->next;
1578                 }
1579         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1580         g_list_free(slist);
1581 }
1582
1583 /*
1584  *-----------------------------------------------------------------------------
1585  * core (population)
1586  *-----------------------------------------------------------------------------
1587  */
1588
1589 static void vflist_listview_set_columns(GtkWidget *listview, gint thumb)
1590 {
1591         GtkTreeViewColumn *column;
1592         GtkCellRenderer *cell;
1593         GList *list;
1594         gboolean multiline;
1595
1596         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1597         if (!column) return;
1598
1599         gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1600
1601         list = gtk_tree_view_column_get_cell_renderers(column);
1602         if (!list) return;
1603         cell = list->data;
1604         g_list_free(list);
1605
1606         g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1607         gtk_tree_view_column_set_visible(column, thumb);
1608
1609         multiline = (thumb && options->thumbnails.max_height >= 48);
1610         
1611         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1612         if (!column) return;
1613         gtk_tree_view_column_set_visible(column, multiline);
1614         if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1615
1616         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1617         if (!column) return;
1618         gtk_tree_view_column_set_visible(column, !multiline);
1619         if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1620
1621         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1622         if (!column) return;
1623         gtk_tree_view_column_set_visible(column, !multiline);
1624
1625         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1626         if (!column) return;
1627         gtk_tree_view_column_set_visible(column, !multiline);
1628
1629         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1630 }
1631
1632 static void vflist_populate_view(ViewFile *vf)
1633 {
1634         GtkTreeStore *store;
1635         gint thumbs;
1636         GList *selected;
1637
1638         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1639         thumbs = VFLIST(vf)->thumbs_enabled;
1640
1641         vflist_thumb_stop(vf);
1642
1643         if (!vf->list)
1644                 {
1645                 vflist_store_clear(vf);
1646                 vf_send_update(vf);
1647                 return;
1648                 }
1649
1650         vflist_listview_set_columns(vf->listview, thumbs);
1651
1652         selected = vflist_selection_get_list(vf);
1653         
1654         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1655
1656         if (selected && vflist_selection_count(vf, NULL) == 0)
1657                 {
1658                 /* all selected files disappeared */
1659                 vflist_select_closest(vf, selected->data);
1660                 }
1661
1662         filelist_free(selected);
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                 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
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         if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1810                 {
1811                 vf_refresh_idle(vf);
1812                 }
1813         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1814
1815         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1816         gtk_tree_path_free(path);
1817 }
1818
1819 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1820 {
1821         GtkTreeViewColumn *column;
1822         GtkCellRenderer *renderer;
1823         GtkTreeStore *store;
1824         gint index;
1825
1826         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1827
1828         renderer = gtk_cell_renderer_toggle_new();
1829         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1830
1831         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1832         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1833         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1834
1835         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1836         gtk_tree_view_column_set_fixed_width(column, 18);
1837         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1838
1839
1840         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1841 }
1842
1843 /*
1844  *-----------------------------------------------------------------------------
1845  * base
1846  *-----------------------------------------------------------------------------
1847  */
1848
1849 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1850 {
1851         if (!dir_fd) return FALSE;
1852         if (vf->dir_fd == dir_fd) return TRUE;
1853
1854         file_data_unref(vf->dir_fd);
1855         vf->dir_fd = file_data_ref(dir_fd);
1856
1857         /* force complete reload */
1858         vflist_store_clear(vf);
1859
1860         filelist_free(vf->list);
1861         vf->list = NULL;
1862
1863         return vf_refresh(vf);
1864 }
1865
1866 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1867 {
1868         ViewFile *vf = data;
1869
1870         file_data_unregister_notify_func(vf_notify_cb, vf);
1871
1872         vflist_select_idle_cancel(vf);
1873         vf_refresh_idle_cancel(vf);
1874         vflist_thumb_stop(vf);
1875
1876         filelist_free(vf->list);
1877 }
1878
1879 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1880 {
1881         GtkTreeStore *store;
1882         GtkTreeSelection *selection;
1883
1884         GType flist_types[FILE_COLUMN_COUNT];
1885         gint i;
1886         gint column;
1887
1888         vf->info = g_new0(ViewFileInfoList, 1);
1889         
1890         VFLIST(vf)->click_fd = NULL;
1891         VFLIST(vf)->select_fd = NULL;
1892         VFLIST(vf)->thumbs_enabled = FALSE;
1893
1894         VFLIST(vf)->select_idle_id = -1;
1895
1896         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1897         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1898         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1899         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1900         flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1901         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1902         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1903         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1904         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1905                 flist_types[i] = G_TYPE_BOOLEAN;
1906
1907         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1908
1909         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1910         g_object_unref(store);
1911
1912         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1913         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1914         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1915
1916         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1917         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1918
1919         column = 0;
1920
1921         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1922                 {
1923                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1924                 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1925                 column++;
1926                 }
1927
1928         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1929         g_assert(column == FILE_VIEW_COLUMN_THUMB);
1930         column++;
1931         
1932         vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1933         g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1934         column++;
1935
1936         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1937         g_assert(column == FILE_VIEW_COLUMN_NAME);
1938         column++;
1939
1940         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1941         g_assert(column == FILE_VIEW_COLUMN_SIZE);
1942         column++;
1943
1944         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1945         g_assert(column == FILE_VIEW_COLUMN_DATE);
1946         column++;
1947
1948         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1949         return vf;
1950 }
1951
1952 void vflist_thumb_set(ViewFile *vf, gint enable)
1953 {
1954         if (VFLIST(vf)->thumbs_enabled == enable) return;
1955
1956         VFLIST(vf)->thumbs_enabled = enable;
1957         if (vf->layout) vf_refresh(vf);
1958 }
1959
1960 void vflist_marks_set(ViewFile *vf, gint enable)
1961 {
1962         GList *columns, *work;
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