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