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