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