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