Remove unused functions:
[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 = sidecar_file_data_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_path(ViewFile *vf, const gchar *path)
1528 {
1529         FileData *fd;
1530
1531         if (vflist_row_by_path(vf, path, &fd) < 0) return;
1532
1533         vflist_select_by_fd(vf, fd);
1534 }
1535
1536 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1537 {
1538         GtkTreeIter iter;
1539
1540         if (vflist_find_row(vf, fd, &iter) < 0) return;
1541
1542         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1543
1544         if (!vflist_row_is_selected(vf, fd))
1545                 {
1546                 GtkTreeSelection *selection;
1547                 GtkTreeModel *store;
1548                 GtkTreePath *tpath;
1549
1550                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551                 gtk_tree_selection_unselect_all(selection);
1552                 gtk_tree_selection_select_iter(selection, &iter);
1553                 vflist_move_cursor(vf, &iter);
1554
1555                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1556                 tpath = gtk_tree_model_get_path(store, &iter);
1557                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1558                 gtk_tree_path_free(tpath);
1559                 }
1560 }
1561
1562 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1563 {
1564         GtkTreeModel *store;
1565         GtkTreeIter iter;
1566         GtkTreeSelection *selection;
1567         gint valid;
1568
1569         g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1570
1571         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1572         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1573
1574         valid = gtk_tree_model_get_iter_first(store, &iter);
1575         while (valid)
1576                 {
1577                 FileData *fd;
1578                 gboolean mark_val, selected;
1579                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1580
1581                 mark_val = fd->marks[mark];
1582                 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1583
1584                 switch (mode)
1585                         {
1586                         case MTS_MODE_SET: selected = mark_val;
1587                                 break;
1588                         case MTS_MODE_OR: selected = mark_val | selected;
1589                                 break;
1590                         case MTS_MODE_AND: selected = mark_val & selected;
1591                                 break;
1592                         case MTS_MODE_MINUS: selected = !mark_val & selected;
1593                                 break;
1594                         }
1595
1596                 if (selected)
1597                         gtk_tree_selection_select_iter(selection, &iter);
1598                 else
1599                         gtk_tree_selection_unselect_iter(selection, &iter);
1600
1601                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1602                 }
1603 }
1604
1605 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1606 {
1607         GtkTreeModel *store;
1608         GtkTreeSelection *selection;
1609         GList *slist;
1610         GList *work;
1611
1612         g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1613
1614         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1615         slist = gtk_tree_selection_get_selected_rows(selection, &store);
1616         work = slist;
1617         while (work)
1618                 {
1619                 GtkTreePath *tpath = work->data;
1620                 FileData *fd;
1621                 GtkTreeIter iter;
1622
1623                 gtk_tree_model_get_iter(store, &iter, tpath);
1624                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1625
1626                 switch (mode)
1627                         {
1628                         case STM_MODE_SET: fd->marks[mark] = 1;
1629                                 break;
1630                         case STM_MODE_RESET: fd->marks[mark] = 0;
1631                                 break;
1632                         case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1633                                 break;
1634                         }
1635
1636                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1637
1638                 work = work->next;
1639                 }
1640         g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1641         g_list_free(slist);
1642 }
1643
1644 /*
1645  *-----------------------------------------------------------------------------
1646  * core (population)
1647  *-----------------------------------------------------------------------------
1648  */
1649
1650 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1651 {
1652         GtkTreeViewColumn *column;
1653         GtkCellRenderer *cell;
1654         GList *list;
1655
1656         column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1657         if (!column) return;
1658
1659         gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1660
1661         list = gtk_tree_view_column_get_cell_renderers(column);
1662         if (!list) return;
1663         cell = list->data;
1664         g_list_free(list);
1665
1666         g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1667         gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1668 }
1669
1670 static void vflist_populate_view(ViewFile *vf)
1671 {
1672         GtkTreeStore *store;
1673         GtkTreeIter iter;
1674         gint thumbs;
1675         GList *work;
1676         GtkTreeRowReference *visible_row = NULL;
1677         GtkTreePath *tpath;
1678         gint valid;
1679
1680         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1681         thumbs = VFLIST_INFO(vf, thumbs_enabled);
1682
1683         vflist_thumb_stop(vf);
1684
1685         if (!vf->list)
1686                 {
1687                 gtk_tree_store_clear(store);
1688                 vflist_send_update(vf);
1689                 return;
1690                 }
1691
1692         if (GTK_WIDGET_REALIZED(vf->listview) &&
1693             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1694                 {
1695                 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1696                 gtk_tree_path_free(tpath);
1697                 }
1698
1699         vflist_listview_set_height(vf->listview, thumbs);
1700
1701         valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1702
1703         work = vf->list;
1704         while (work)
1705                 {
1706                 gint match;
1707                 FileData *fd = work->data;
1708                 gint done = FALSE;
1709
1710                 while (!done)
1711                         {
1712                         FileData *old_fd = NULL;
1713
1714                         if (valid)
1715                                 {
1716                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1717
1718                                 if (fd == old_fd)
1719                                         {
1720                                         match = 0;
1721                                         }
1722                                 else
1723                                         {
1724                                         match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1725                                         if (match == 0)
1726                                                 match = -1; /* probably should not happen*/
1727                                         }
1728                                 }
1729
1730                         else
1731                                 {
1732                                 match = -1;
1733                                 }
1734
1735                         if (match < 0)
1736                                 {
1737                                 GtkTreeIter new;
1738
1739                                 if (valid)
1740                                         {
1741                                         gtk_tree_store_insert_before(store, &new, NULL, &iter);
1742                                         }
1743                                 else
1744                                         {
1745                                         gtk_tree_store_append(store, &new, NULL);
1746                                         }
1747                                 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1748
1749                                 done = TRUE;
1750                                 }
1751                         else if (match > 0)
1752                                 {
1753                                 valid = gtk_tree_store_remove(store, &iter);
1754                                 }
1755                         else
1756                                 {
1757                                 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1758
1759                                 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1760
1761                                 done = TRUE;
1762                                 }
1763                         }
1764                 work = work->next;
1765                 }
1766
1767         while (valid)
1768                 {
1769                 valid = gtk_tree_store_remove(store, &iter);
1770                 }
1771
1772         if (visible_row)
1773                 {
1774                 if (gtk_tree_row_reference_valid(visible_row))
1775                         {
1776                         tpath = gtk_tree_row_reference_get_path(visible_row);
1777                         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1778                         gtk_tree_path_free(tpath);
1779                         }
1780                 gtk_tree_row_reference_free(visible_row);
1781                 }
1782
1783         vflist_send_update(vf);
1784         vflist_thumb_update(vf);
1785 }
1786
1787 gint vflist_refresh(ViewFile *vf)
1788 {
1789         GList *old_list;
1790         gint ret = TRUE;
1791
1792         old_list = vf->list;
1793         vf->list = NULL;
1794
1795         if (vf->path)
1796                 {
1797                 ret = filelist_read(vf->path, &vf->list, NULL);
1798                 }
1799
1800         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1801         vflist_populate_view(vf);
1802
1803         filelist_free(old_list);
1804
1805         return ret;
1806 }
1807
1808 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1809
1810 #define CELL_HEIGHT_OVERRIDE 512
1811
1812 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1813 {
1814         GParamSpec *spec;
1815
1816         spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1817         if (spec && G_IS_PARAM_SPEC_INT(spec))
1818                 {
1819                 GParamSpecInt *spec_int;
1820
1821                 spec_int = G_PARAM_SPEC_INT(spec);
1822                 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1823                 }
1824 }
1825
1826 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1827 {
1828         static GdkColor color;
1829         static GtkWidget *done = NULL;
1830
1831         if (done != widget)
1832                 {
1833                 GtkStyle *style;
1834
1835                 style = gtk_widget_get_style(widget);
1836                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1837                 shift_color(&color, -1, 0);
1838                 done = widget;
1839                 }
1840
1841         return &color;
1842 }
1843
1844 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1845                                      GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1846 {
1847         ViewFile *vf = data;
1848         gboolean set;
1849
1850         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1851         g_object_set(G_OBJECT(cell),
1852                      "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1853                      "cell-background-set", set, NULL);
1854 }
1855
1856 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1857 {
1858         GtkTreeViewColumn *column;
1859         GtkCellRenderer *renderer;
1860
1861         column = gtk_tree_view_column_new();
1862         gtk_tree_view_column_set_title(column, title);
1863         gtk_tree_view_column_set_min_width(column, 4);
1864
1865         if (!image)
1866                 {
1867                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1868                 renderer = gtk_cell_renderer_text_new();
1869                 if (right_justify)
1870                         {
1871                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1872                         }
1873                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1874                 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1875                 if (expand)
1876                         gtk_tree_view_column_set_expand(column, TRUE);
1877                 }
1878         else
1879                 {
1880                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1881                 renderer = gtk_cell_renderer_pixbuf_new();
1882                 cell_renderer_height_override(renderer);
1883                 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1884                 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1885                 }
1886
1887         gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1888         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1889         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1890
1891         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1892 }
1893
1894 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1895 {
1896         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1897         GtkTreeIter iter;
1898         FileData *fd;
1899         gboolean mark;
1900         guint col_idx;
1901
1902         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1903                 return;
1904
1905         col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1906
1907         g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1908
1909         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1910         mark = !mark;
1911         fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1912
1913         gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1914         gtk_tree_path_free(path);
1915 }
1916
1917 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1918 {
1919         GtkTreeViewColumn *column;
1920         GtkCellRenderer *renderer;
1921         GtkTreeStore *store;
1922         gint index;
1923
1924         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1925
1926         renderer = gtk_cell_renderer_toggle_new();
1927         column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1928
1929         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1930         g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1931         g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1932
1933         index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1934         gtk_tree_view_column_set_fixed_width(column, 16);
1935         gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1936
1937
1938         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1939 }
1940
1941 /*
1942  *-----------------------------------------------------------------------------
1943  * base
1944  *-----------------------------------------------------------------------------
1945  */
1946
1947 gint vflist_set_path(ViewFile *vf, const gchar *path)
1948 {
1949         GtkTreeStore *store;
1950
1951         if (!path) return FALSE;
1952         if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1953
1954         g_free(vf->path);
1955         vf->path = g_strdup(path);
1956
1957         /* force complete reload */
1958         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1959         gtk_tree_store_clear(store);
1960
1961         filelist_free(vf->list);
1962         vf->list = NULL;
1963
1964         return vflist_refresh(vf);
1965 }
1966
1967 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1968 {
1969         ViewFile *vf = data;
1970
1971         vflist_select_idle_cancel(vf);
1972         vflist_thumb_stop(vf);
1973
1974         filelist_free(vf->list);
1975 }
1976
1977 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1978 {
1979         GtkTreeStore *store;
1980         GtkTreeSelection *selection;
1981
1982         GType flist_types[FILE_COLUMN_COUNT];
1983         int i;
1984
1985         vf->info = g_new0(ViewFileInfoList, 1);
1986         
1987         VFLIST_INFO(vf, click_fd) = NULL;
1988         VFLIST_INFO(vf, select_fd) = NULL;
1989         VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1990
1991         VFLIST_INFO(vf, select_idle_id) = -1;
1992
1993         flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1994         flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1995         flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1996         flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1997         flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1998         flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1999         flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2000         for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2001                 flist_types[i] = G_TYPE_BOOLEAN;
2002
2003         store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2004
2005         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2006         g_object_unref(store);
2007
2008         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2009         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2010         gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2011
2012         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2013         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2014
2015         vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2016
2017         for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2018                 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2019
2020         vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2021         vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2022
2023         vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2024         vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2025
2026         return vf;
2027 }
2028
2029 void vflist_thumb_set(ViewFile *vf, gint enable)
2030 {
2031         if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2032
2033         VFLIST_INFO(vf, thumbs_enabled) = enable;
2034         if (vf->layout) vflist_refresh(vf);
2035 }
2036
2037 void vflist_marks_set(ViewFile *vf, gint enable)
2038 {
2039         GList *columns, *work;
2040
2041         if (vf->marks_enabled == enable) return;
2042
2043         vf->marks_enabled = enable;
2044
2045         columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2046
2047         work = columns;
2048         while (work)
2049                 {
2050                 GtkTreeViewColumn *column = work->data;
2051                 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2052                 work = work->next;
2053
2054                 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2055                         gtk_tree_view_column_set_visible(column, enable);
2056                 }
2057
2058         g_list_free(columns);
2059         //vflist_refresh(vf);
2060 }
2061
2062 /*
2063  *-----------------------------------------------------------------------------
2064  * maintenance (for rename, move, remove)
2065  *-----------------------------------------------------------------------------
2066  */
2067
2068 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2069 {
2070         GList *list = NULL;
2071         GList *work;
2072         gint rev = row - 1;
2073         row ++;
2074
2075         work = ignore_list;
2076         while (work)
2077                 {
2078                 gint f = vflist_index_by_path(vf, work->data);
2079                 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2080                 work = work->next;
2081                 }
2082
2083         while (list)
2084                 {
2085                 gint c = TRUE;
2086                 work = list;
2087                 while (work && c)
2088                         {
2089                         gpointer p = work->data;
2090                         work = work->next;
2091                         if (row == GPOINTER_TO_INT(p))
2092                                 {
2093                                 row++;
2094                                 c = FALSE;
2095                                 }
2096                         if (rev == GPOINTER_TO_INT(p))
2097                                 {
2098                                 rev--;
2099                                 c = FALSE;
2100                                 }
2101                         if (!c) list = g_list_remove(list, p);
2102                         }
2103                 if (c && list)
2104                         {
2105                         g_list_free(list);
2106                         list = NULL;
2107                         }
2108                 }
2109         if (row > count - 1)
2110                 {
2111                 if (rev < 0)
2112                         return -1;
2113                 else
2114                         return rev;
2115                 }
2116         else
2117                 {
2118                 return row;
2119                 }
2120 }
2121
2122 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2123 {
2124         gint ret = FALSE;
2125         gchar *source_base;
2126         gchar *dest_base;
2127
2128         if (g_list_index(vf->list, fd) < 0) return FALSE;
2129
2130         source_base = remove_level_from_path(fd->change->source);
2131         dest_base = remove_level_from_path(fd->change->dest);
2132
2133
2134         if (strcmp(source_base, dest_base) == 0)
2135                 {
2136                 GtkTreeStore *store;
2137                 GtkTreeIter iter;
2138                 GtkTreeIter position;
2139                 gint old_row;
2140                 gint n;
2141
2142                 old_row = g_list_index(vf->list, fd);
2143
2144                 vf->list = g_list_remove(vf->list, fd);
2145
2146                 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2147                 n = g_list_index(vf->list, fd);
2148
2149                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2150                 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2151                     gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2152                         {
2153                         if (old_row >= n)
2154                                 {
2155                                 gtk_tree_store_move_before(store, &iter, &position);
2156                                 }
2157                         else
2158                                 {
2159                                 gtk_tree_store_move_after(store, &iter, &position);
2160                                 }
2161                         }
2162                 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2163
2164                 ret = TRUE;
2165                 }
2166         else
2167                 {
2168                 ret = vflist_maint_removed(vf, fd, NULL);
2169                 }
2170
2171         g_free(source_base);
2172         g_free(dest_base);
2173
2174         return ret;
2175 }
2176
2177 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2178 {
2179         GtkTreeIter iter;
2180         GList *list;
2181         gint row;
2182         gint new_row = -1;
2183
2184         row = g_list_index(vf->list, fd);
2185         if (row < 0) return FALSE;
2186
2187         if (vflist_index_is_selected(vf, row) &&
2188             layout_image_get_collection(vf->layout, NULL) == NULL)
2189                 {
2190                 gint n;
2191
2192                 n = vflist_count(vf, NULL);
2193                 if (ignore_list)
2194                         {
2195                         new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2196                         DEBUG_1("row = %d, closest is %d", row, new_row);
2197                         }
2198                 else
2199                         {
2200                         if (row + 1 < n)
2201                                 {
2202                                 new_row = row + 1;
2203                                 }
2204                         else if (row > 0)
2205                                 {
2206                                 new_row = row - 1;
2207                                 }
2208                         }
2209                 vflist_select_none(vf);
2210                 if (new_row >= 0)
2211                         {
2212                         fd = vflist_index_get_data(vf, new_row);
2213                         if (vflist_find_row(vf, fd, &iter) >= 0)
2214                                 {
2215                                 GtkTreeSelection *selection;
2216
2217                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2218                                 gtk_tree_selection_select_iter(selection, &iter);
2219                                 vflist_move_cursor(vf, &iter);
2220                                 }
2221                         }
2222                 }
2223
2224         fd = vflist_index_get_data(vf, row);
2225         if (vflist_find_row(vf, fd, &iter) >= 0)
2226                 {
2227                 GtkTreeStore *store;
2228                 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2229                 gtk_tree_store_remove(store, &iter);
2230                 }
2231         list = g_list_nth(vf->list, row);
2232         fd = list->data;
2233
2234         /* thumbnail loader check */
2235         if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2236         if (vf->thumbs_count > 0) vf->thumbs_count--;
2237
2238         vf->list = g_list_remove(vf->list, fd);
2239         file_data_unref(fd);
2240
2241         vflist_send_update(vf);
2242
2243         return TRUE;
2244 }
2245
2246 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2247 {
2248         gint ret = FALSE;
2249         gchar *buf;
2250
2251         if (!fd->change->source || !vf->path) return FALSE;
2252
2253         buf = remove_level_from_path(fd->change->source);
2254
2255         if (strcmp(buf, vf->path) == 0)
2256                 {
2257                 ret = vflist_maint_removed(vf, fd, ignore_list);
2258                 }
2259
2260         g_free(buf);
2261
2262         return ret;
2263 }