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