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