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