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