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