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