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