replaced directory path with FileData* dir_fd
[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->dir_fd->path, old, NULL);
386         new_path = g_build_filename(vf->dir_fd->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 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
710 {
711         int i;
712         gchar *size;
713         gchar *sidecars = NULL;
714
715         if (fd->sidecar_files)
716                 sidecars = file_data_sc_list_to_string(fd);
717         size = text_from_size(fd->size);
718
719         gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
720                                         FILE_COLUMN_VERSION, fd->version,
721                                         FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
722                                         FILE_COLUMN_NAME, fd->name,
723                                         FILE_COLUMN_SIDECARS, sidecars,
724                                         FILE_COLUMN_SIZE, size,
725                                         FILE_COLUMN_DATE, text_from_time(fd->date),
726                                         FILE_COLUMN_COLOR, FALSE, -1);
727         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
728                 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
729
730         g_free(size);
731         if (sidecars)
732                 g_free(sidecars);
733 }
734
735 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
736 {
737         GList *work;
738         GtkTreeIter iter;
739         gint valid;
740
741         valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
742
743         work = list;
744         while (work)
745                 {
746                 gint match;
747                 FileData *fd = work->data;
748                 gint done = FALSE;
749
750                 while (!done)
751                         {
752                         FileData *old_fd = NULL;
753                         gint old_version = 0;
754
755                         if (valid)
756                                 {
757                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
758                                                    FILE_COLUMN_POINTER, &old_fd,
759                                                    FILE_COLUMN_VERSION, &old_version,
760                                                    -1);
761
762                                 if (fd == old_fd)
763                                         {
764                                         match = 0;
765                                         }
766                                 else
767                                         {
768                                         if (parent_iter)
769                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
770                                         else
771                                                 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
772
773                                         if (match == 0) g_warning("multiple fd for the same path");
774                                         }
775                                         
776                                 }
777                         else
778                                 {
779                                 match = -1;
780                                 }
781
782                         if (match < 0)
783                                 {
784                                 GtkTreeIter new;
785
786                                 if (valid)
787                                         {
788                                         gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
789                                         }
790                                 else
791                                         {
792                                         gtk_tree_store_append(store, &new, parent_iter);
793                                         }
794
795                                 vflist_setup_iter(vf, store, &new, fd);
796                                 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
797                                 
798                                 if (g_list_find(selected, fd))
799                                         {
800                                         /* renamed files - the same fd appears at different position - select it again*/
801                                         GtkTreeSelection *selection;
802                                         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
803                                         gtk_tree_selection_select_iter(selection, &new);
804                                         }
805
806                                 done = TRUE;
807                                 }
808                         else if (match > 0)
809                                 {
810                                 valid = gtk_tree_store_remove(store, &iter);
811                                 }
812                         else
813                                 {
814                                 if (fd->version != old_version)
815                                         {
816                                         vflist_setup_iter(vf, store, &iter, fd);
817                                         vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
818                                         }
819
820                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
821
822                                 done = TRUE;
823                                 }
824                         }
825                 work = work->next;
826                 }
827
828         while (valid)
829                 {
830                 valid = gtk_tree_store_remove(store, &iter);
831                 }
832 }
833
834 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
835 {
836         gint i;
837         GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
838         gint *new_order;
839         GtkTreeStore *store;
840         GList *work;
841
842         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
843         if (!vf->list) return;
844
845         work = vf->list;
846         i = 0;
847         while (work)
848                 {
849                 FileData *fd = work->data;
850                 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
851                 i++;
852                 work = work->next;
853                 }
854
855         vf->sort_method = type;
856         vf->sort_ascend = ascend;
857
858         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
859
860         new_order = g_malloc(i * sizeof(gint));
861
862         work = vf->list;
863         i = 0;
864         while (work)
865                 {
866                 FileData *fd = work->data;
867                 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
868                 i++;
869                 work = work->next;
870                 }
871
872         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
873         gtk_tree_store_reorder(store, NULL, new_order);
874
875         g_free(new_order);
876         g_hash_table_destroy(fd_idx_hash);
877 }
878
879 /*
880  *-----------------------------------------------------------------------------
881  * thumb updates
882  *-----------------------------------------------------------------------------
883  */
884
885 static gint vflist_thumb_next(ViewFile *vf);
886
887 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
888 {
889         if (vf->func_thumb_status)
890                 {
891                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
892                 }
893 }
894
895 static void vflist_thumb_cleanup(ViewFile *vf)
896 {
897         vflist_thumb_status(vf, 0.0, NULL);
898
899         vf->thumbs_count = 0;
900         vf->thumbs_running = FALSE;
901
902         thumb_loader_free(vf->thumbs_loader);
903         vf->thumbs_loader = NULL;
904
905         vf->thumbs_filedata = NULL;
906 }
907
908 static void vflist_thumb_stop(ViewFile *vf)
909 {
910         if (vf->thumbs_running) vflist_thumb_cleanup(vf);
911 }
912
913 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
914 {
915         GtkTreeStore *store;
916         GtkTreeIter iter;
917
918         if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
919
920         if (fd->pixbuf) g_object_unref(fd->pixbuf);
921         fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
922
923         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
924         gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
925
926         vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
927 }
928
929 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
930 {
931         ViewFile *vf = data;
932
933         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
934                 {
935                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
936                 }
937
938         while (vflist_thumb_next(vf));
939 }
940
941 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
942 {
943         ViewFile *vf = data;
944
945         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
946                 {
947                 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
948                 }
949
950         while (vflist_thumb_next(vf));
951 }
952
953 static gint vflist_thumb_next(ViewFile *vf)
954 {
955         GtkTreePath *tpath;
956         FileData *fd = NULL;
957
958         /* first check the visible files */
959
960         if (GTK_WIDGET_REALIZED(vf->listview) &&
961             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
962                 {
963                 GtkTreeModel *store;
964                 GtkTreeIter iter;
965                 gint valid = TRUE;
966
967                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
968                 gtk_tree_model_get_iter(store, &iter, tpath);
969                 gtk_tree_path_free(tpath);
970
971                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
972                         {
973                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
974                         if (fd->pixbuf) fd = NULL;
975
976                         valid = gtk_tree_model_iter_next(store, &iter);
977                         }
978                 }
979
980         /* then find first undone */
981
982         if (!fd)
983                 {
984                 GList *work = vf->list;
985                 while (work && !fd)
986                         {
987                         FileData *fd_p = work->data;
988                         if (!fd_p->pixbuf)
989                                 fd = fd_p;
990                         else
991                                 {
992                                 GList *work2 = fd_p->sidecar_files;
993
994                                 while (work2 && !fd)
995                                         {
996                                         fd_p = work2->data;
997                                         if (!fd_p->pixbuf) fd = fd_p;
998                                         work2 = work2->next;
999                                         }
1000                                 }
1001                         work = work->next;
1002                         }
1003                 }
1004
1005         if (!fd)
1006                 {
1007                 /* done */
1008                 vflist_thumb_cleanup(vf);
1009                 return FALSE;
1010                 }
1011
1012         vf->thumbs_count++;
1013
1014         vf->thumbs_filedata = fd;
1015
1016         thumb_loader_free(vf->thumbs_loader);
1017
1018         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1019         thumb_loader_set_callbacks(vf->thumbs_loader,
1020                                    vflist_thumb_done_cb,
1021                                    vflist_thumb_error_cb,
1022                                    NULL,
1023                                    vf);
1024
1025         if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1026                 {
1027                 /* set icon to unknown, continue */
1028                 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1029                 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1030
1031                 return TRUE;
1032                 }
1033
1034         return FALSE;
1035 }
1036
1037 static void vflist_thumb_update(ViewFile *vf)
1038 {
1039         vflist_thumb_stop(vf);
1040         if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1041
1042         vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1043         vf->thumbs_running = TRUE;
1044
1045         while (vflist_thumb_next(vf));
1046 }
1047
1048 /*
1049  *-----------------------------------------------------------------------------
1050  * row stuff
1051  *-----------------------------------------------------------------------------
1052  */
1053
1054 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1055 {
1056         return g_list_nth_data(vf->list, row);
1057 }
1058
1059 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1060 {
1061         gint p = 0;
1062         GList *work;
1063
1064         if (!path) return -1;
1065
1066         work = vf->list;
1067         while (work)
1068                 {
1069                 FileData *fd = work->data;
1070                 if (strcmp(path, fd->path) == 0) return p;
1071                 
1072                 work = work->next;
1073                 p++;
1074                 }
1075
1076         return -1;
1077 }
1078
1079 guint vflist_count(ViewFile *vf, gint64 *bytes)
1080 {
1081         if (bytes)
1082                 {
1083                 gint64 b = 0;
1084                 GList *work;
1085
1086                 work = vf->list;
1087                 while (work)
1088                         {
1089                         FileData *fd = work->data;
1090                         work = work->next;
1091                         b += fd->size;
1092                         }
1093
1094                 *bytes = b;
1095                 }
1096
1097         return g_list_length(vf->list);
1098 }
1099
1100 GList *vflist_get_list(ViewFile *vf)
1101 {
1102         GList *list = NULL;
1103         GList *work;
1104
1105         work = vf->list;
1106         while (work)
1107                 {
1108                 FileData *fd = work->data;
1109                 work = work->next;
1110
1111                 list = g_list_prepend(list, file_data_ref(fd));
1112                 }
1113
1114         return g_list_reverse(list);
1115 }
1116
1117 /*
1118  *-----------------------------------------------------------------------------
1119  * selections
1120  *-----------------------------------------------------------------------------
1121  */
1122
1123 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1124 {
1125         GtkTreeModel *store;
1126         GtkTreeSelection *selection;
1127         GList *slist;
1128         GList *work;
1129         gint found = FALSE;
1130
1131         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1132         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1133         work = slist;
1134         while (!found && work)
1135                 {
1136                 GtkTreePath *tpath = work->data;
1137                 FileData *fd_n;
1138                 GtkTreeIter iter;
1139
1140                 gtk_tree_model_get_iter(store, &iter, tpath);
1141                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1142                 if (fd_n == fd) found = TRUE;
1143                 work = work->next;
1144                 }
1145         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1146         g_list_free(slist);
1147
1148         return found;
1149 }
1150
1151 gint vflist_index_is_selected(ViewFile *vf, gint row)
1152 {
1153         FileData *fd;
1154
1155         fd = vf_index_get_data(vf, row);
1156         return vflist_row_is_selected(vf, fd);
1157 }
1158
1159 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1160 {
1161         GtkTreeModel *store;
1162         GtkTreeSelection *selection;
1163         GList *slist;
1164         guint count;
1165
1166         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1167         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1168
1169         if (bytes)
1170                 {
1171                 gint64 b = 0;
1172                 GList *work;
1173
1174                 work = slist;
1175                 while (work)
1176                         {
1177                         GtkTreePath *tpath = work->data;
1178                         GtkTreeIter iter;
1179                         FileData *fd;
1180
1181                         gtk_tree_model_get_iter(store, &iter, tpath);
1182                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1183                         b += fd->size;
1184
1185                         work = work->next;
1186                         }
1187
1188                 *bytes = b;
1189                 }
1190
1191         count = g_list_length(slist);
1192         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1193         g_list_free(slist);
1194
1195         return count;
1196 }
1197
1198 GList *vflist_selection_get_list(ViewFile *vf)
1199 {
1200         GtkTreeModel *store;
1201         GtkTreeSelection *selection;
1202         GList *slist;
1203         GList *list = NULL;
1204         GList *work;
1205
1206         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1207         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1208         work = slist;
1209         while (work)
1210                 {
1211                 GtkTreePath *tpath = work->data;
1212                 FileData *fd;
1213                 GtkTreeIter iter;
1214
1215                 gtk_tree_model_get_iter(store, &iter, tpath);
1216                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1217
1218                 list = g_list_prepend(list, file_data_ref(fd));
1219
1220                 work = work->next;
1221                 }
1222         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1223         g_list_free(slist);
1224
1225         return g_list_reverse(list);
1226 }
1227
1228 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1229 {
1230         GtkTreeModel *store;
1231         GtkTreeSelection *selection;
1232         GList *slist;
1233         GList *list = NULL;
1234         GList *work;
1235
1236         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1237         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1238         work = slist;
1239         while (work)
1240                 {
1241                 GtkTreePath *tpath = work->data;
1242                 FileData *fd;
1243                 GtkTreeIter iter;
1244
1245                 gtk_tree_model_get_iter(store, &iter, tpath);
1246                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1247
1248                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1249
1250                 work = work->next;
1251                 }
1252         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1253         g_list_free(slist);
1254
1255         return g_list_reverse(list);
1256 }
1257
1258 void vflist_select_all(ViewFile *vf)
1259 {
1260         GtkTreeSelection *selection;
1261
1262         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1263         gtk_tree_selection_select_all(selection);
1264
1265         VFLIST_INFO(vf, select_fd) = NULL;
1266 }
1267
1268 void vflist_select_none(ViewFile *vf)
1269 {
1270         GtkTreeSelection *selection;
1271
1272         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1273         gtk_tree_selection_unselect_all(selection);
1274 }
1275
1276 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1277 {
1278         GtkTreePath *tpath;
1279         gboolean result;
1280
1281         tpath = gtk_tree_model_get_path(store, iter);
1282         result = gtk_tree_path_prev(tpath);
1283         if (result)
1284                 gtk_tree_model_get_iter(store, iter, tpath);
1285
1286         gtk_tree_path_free(tpath);
1287
1288         return result;
1289 }
1290
1291 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1292 {
1293         if (!gtk_tree_model_get_iter_first(store, iter))
1294                 return FALSE;
1295
1296         while (TRUE)
1297                 {
1298                 GtkTreeIter next = *iter;
1299                 
1300                 if (gtk_tree_model_iter_next(store, &next))
1301                         *iter = next;
1302                 else
1303                         break;
1304                 }
1305         
1306         return TRUE;
1307 }
1308
1309 void vflist_select_invert(ViewFile *vf)
1310 {
1311         GtkTreeIter iter;
1312         GtkTreeSelection *selection;
1313         GtkTreeModel *store;
1314         gboolean valid;
1315
1316         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1317         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1318
1319         /* Backward iteration prevents scrolling to the end of the list,
1320          * it scrolls to the first selected row instead. */
1321         valid = tree_model_get_iter_last(store, &iter);
1322
1323         while (valid)
1324                 {
1325                 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1326
1327                 if (selected)
1328                         gtk_tree_selection_unselect_iter(selection, &iter);
1329                 else
1330                         gtk_tree_selection_select_iter(selection, &iter);
1331                                 
1332                 valid = tree_model_iter_prev(store, &iter);
1333                 }
1334 }
1335
1336 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1337 {
1338         GtkTreeIter iter;
1339
1340         if (vflist_find_row(vf, fd, &iter) < 0) return;
1341
1342         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1343
1344         if (!vflist_row_is_selected(vf, fd))
1345                 {
1346                 GtkTreeSelection *selection;
1347                 GtkTreeModel *store;
1348                 GtkTreePath *tpath;
1349
1350                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1351                 gtk_tree_selection_unselect_all(selection);
1352                 gtk_tree_selection_select_iter(selection, &iter);
1353                 vflist_move_cursor(vf, &iter);
1354
1355                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1356                 tpath = gtk_tree_model_get_path(store, &iter);
1357                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1358                 gtk_tree_path_free(tpath);
1359                 }
1360 }
1361
1362 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1363 {
1364         GList *work;
1365         
1366         if (sel_fd->parent) sel_fd = sel_fd->parent;
1367         work = vf->list;
1368         
1369         while (work)
1370                 {
1371                 gint match;
1372                 FileData *fd = work->data;
1373                 work = work->next;
1374                 
1375
1376                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1377                 
1378                 if (match >= 0)
1379                         {
1380                         vflist_select_by_fd(vf, fd);
1381                         break;
1382                         }
1383                 }
1384
1385 }
1386
1387 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1388 {
1389         GtkTreeModel *store;
1390         GtkTreeIter iter;
1391         GtkTreeSelection *selection;
1392         gint valid;
1393         gint n = mark - 1;
1394
1395         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1396
1397         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1398         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1399
1400         valid = gtk_tree_model_get_iter_first(store, &iter);
1401         while (valid)
1402                 {
1403                 FileData *fd;
1404                 gboolean mark_val, selected;
1405                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1406
1407                 mark_val = fd->marks[n];
1408                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1409
1410                 switch (mode)
1411                         {
1412                         case MTS_MODE_SET: selected = mark_val;
1413                                 break;
1414                         case MTS_MODE_OR: selected = mark_val | selected;
1415                                 break;
1416                         case MTS_MODE_AND: selected = mark_val & selected;
1417                                 break;
1418                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1419                                 break;
1420                         }
1421
1422                 if (selected)
1423                         gtk_tree_selection_select_iter(selection, &iter);
1424                 else
1425                         gtk_tree_selection_unselect_iter(selection, &iter);
1426
1427                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1428                 }
1429 }
1430
1431 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1432 {
1433         GtkTreeModel *store;
1434         GtkTreeSelection *selection;
1435         GList *slist;
1436         GList *work;
1437         gint n = mark - 1;
1438
1439         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1440
1441         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1442         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1443         work = slist;
1444         while (work)
1445                 {
1446                 GtkTreePath *tpath = work->data;
1447                 FileData *fd;
1448                 GtkTreeIter iter;
1449
1450                 gtk_tree_model_get_iter(store, &iter, tpath);
1451                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1452
1453                 switch (mode)
1454                         {
1455                         case STM_MODE_SET: fd->marks[n] = 1;
1456                                 break;
1457                         case STM_MODE_RESET: fd->marks[n] = 0;
1458                                 break;
1459                         case STM_MODE_TOGGLE: fd->marks[n] = !fd->marks[n];
1460                                 break;
1461                         }
1462                 
1463                 file_data_increment_version(fd);
1464
1465                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, fd->marks[n], -1);
1466
1467                 work = work->next;
1468                 }
1469         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1470         g_list_free(slist);
1471 }
1472
1473 /*
1474  *-----------------------------------------------------------------------------
1475  * core (population)
1476  *-----------------------------------------------------------------------------
1477  */
1478
1479 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1480 {
1481         GtkTreeViewColumn *column;
1482         GtkCellRenderer *cell;
1483         GList *list;
1484
1485         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), 0); /* first column is thumbnail */
1486         if (!column) return;
1487
1488         gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1489
1490         list = gtk_tree_view_column_get_cell_renderers(column);
1491         if (!list) return;
1492         cell = list->data;
1493         g_list_free(list);
1494
1495         g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1496         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1497 }
1498
1499 static void vflist_populate_view(ViewFile *vf)
1500 {
1501         GtkTreeStore *store;
1502         gint thumbs;
1503         GtkTreeRowReference *visible_row = NULL;
1504         GtkTreePath *tpath;
1505         GList *selected;
1506
1507         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1508         thumbs = VFLIST_INFO(vf, thumbs_enabled);
1509
1510         vflist_thumb_stop(vf);
1511
1512         if (!vf->list)
1513                 {
1514                 gtk_tree_store_clear(store);
1515                 vf_send_update(vf);
1516                 return;
1517                 }
1518
1519         if (GTK_WIDGET_REALIZED(vf->listview) &&
1520             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1521                 {
1522                 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1523                 gtk_tree_path_free(tpath);
1524                 }
1525
1526         vflist_listview_set_height(vf->listview, thumbs);
1527
1528         selected = vflist_selection_get_list(vf);
1529         
1530         vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1531
1532         if (selected && vflist_selection_count(vf, NULL) == 0)
1533                 {
1534                 /* all selected files disappeared */
1535                 vflist_select_closest(vf, selected->data);
1536                 }       
1537
1538         filelist_free(selected);
1539         
1540         if (visible_row)
1541                 {
1542                 if (gtk_tree_row_reference_valid(visible_row))
1543                         {
1544                         tpath = gtk_tree_row_reference_get_path(visible_row);
1545                         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1546                         gtk_tree_path_free(tpath);
1547                         }
1548                 gtk_tree_row_reference_free(visible_row);
1549                 }
1550
1551         vf_send_update(vf);
1552         vflist_thumb_update(vf);
1553 }
1554
1555 gint vflist_refresh(ViewFile *vf)
1556 {
1557         GList *old_list;
1558         gint ret = TRUE;
1559
1560         old_list = vf->list;
1561         vf->list = NULL;
1562
1563         DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1564         if (vf->dir_fd)
1565                 {
1566                 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1567
1568                 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1569                 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1570                 }
1571
1572         DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1573
1574         vflist_populate_view(vf);
1575
1576         filelist_free(old_list);
1577         DEBUG_1("%s vflist_refresh: done", get_exec_time());
1578
1579         return ret;
1580 }
1581
1582 static gint vflist_refresh_idle_cb(gpointer data)
1583 {
1584         ViewFile *vf = data;
1585
1586         vflist_refresh(vf);
1587         vf->refresh_idle_id = -1;
1588         return FALSE;
1589 }
1590
1591 static void vflist_refresh_idle_cancel(ViewFile *vf)
1592 {
1593         if (vf->refresh_idle_id != -1) g_source_remove(vf->refresh_idle_id);
1594         vf->refresh_idle_id = -1;
1595 }
1596
1597
1598 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1599
1600 #define CELL_HEIGHT_OVERRIDE 512
1601
1602 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1603 {
1604         GParamSpec *spec;
1605
1606         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1607         if (spec && G_IS_PARAM_SPEC_INT(spec))
1608                 {
1609                 GParamSpecInt *spec_int;
1610
1611                 spec_int = G_PARAM_SPEC_INT(spec);
1612                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1613                 }
1614 }
1615
1616 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1617 {
1618         static GdkColor color;
1619         static GtkWidget *done = NULL;
1620
1621         if (done != widget)
1622                 {
1623                 GtkStyle *style;
1624
1625                 style = gtk_widget_get_style(widget);
1626                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1627                 shift_color(&color, -1, 0);
1628                 done = widget;
1629                 }
1630
1631         return &color;
1632 }
1633
1634 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1635                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1636 {
1637         ViewFile *vf = data;
1638         gboolean set;
1639
1640         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1641         g_object_set(G_OBJECT(cell),
1642                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1643                      "cell-background-set", set, NULL);
1644 }
1645
1646 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1647 {
1648         GtkTreeViewColumn *column;
1649         GtkCellRenderer *renderer;
1650
1651         column = gtk_tree_view_column_new();
1652         gtk_tree_view_column_set_title(column, title);
1653         gtk_tree_view_column_set_min_width(column, 4);
1654
1655         if (!image)
1656                 {
1657                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1658                 renderer = gtk_cell_renderer_text_new();
1659                 if (right_justify)
1660                         {
1661                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1662                         }
1663                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1664                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1665                 if (expand)
1666                         gtk_tree_view_column_set_expand(column, TRUE);
1667                 }
1668         else
1669                 {
1670                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1671                 renderer = gtk_cell_renderer_pixbuf_new();
1672                 cell_renderer_height_override(renderer);
1673                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1674                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1675                 }
1676
1677         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1678         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1679         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1680
1681         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1682 }
1683
1684 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1685 {
1686         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1687         GtkTreeIter iter;
1688         FileData *fd;
1689         gboolean mark;
1690         guint col_idx;
1691
1692         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1693                 return;
1694
1695         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1696
1697         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1698
1699         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1700         mark = !mark;
1701         fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1702         file_data_increment_version(fd);
1703
1704         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1705         gtk_tree_path_free(path);
1706 }
1707
1708 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1709 {
1710         GtkTreeViewColumn *column;
1711         GtkCellRenderer *renderer;
1712         GtkTreeStore *store;
1713         gint index;
1714
1715         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1716
1717         renderer = gtk_cell_renderer_toggle_new();
1718         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1719
1720         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1721         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1722         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1723
1724         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1725         gtk_tree_view_column_set_fixed_width(column, 16);
1726         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1727
1728
1729         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1730 }
1731
1732 /*
1733  *-----------------------------------------------------------------------------
1734  * base
1735  *-----------------------------------------------------------------------------
1736  */
1737
1738 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1739 {
1740         GtkTreeStore *store;
1741
1742         if (!dir_fd) return FALSE;
1743         if (vf->dir_fd == dir_fd) return TRUE;
1744
1745         file_data_unref(vf->dir_fd);
1746         vf->dir_fd = file_data_ref(dir_fd);
1747
1748         /* force complete reload */
1749         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1750         gtk_tree_store_clear(store);
1751
1752         filelist_free(vf->list);
1753         vf->list = NULL;
1754
1755         return vf_refresh(vf);
1756 }
1757
1758 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1759 {
1760         ViewFile *vf = data;
1761
1762         vflist_select_idle_cancel(vf);
1763         vflist_refresh_idle_cancel(vf);
1764         vflist_thumb_stop(vf);
1765
1766         filelist_free(vf->list);
1767 }
1768
1769 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1770 {
1771         GtkTreeStore *store;
1772         GtkTreeSelection *selection;
1773
1774         GType flist_types[FILE_COLUMN_COUNT];
1775         int i;
1776
1777         vf->info = g_new0(ViewFileInfoList, 1);
1778         
1779         VFLIST_INFO(vf, click_fd) = NULL;
1780         VFLIST_INFO(vf, select_fd) = NULL;
1781         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1782
1783         VFLIST_INFO(vf, select_idle_id) = -1;
1784         vf->refresh_idle_id = -1;
1785
1786
1787         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1788         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1789         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1790         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1791         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1792         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1793         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1794         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1795         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1796                 flist_types[i] = G_TYPE_BOOLEAN;
1797
1798         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1799
1800         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1801         g_object_unref(store);
1802
1803         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1804         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1805         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1806
1807         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1808         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1809
1810         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1811
1812         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1813                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1814
1815         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1816         vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1817
1818         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1819         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1820
1821         return vf;
1822 }
1823
1824 void vflist_thumb_set(ViewFile *vf, gint enable)
1825 {
1826         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1827
1828         VFLIST_INFO(vf, thumbs_enabled) = enable;
1829         if (vf->layout) vf_refresh(vf);
1830 }
1831
1832 void vflist_marks_set(ViewFile *vf, gint enable)
1833 {
1834         GList *columns, *work;
1835
1836         if (vf->marks_enabled == enable) return;
1837
1838         vf->marks_enabled = enable;
1839
1840         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1841
1842         work = columns;
1843         while (work)
1844                 {
1845                 GtkTreeViewColumn *column = work->data;
1846                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1847                 work = work->next;
1848
1849                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1850                         gtk_tree_view_column_set_visible(column, enable);
1851                 }
1852
1853         g_list_free(columns);
1854         //vf_refresh(vf);
1855 }
1856
1857 /*
1858  *-----------------------------------------------------------------------------
1859  * maintenance (for rename, move, remove)
1860  *-----------------------------------------------------------------------------
1861  */
1862
1863 void vflist_maint(ViewFile *vf, FileData *fd)
1864 {
1865         gboolean refresh;
1866
1867         if (vf->refresh_idle_id != -1) return;
1868         
1869         refresh = (fd == vf->dir_fd);
1870
1871         if (!refresh && fd->change->dest)
1872                 {
1873                 gchar *base = remove_level_from_path(fd->path);
1874                 refresh = (strcmp(base, vf->dir_fd->path) == 0);
1875                 g_free(base);
1876                 }
1877
1878         if (!refresh && fd->change->dest)
1879                 {
1880                 gchar *dest_base = remove_level_from_path(fd->change->dest);
1881                 refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
1882                 g_free(dest_base);
1883                 }
1884
1885         if (!refresh && fd->change->source)
1886                 {
1887                 gchar *source_base = remove_level_from_path(fd->change->source);
1888                 refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
1889                 g_free(source_base);
1890                 }
1891         
1892         if (refresh)
1893                 {
1894                 vf->refresh_idle_id = g_idle_add(vflist_refresh_idle_cb, vf);
1895                 }
1896 }
1897
1898 /* the plan is to drop these functions and use vflist_maint directly */
1899
1900 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1901 {
1902         vflist_maint(vf, fd);
1903         return TRUE;
1904 }
1905 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
1906 {
1907         vflist_maint(vf, fd);
1908         return TRUE;
1909 }
1910 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
1911 {
1912         vflist_maint(vf, fd);
1913         return TRUE;
1914 }
1915
1916
1917 #if 0
1918 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1919 {
1920         GList *list = NULL;
1921         GList *work;
1922         gint rev = row - 1;
1923         row ++;
1924
1925         work = ignore_list;
1926         while (work)
1927                 {
1928                 gint f = vf_index_by_path(vf, work->data);
1929                 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1930                 work = work->next;
1931                 }
1932
1933         while (list)
1934                 {
1935                 gint c = TRUE;
1936                 work = list;
1937                 while (work && c)
1938                         {
1939                         gpointer p = work->data;
1940                         work = work->next;
1941                         if (row == GPOINTER_TO_INT(p))
1942                                 {
1943                                 row++;
1944                                 c = FALSE;
1945                                 }
1946                         if (rev == GPOINTER_TO_INT(p))
1947                                 {
1948                                 rev--;
1949                                 c = FALSE;
1950                                 }
1951                         if (!c) list = g_list_remove(list, p);
1952                         }
1953                 if (c && list)
1954                         {
1955                         g_list_free(list);
1956                         list = NULL;
1957                         }
1958                 }
1959         if (row > count - 1)
1960                 {
1961                 if (rev < 0)
1962                         return -1;
1963                 else
1964                         return rev;
1965                 }
1966         else
1967                 {
1968                 return row;
1969                 }
1970 }
1971
1972 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1973 {
1974         gint ret = FALSE;
1975         gchar *source_base;
1976         gchar *dest_base;
1977
1978         DEBUG_1("%s vflist_maint_renamed: start", get_exec_time());
1979
1980         if (g_list_index(vf->list, fd) < 0) return FALSE;
1981
1982         source_base = remove_level_from_path(fd->change->source);
1983         dest_base = remove_level_from_path(fd->change->dest);
1984
1985
1986         if (strcmp(source_base, dest_base) == 0)
1987                 {
1988                 GtkTreeStore *store;
1989                 GtkTreeIter iter;
1990                 GtkTreeIter position;
1991                 gint old_row;
1992                 gint n;
1993
1994                 old_row = g_list_index(vf->list, fd);
1995
1996                 vf->list = g_list_remove(vf->list, fd);
1997
1998                 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
1999                 n = g_list_index(vf->list, fd);
2000
2001                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2002                 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2003                     gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2004                         {
2005                         if (old_row >= n)
2006                                 {
2007                                 gtk_tree_store_move_before(store, &iter, &position);
2008                                 }
2009                         else
2010                                 {
2011                                 gtk_tree_store_move_after(store, &iter, &position);
2012                                 }
2013                         }
2014                 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2015
2016                 ret = TRUE;
2017                 }
2018         else
2019                 {
2020                 ret = vflist_maint_removed(vf, fd, NULL);
2021                 }
2022
2023         g_free(source_base);
2024         g_free(dest_base);
2025
2026         DEBUG_1("%s vflist_maint_renamed: done", get_exec_time());
2027
2028         return ret;
2029 }
2030
2031 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2032 {
2033         GtkTreeIter iter;
2034         GList *list;
2035         gint row;
2036         gint new_row = -1;
2037
2038         DEBUG_1("%s vflist_maint_removed: start", get_exec_time());
2039
2040         row = g_list_index(vf->list, fd);
2041         if (row < 0) return FALSE;
2042
2043         if (vflist_index_is_selected(vf, row) &&
2044             layout_image_get_collection(vf->layout, NULL) == NULL)
2045                 {
2046                 gint n;
2047
2048                 n = vf_count(vf, NULL);
2049                 if (ignore_list)
2050                         {
2051                         new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2052                         DEBUG_1("row = %d, closest is %d", row, new_row);
2053                         }
2054                 else
2055                         {
2056                         if (row + 1 < n)
2057                                 {
2058                                 new_row = row + 1;
2059                                 }
2060                         else if (row > 0)
2061                                 {
2062                                 new_row = row - 1;
2063                                 }
2064                         }
2065                 vf_select_none(vf);
2066                 if (new_row >= 0)
2067                         {
2068                         fd = vf_index_get_data(vf, new_row);
2069                         if (vflist_find_row(vf, fd, &iter) >= 0)
2070                                 {
2071                                 GtkTreeSelection *selection;
2072
2073                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2074                                 gtk_tree_selection_select_iter(selection, &iter);
2075                                 vflist_move_cursor(vf, &iter);
2076                                 }
2077                         }
2078                 }
2079
2080         fd = vf_index_get_data(vf, row);
2081         if (vflist_find_row(vf, fd, &iter) >= 0)
2082                 {
2083                 GtkTreeStore *store;
2084                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2085                 gtk_tree_store_remove(store, &iter);
2086                 }
2087         list = g_list_nth(vf->list, row);
2088         fd = list->data;
2089
2090         /* thumbnail loader check */
2091         if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2092         if (vf->thumbs_count > 0) vf->thumbs_count--;
2093
2094         vf->list = g_list_remove(vf->list, fd);
2095         file_data_unref(fd);
2096
2097         vf_send_update(vf);
2098
2099         DEBUG_1("%s vflist_maint_removed: done", get_exec_time());
2100
2101         return TRUE;
2102 }
2103
2104 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2105 {
2106         gint ret = FALSE;
2107         gchar *buf;
2108
2109         if (!fd->change->source || !vf->path) return FALSE;
2110
2111         buf = remove_level_from_path(fd->change->source);
2112
2113         if (strcmp(buf, vf->path) == 0)
2114                 {
2115                 ret = vflist_maint_removed(vf, fd, ignore_list);
2116                 }
2117
2118         g_free(buf);
2119
2120         return ret;
2121 }
2122 #endif