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