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