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