various notification improvements
[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, NotifyType type, 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, file_data_get_mark(fd, 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 = file_data_get_mark(fd, 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                 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1455
1456                 switch (mode)
1457                         {
1458                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1459                                 break;
1460                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1461                                 break;
1462                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1463                                 break;
1464                         }
1465                 
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, file_data_get_mark(fd, 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         file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1712         file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1713         file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1714
1715         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1716         gtk_tree_path_free(path);
1717 }
1718
1719 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1720 {
1721         GtkTreeViewColumn *column;
1722         GtkCellRenderer *renderer;
1723         GtkTreeStore *store;
1724         gint index;
1725
1726         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1727
1728         renderer = gtk_cell_renderer_toggle_new();
1729         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1730
1731         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1732         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1733         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1734
1735         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1736         gtk_tree_view_column_set_fixed_width(column, 16);
1737         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1738
1739
1740         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1741 }
1742
1743 /*
1744  *-----------------------------------------------------------------------------
1745  * base
1746  *-----------------------------------------------------------------------------
1747  */
1748
1749 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1750 {
1751         GtkTreeStore *store;
1752
1753         if (!dir_fd) return FALSE;
1754         if (vf->dir_fd == dir_fd) return TRUE;
1755
1756         file_data_unref(vf->dir_fd);
1757         vf->dir_fd = file_data_ref(dir_fd);
1758
1759         /* force complete reload */
1760         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1761         gtk_tree_store_clear(store);
1762
1763         filelist_free(vf->list);
1764         vf->list = NULL;
1765
1766         return vf_refresh(vf);
1767 }
1768
1769 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1770 {
1771         ViewFile *vf = data;
1772
1773         file_data_unregister_notify_func(vflist_notify_cb, vf);
1774
1775         vflist_select_idle_cancel(vf);
1776         vflist_refresh_idle_cancel(vf);
1777         vflist_thumb_stop(vf);
1778
1779         filelist_free(vf->list);
1780 }
1781
1782 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1783 {
1784         GtkTreeStore *store;
1785         GtkTreeSelection *selection;
1786
1787         GType flist_types[FILE_COLUMN_COUNT];
1788         int i;
1789
1790         vf->info = g_new0(ViewFileInfoList, 1);
1791         
1792         VFLIST_INFO(vf, click_fd) = NULL;
1793         VFLIST_INFO(vf, select_fd) = NULL;
1794         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1795
1796         VFLIST_INFO(vf, select_idle_id) = -1;
1797         vf->refresh_idle_id = -1;
1798
1799
1800         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1801         flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1802         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1803         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1804         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1805         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1806         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1807         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1808         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1809                 flist_types[i] = G_TYPE_BOOLEAN;
1810
1811         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1812
1813         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1814         g_object_unref(store);
1815
1816         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1817         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1818         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1819
1820         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1821         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1822
1823         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1824
1825         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1826                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1827
1828         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1829         vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1830
1831         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1832         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1833
1834         file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1835         return vf;
1836 }
1837
1838 void vflist_thumb_set(ViewFile *vf, gint enable)
1839 {
1840         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1841
1842         VFLIST_INFO(vf, thumbs_enabled) = enable;
1843         if (vf->layout) vf_refresh(vf);
1844 }
1845
1846 void vflist_marks_set(ViewFile *vf, gint enable)
1847 {
1848         GList *columns, *work;
1849
1850         if (vf->marks_enabled == enable) return;
1851
1852         vf->marks_enabled = enable;
1853
1854         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1855
1856         work = columns;
1857         while (work)
1858                 {
1859                 GtkTreeViewColumn *column = work->data;
1860                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1861                 work = work->next;
1862
1863                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1864                         gtk_tree_view_column_set_visible(column, enable);
1865                 }
1866
1867         g_list_free(columns);
1868         //vf_refresh(vf);
1869 }
1870
1871 /*
1872  *-----------------------------------------------------------------------------
1873  * maintenance (for rename, move, remove)
1874  *-----------------------------------------------------------------------------
1875  */
1876
1877 static void vflist_notify_cb(FileData *fd, NotifyType type, gpointer data)
1878 {
1879         ViewFile *vf = data;
1880         gboolean refresh;
1881
1882         if (vf->refresh_idle_id != -1) return;
1883         
1884         refresh = (fd == vf->dir_fd);
1885
1886         if (!refresh)
1887                 {
1888                 gchar *base = remove_level_from_path(fd->path);
1889                 refresh = (strcmp(base, vf->dir_fd->path) == 0);
1890                 g_free(base);
1891                 }
1892
1893         if (!refresh && fd->change && fd->change->dest)
1894                 {
1895                 gchar *dest_base = remove_level_from_path(fd->change->dest);
1896                 refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
1897                 g_free(dest_base);
1898                 }
1899
1900         if (!refresh && fd->change && fd->change->source)
1901                 {
1902                 gchar *source_base = remove_level_from_path(fd->change->source);
1903                 refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
1904                 g_free(source_base);
1905                 }
1906         
1907         if (refresh)
1908                 {
1909                 vf->refresh_idle_id = g_idle_add(vflist_refresh_idle_cb, vf);
1910                 }
1911 }
1912
1913 #if 0
1914
1915 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1916 {
1917         vflist_maint(vf, fd);
1918         return TRUE;
1919 }
1920 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
1921 {
1922         vflist_maint(vf, fd);
1923         return TRUE;
1924 }
1925 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
1926 {
1927         vflist_maint(vf, fd);
1928         return TRUE;
1929 }
1930
1931
1932 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1933 {
1934         GList *list = NULL;
1935         GList *work;
1936         gint rev = row - 1;
1937         row ++;
1938
1939         work = ignore_list;
1940         while (work)
1941                 {
1942                 gint f = vf_index_by_path(vf, work->data);
1943                 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1944                 work = work->next;
1945                 }
1946
1947         while (list)
1948                 {
1949                 gint c = TRUE;
1950                 work = list;
1951                 while (work && c)
1952                         {
1953                         gpointer p = work->data;
1954                         work = work->next;
1955                         if (row == GPOINTER_TO_INT(p))
1956                                 {
1957                                 row++;
1958                                 c = FALSE;
1959                                 }
1960                         if (rev == GPOINTER_TO_INT(p))
1961                                 {
1962                                 rev--;
1963                                 c = FALSE;
1964                                 }
1965                         if (!c) list = g_list_remove(list, p);
1966                         }
1967                 if (c && list)
1968                         {
1969                         g_list_free(list);
1970                         list = NULL;
1971                         }
1972                 }
1973         if (row > count - 1)
1974                 {
1975                 if (rev < 0)
1976                         return -1;
1977                 else
1978                         return rev;
1979                 }
1980         else
1981                 {
1982                 return row;
1983                 }
1984 }
1985
1986 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1987 {
1988         gint ret = FALSE;
1989         gchar *source_base;
1990         gchar *dest_base;
1991
1992         DEBUG_1("%s vflist_maint_renamed: start", get_exec_time());
1993
1994         if (g_list_index(vf->list, fd) < 0) return FALSE;
1995
1996         source_base = remove_level_from_path(fd->change->source);
1997         dest_base = remove_level_from_path(fd->change->dest);
1998
1999
2000         if (strcmp(source_base, dest_base) == 0)
2001                 {
2002                 GtkTreeStore *store;
2003                 GtkTreeIter iter;
2004                 GtkTreeIter position;
2005                 gint old_row;
2006                 gint n;
2007
2008                 old_row = g_list_index(vf->list, fd);
2009
2010                 vf->list = g_list_remove(vf->list, fd);
2011
2012                 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2013                 n = g_list_index(vf->list, fd);
2014
2015                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2016                 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2017                     gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2018                         {
2019                         if (old_row >= n)
2020                                 {
2021                                 gtk_tree_store_move_before(store, &iter, &position);
2022                                 }
2023                         else
2024                                 {
2025                                 gtk_tree_store_move_after(store, &iter, &position);
2026                                 }
2027                         }
2028                 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2029
2030                 ret = TRUE;
2031                 }
2032         else
2033                 {
2034                 ret = vflist_maint_removed(vf, fd, NULL);
2035                 }
2036
2037         g_free(source_base);
2038         g_free(dest_base);
2039
2040         DEBUG_1("%s vflist_maint_renamed: done", get_exec_time());
2041
2042         return ret;
2043 }
2044
2045 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2046 {
2047         GtkTreeIter iter;
2048         GList *list;
2049         gint row;
2050         gint new_row = -1;
2051
2052         DEBUG_1("%s vflist_maint_removed: start", get_exec_time());
2053
2054         row = g_list_index(vf->list, fd);
2055         if (row < 0) return FALSE;
2056
2057         if (vflist_index_is_selected(vf, row) &&
2058             layout_image_get_collection(vf->layout, NULL) == NULL)
2059                 {
2060                 gint n;
2061
2062                 n = vf_count(vf, NULL);
2063                 if (ignore_list)
2064                         {
2065                         new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2066                         DEBUG_1("row = %d, closest is %d", row, new_row);
2067                         }
2068                 else
2069                         {
2070                         if (row + 1 < n)
2071                                 {
2072                                 new_row = row + 1;
2073                                 }
2074                         else if (row > 0)
2075                                 {
2076                                 new_row = row - 1;
2077                                 }
2078                         }
2079                 vf_select_none(vf);
2080                 if (new_row >= 0)
2081                         {
2082                         fd = vf_index_get_data(vf, new_row);
2083                         if (vflist_find_row(vf, fd, &iter) >= 0)
2084                                 {
2085                                 GtkTreeSelection *selection;
2086
2087                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2088                                 gtk_tree_selection_select_iter(selection, &iter);
2089                                 vflist_move_cursor(vf, &iter);
2090                                 }
2091                         }
2092                 }
2093
2094         fd = vf_index_get_data(vf, row);
2095         if (vflist_find_row(vf, fd, &iter) >= 0)
2096                 {
2097                 GtkTreeStore *store;
2098                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2099                 gtk_tree_store_remove(store, &iter);
2100                 }
2101         list = g_list_nth(vf->list, row);
2102         fd = list->data;
2103
2104         /* thumbnail loader check */
2105         if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2106         if (vf->thumbs_count > 0) vf->thumbs_count--;
2107
2108         vf->list = g_list_remove(vf->list, fd);
2109         file_data_unref(fd);
2110
2111         vf_send_update(vf);
2112
2113         DEBUG_1("%s vflist_maint_removed: done", get_exec_time());
2114
2115         return TRUE;
2116 }
2117
2118 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2119 {
2120         gint ret = FALSE;
2121         gchar *buf;
2122
2123         if (!fd->change->source || !vf->path) return FALSE;
2124
2125         buf = remove_level_from_path(fd->change->source);
2126
2127         if (strcmp(buf, vf->path) == 0)
2128                 {
2129                 ret = vflist_maint_removed(vf, fd, ignore_list);
2130                 }
2131
2132         g_free(buf);
2133
2134         return ret;
2135 }
2136 #endif