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