Switch to fullscreen when double clicking on file in icon or list view. Feature reque...
[geeqie.git] / src / view_file_icon.c
1 /*
2  * Geeqie
3  * (C) 2006 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_icon.h"
15
16 #include "bar_info.h"
17 #include "cellrenderericon.h"
18 #include "collect.h"
19 #include "collect-io.h"
20 #include "collect-table.h"
21 #include "dnd.h"
22 #include "editors.h"
23 #include "img-view.h"
24 #include "info.h"
25 #include "filedata.h"
26 #include "layout.h"
27 #include "layout_image.h"
28 #include "menu.h"
29 #include "metadata.h"
30 #include "thumb.h"
31 #include "utilops.h"
32 #include "ui_fileops.h"
33 #include "ui_menu.h"
34 #include "ui_tree_edit.h"
35 #include "uri_utils.h"
36 #include "view_file.h"
37
38 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39
40
41 /* between these, the icon width is increased by thumb_max_width / 2 */
42 #define THUMB_MIN_ICON_WIDTH 128
43 #define THUMB_MAX_ICON_WIDTH 150
44
45 #define VFICON_MAX_COLUMNS 32
46 #define THUMB_BORDER_PADDING 2
47
48 #define VFICON_TIP_DELAY 500
49
50 enum {
51         FILE_COLUMN_POINTER = 0,
52         FILE_COLUMN_COUNT
53 };
54
55 typedef enum {
56         SELECTION_NONE          = 0,
57         SELECTION_SELECTED      = 1 << 0,
58         SELECTION_PRELIGHT      = 1 << 1,
59         SELECTION_FOCUS         = 1 << 2
60 } SelectionType;
61
62 typedef struct _IconData IconData;
63 struct _IconData
64 {
65         SelectionType selected;
66         gint row;
67         FileData *fd;
68 };
69
70 static gint vficon_index_by_id(ViewFile *vf, IconData *in_id);
71
72 static IconData *vficon_icon_data(ViewFile *vf, FileData *fd)
73 {
74         IconData *id = NULL;
75         GList *work;
76
77         if (!fd) return NULL;
78         work = vf->list;
79         while (work && !id)
80                 {
81                 IconData *chk = work->data;
82                 work = work->next;
83                 if (chk->fd == fd) id = chk;
84                 }
85         return id;
86 }
87
88 #if 0
89 /* not used */
90 static gint iconlist_read(FileData *dir_fd, GList **list)
91 {
92         GList *temp;
93         GList *work;
94
95         if (!filelist_read(dir_fd, &temp, NULL)) return FALSE;
96
97         work = temp;
98         while (work)
99                 {
100                 FileData *fd;
101                 IconData *id;
102
103                 fd = work->data;
104                 g_assert(fd->magick == 0x12345678);
105                 id = g_new0(IconData, 1);
106
107                 id->selected = SELECTION_NONE;
108                 id->row = -1;
109                 id->fd = fd;
110
111                 work->data = id;
112                 work = work->next;
113                 }
114
115         *list = temp;
116
117         return TRUE;
118 }
119 #endif
120
121 static void iconlist_free(GList *list)
122 {
123         GList *work = list;
124         while (work)
125                 {
126                 IconData *id = work->data;
127                 file_data_unref(id->fd);
128                 g_free(id);
129                 work = work->next;
130                 }
131
132         g_list_free(list);
133
134 }
135
136 gint iconlist_sort_file_cb(gpointer a, gpointer b)
137 {
138         IconData *ida = a;
139         IconData *idb = b;
140         return filelist_sort_compare_filedata(ida->fd, idb->fd);
141 }
142
143 GList *iconlist_sort(GList *list, SortType method, gint ascend)
144 {
145         return filelist_sort_full(list, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
146 }
147
148 GList *iconlist_insert_sort(GList *list, IconData *id, SortType method, gint ascend)
149 {
150         return filelist_insert_sort_full(list, id, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
151 }
152
153
154 static void vficon_toggle_filenames(ViewFile *vf);
155 static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter);
156 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gint relative);
157 static void vficon_set_focus(ViewFile *vf, IconData *id);
158 static void vficon_thumb_update(ViewFile *vf);
159 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gint force);
160
161
162 /*
163  *-----------------------------------------------------------------------------
164  * pop-up menu
165  *-----------------------------------------------------------------------------
166  */
167
168 GList *vficon_pop_menu_file_list(ViewFile *vf)
169 {
170         if (!VFICON(vf)->click_id) return NULL;
171
172         if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
173                 {
174                 return vf_selection_get_list(vf);
175                 }
176
177         return g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd));
178 }
179
180 void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
181 {
182         ViewFile *vf = data;
183
184         if (!VFICON(vf)->click_id) return;
185
186         if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
187                 {
188                 GList *list;
189
190                 list = vf_selection_get_list(vf);
191                 view_window_new_from_list(list);
192                 filelist_free(list);
193                 }
194         else
195                 {
196                 view_window_new(VFICON(vf)->click_id->fd);
197                 }
198 }
199
200 void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
201 {
202         ViewFile *vf = data;
203
204         file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview);
205 }
206
207 void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
208 {
209         ViewFile *vf = data;
210
211         vficon_toggle_filenames(vf);
212 }
213
214 void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
215 {
216         ViewFile *vf = data;
217
218         vf_refresh(vf);
219 }
220
221 void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
222 {
223         ViewFile *vf = data;
224         vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
225         VFICON(vf)->click_id = NULL;
226         vf->popup = NULL;
227 }
228
229 /*
230  *-------------------------------------------------------------------
231  * signals
232  *-------------------------------------------------------------------
233  */
234
235 static void vficon_send_layout_select(ViewFile *vf, IconData *id)
236 {
237         FileData *read_ahead_fd = NULL;
238         FileData *sel_fd;
239         FileData *cur_fd;
240
241         if (!vf->layout || !id || !id->fd) return;
242
243         sel_fd = id->fd;
244
245         cur_fd = layout_image_get_fd(vf->layout);
246         if (sel_fd == cur_fd) return; /* no change */
247
248         if (options->image.enable_read_ahead)
249                 {
250                 gint row;
251
252                 row = g_list_index(vf->list, id);
253                 if (row > vficon_index_by_fd(vf, cur_fd) &&
254                     (guint) (row + 1) < vf_count(vf, NULL))
255                         {
256                         read_ahead_fd = vf_index_get_data(vf, row + 1);
257                         }
258                 else if (row > 0)
259                         {
260                         read_ahead_fd = vf_index_get_data(vf, row - 1);
261                         }
262                 }
263
264         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
265 }
266
267 static void vficon_toggle_filenames(ViewFile *vf)
268 {
269         VFICON(vf)->show_text = !VFICON(vf)->show_text;
270         options->show_icon_names = VFICON(vf)->show_text;
271
272         vficon_populate_at_new_size(vf, vf->listview->allocation.width, vf->listview->allocation.height, TRUE);
273 }
274
275 static gint vficon_get_icon_width(ViewFile *vf)
276 {
277         gint width;
278
279         if (!VFICON(vf)->show_text) return options->thumbnails.max_width;
280
281         width = options->thumbnails.max_width + options->thumbnails.max_width / 2;
282         if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
283         if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width;
284
285         return width;
286 }
287
288 /*
289  *-------------------------------------------------------------------
290  * misc utils
291  *-------------------------------------------------------------------
292  */
293
294 static gint vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint *col)
295 {
296         gint n;
297
298         n = g_list_index(vf->list, id);
299
300         if (n < 0) return FALSE;
301
302         *row = n / VFICON(vf)->columns;
303         *col = n - (*row * VFICON(vf)->columns);
304
305         return TRUE;
306 }
307
308 static gint vficon_find_iter(ViewFile *vf, IconData *id, GtkTreeIter *iter, gint *column)
309 {
310         GtkTreeModel *store;
311         gint row, col;
312
313         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
314         if (!vficon_find_position(vf, id, &row, &col)) return FALSE;
315         if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
316         if (column) *column = col;
317
318         return TRUE;
319 }
320
321 static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
322 {
323         GtkTreeModel *store;
324         GtkTreeIter p;
325
326         if (row < 0 || col < 0) return NULL;
327
328         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
329         if (gtk_tree_model_iter_nth_child(store, &p, NULL, row))
330                 {
331                 GList *list;
332
333                 gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1);
334                 if (!list) return NULL;
335
336                 if (iter) *iter = p;
337
338                 return g_list_nth_data(list, col);
339                 }
340
341         return NULL;
342 }
343
344 static IconData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
345 {
346         GtkTreePath *tpath;
347         GtkTreeViewColumn *column;
348
349         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
350                                           &tpath, &column, NULL, NULL))
351                 {
352                 GtkTreeModel *store;
353                 GtkTreeIter row;
354                 GList *list;
355                 gint n;
356
357                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
358                 gtk_tree_model_get_iter(store, &row, tpath);
359                 gtk_tree_path_free(tpath);
360
361                 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
362
363                 n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number"));
364                 if (list)
365                         {
366                         if (iter) *iter = row;
367                         return g_list_nth_data(list, n);
368                         }
369                 }
370
371         return NULL;
372 }
373
374 static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
375 {
376         ViewFile *vf = data;
377         GtkTreeModel *store;
378         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
379         GtkTreeIter row;
380         gint column;
381         GList *list;
382         guint toggled_mark;
383         IconData *id;
384
385         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
386         if (!path || !gtk_tree_model_get_iter(store, &row, path))
387                 return;
388
389         gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
390
391         column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number"));
392         g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL);
393
394         id = g_list_nth_data(list, column);
395         if (id)
396                 {
397                 FileData *fd = id->fd;
398                 file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark));
399                 }
400 }
401
402
403 /*
404  *-------------------------------------------------------------------
405  * tooltip type window
406  *-------------------------------------------------------------------
407  */
408
409 static void tip_show(ViewFile *vf)
410 {
411         GtkWidget *label;
412         gint x, y;
413
414         if (VFICON(vf)->tip_window) return;
415
416         gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL);
417
418         VFICON(vf)->tip_id = vficon_find_data_by_coord(vf, x, y, NULL);
419         if (!VFICON(vf)->tip_id) return;
420
421         VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
422         gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE);
423         gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2);
424
425         label = gtk_label_new(VFICON(vf)->tip_id->fd->name);
426
427         g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label);
428         gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label);
429         gtk_widget_show(label);
430
431         gdk_window_get_pointer(NULL, &x, &y, NULL);
432
433         if (!GTK_WIDGET_REALIZED(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window);
434         gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
435         gtk_widget_show(VFICON(vf)->tip_window);
436 }
437
438 static void tip_hide(ViewFile *vf)
439 {
440         if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window);
441         VFICON(vf)->tip_window = NULL;
442 }
443
444 static gint tip_schedule_cb(gpointer data)
445 {
446         ViewFile *vf = data;
447         GtkWidget *window;
448
449         if (VFICON(vf)->tip_delay_id == -1) return FALSE;
450
451         window = gtk_widget_get_toplevel(vf->listview);
452
453         if (GTK_WIDGET_SENSITIVE(window) &&
454             GTK_WINDOW(window)->has_focus)
455                 {
456                 tip_show(vf);
457                 }
458
459         VFICON(vf)->tip_delay_id = -1;
460         return FALSE;
461 }
462
463 static void tip_schedule(ViewFile *vf)
464 {
465         tip_hide(vf);
466
467         if (VFICON(vf)->tip_delay_id != -1)
468                 {
469                 g_source_remove(VFICON(vf)->tip_delay_id);
470                 VFICON(vf)->tip_delay_id = -1;
471                 }
472
473         if (!VFICON(vf)->show_text)
474                 {
475                 VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf);
476                 }
477 }
478
479 static void tip_unschedule(ViewFile *vf)
480 {
481         tip_hide(vf);
482
483         if (VFICON(vf)->tip_delay_id != -1) g_source_remove(VFICON(vf)->tip_delay_id);
484         VFICON(vf)->tip_delay_id = -1;
485 }
486
487 static void tip_update(ViewFile *vf, IconData *id)
488 {
489         if (VFICON(vf)->tip_window)
490                 {
491                 gint x, y;
492
493                 gdk_window_get_pointer(NULL, &x, &y, NULL);
494                 gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
495
496                 if (id != VFICON(vf)->tip_id)
497                         {
498                         GtkWidget *label;
499
500                         VFICON(vf)->tip_id = id;
501
502                         if (!VFICON(vf)->tip_id)
503                                 {
504                                 tip_hide(vf);
505                                 tip_schedule(vf);
506                                 return;
507                                 }
508
509                         label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label");
510                         gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_id->fd->name);
511                         }
512                 }
513         else
514                 {
515                 tip_schedule(vf);
516                 }
517 }
518
519 /*
520  *-------------------------------------------------------------------
521  * dnd
522  *-------------------------------------------------------------------
523  */
524
525 static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
526                            GtkSelectionData *selection_data, guint info,
527                            guint time, gpointer data)
528 {
529         ViewFile *vf = data;
530         GList *list = NULL;
531         gchar *uri_text = NULL;
532         gint total;
533
534         if (!VFICON(vf)->click_id) return;
535
536         if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
537                 {
538                 list = vf_selection_get_list(vf);
539                 }
540         else
541                 {
542                 list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd));
543                 }
544
545         if (!list) return;
546         uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
547         filelist_free(list);
548
549         DEBUG_1("%s", uri_text);
550
551         gtk_selection_data_set(selection_data, selection_data->target,
552                                8, (guchar *)uri_text, total);
553         g_free(uri_text);
554 }
555
556 static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
557                                       int x, int y, GtkSelectionData *selection,
558                                       guint info, guint time, gpointer data)
559 {
560         ViewFile *vf = data;
561
562         if (info == TARGET_TEXT_PLAIN) {
563                 IconData *id = vficon_find_data_by_coord(vf, x, y, NULL);
564
565                 if (id && id->fd) {
566                         /* Add keywords to file */
567                         FileData *fd = id->fd;
568                         gchar *str = g_strndup(selection->data, selection->length);
569                         GList *kw_list = string_to_keywords_list(str);
570                         
571                         metadata_set(fd, kw_list, NULL, TRUE);
572                         string_list_free(kw_list);
573                         g_free(str);
574                         if (vf->layout && vf->layout->bar_info) {
575                                 bar_info_set(vf->layout->bar_info, id->fd);
576                         }
577                 }
578         }
579 }
580
581 static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
582 {
583         ViewFile *vf = data;
584
585         tip_unschedule(vf);
586
587         if (VFICON(vf)->click_id && VFICON(vf)->click_id->fd->thumb_pixbuf)
588                 {
589                 gint items;
590
591                 if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
592                         items = g_list_length(VFICON(vf)->selection);
593                 else
594                         items = 1;
595
596                 dnd_set_drag_icon(widget, context, VFICON(vf)->click_id->fd->thumb_pixbuf, items);
597                 }
598 }
599
600 static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
601 {
602         ViewFile *vf = data;
603
604         vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
605
606         if (context->action == GDK_ACTION_MOVE)
607                 {
608                 vf_refresh(vf);
609                 }
610
611         tip_unschedule(vf);
612 }
613
614 void vficon_dnd_init(ViewFile *vf)
615 {
616         gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
617                             dnd_file_drag_types, dnd_file_drag_types_count,
618                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
619         gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
620                             dnd_file_drag_types, dnd_file_drag_types_count,
621                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
622
623         g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
624                          G_CALLBACK(vficon_dnd_get), vf);
625         g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
626                          G_CALLBACK(vficon_dnd_begin), vf);
627         g_signal_connect(G_OBJECT(vf->listview), "drag_end",
628                          G_CALLBACK(vficon_dnd_end), vf);
629         g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
630                          G_CALLBACK(vficon_drag_data_received), vf);
631 }
632
633 /*
634  *-------------------------------------------------------------------
635  * cell updates
636  *-------------------------------------------------------------------
637  */
638
639 static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value, GtkTreeIter *iter)
640 {
641         GtkTreeModel *store;
642         GList *list;
643
644         if (!id) return;
645
646
647         if (id->selected == value) return;
648         id->selected = value;
649
650         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
651         if (iter)
652                 {
653                 gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
654                 if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1);
655                 }
656         else
657                 {
658                 GtkTreeIter row;
659
660                 if (vficon_find_iter(vf, id, &row, NULL))
661                         {
662                         gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
663                         if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
664                         }
665                 }
666 }
667
668 static void vficon_selection_add(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
669 {
670         if (!id) return;
671
672         vficon_selection_set(vf, id, id->selected | mask, iter);
673 }
674
675 static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
676 {
677         if (!id) return;
678
679         vficon_selection_set(vf, id, id->selected & ~mask, iter);
680 }
681
682 void vficon_marks_set(ViewFile *vf, gint enable)
683 {
684         vficon_populate_at_new_size(vf, vf->listview->allocation.width, vf->listview->allocation.height, TRUE);
685 }
686
687 /*
688  *-------------------------------------------------------------------
689  * selections
690  *-------------------------------------------------------------------
691  */
692
693 static void vficon_verify_selections(ViewFile *vf)
694 {
695         GList *work;
696
697         work = VFICON(vf)->selection;
698         while (work)
699                 {
700                 IconData *id = work->data;
701                 work = work->next;
702
703                 if (vficon_index_by_id(vf, id) >= 0) continue;
704
705                 VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
706                 }
707 }
708
709 void vficon_select_all(ViewFile *vf)
710 {
711         GList *work;
712
713         g_list_free(VFICON(vf)->selection);
714         VFICON(vf)->selection = NULL;
715
716         work = vf->list;
717         while (work)
718                 {
719                 IconData *id = work->data;
720                 work = work->next;
721                 
722                 VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
723                 vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
724                 }
725
726         vf_send_update(vf);
727 }
728
729 void vficon_select_none(ViewFile *vf)
730 {
731         GList *work;
732
733         work = VFICON(vf)->selection;
734         while (work)
735                 {
736                 IconData *id = work->data;
737                 work = work->next;
738
739                 vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
740                 }
741
742         g_list_free(VFICON(vf)->selection);
743         VFICON(vf)->selection = NULL;
744
745         vf_send_update(vf);
746 }
747
748 void vficon_select_invert(ViewFile *vf)
749 {
750         GList *work;
751
752         work = vf->list;
753         while (work)
754                 {
755                 IconData *id = work->data;
756                 work = work->next;
757
758                 if (id->selected & SELECTION_SELECTED)
759                         {
760                         VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
761                         vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
762                         }
763                 else
764                         {
765                         VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
766                         vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
767                         }
768                 }
769
770         vf_send_update(vf);
771 }
772
773 static void vficon_select(ViewFile *vf, IconData *id)
774 {
775         VFICON(vf)->prev_selection = id;
776
777         if (!id || id->selected & SELECTION_SELECTED) return;
778
779         VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
780         vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
781
782         vf_send_update(vf);
783 }
784
785 static void vficon_unselect(ViewFile *vf, IconData *id)
786 {
787         VFICON(vf)->prev_selection = id;
788
789         if (!id || !(id->selected & SELECTION_SELECTED) ) return;
790
791         VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
792         vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
793
794         vf_send_update(vf);
795 }
796
797 static void vficon_select_util(ViewFile *vf, IconData *id, gint select)
798 {
799         if (select)
800                 {
801                 vficon_select(vf, id);
802                 }
803         else
804                 {
805                 vficon_unselect(vf, id);
806                 }
807 }
808
809 static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *end, gint select)
810 {
811         gint row1, col1;
812         gint row2, col2;
813         gint t;
814         gint i, j;
815
816         if (!vficon_find_position(vf, start, &row1, &col1) ||
817             !vficon_find_position(vf, end, &row2, &col2) ) return;
818
819         VFICON(vf)->prev_selection = end;
820
821         if (!options->collections.rectangular_selection)
822                 {
823                 GList *work;
824                 IconData *id;
825
826                 if (g_list_index(vf->list, start) > g_list_index(vf->list, end))
827                         {
828                         id = start;
829                         start = end;
830                         end = id;
831                         }
832
833                 work = g_list_find(vf->list, start);
834                 while (work)
835                         {
836                         id = work->data;
837                         vficon_select_util(vf, id, select);
838
839                         if (work->data != end)
840                                 work = work->next;
841                         else
842                                 work = NULL;
843                         }
844                 return;
845                 }
846
847         if (row2 < row1)
848                 {
849                 t = row1;
850                 row1 = row2;
851                 row2 = t;
852                 }
853         if (col2 < col1)
854                 {
855                 t = col1;
856                 col1 = col2;
857                 col2 = t;
858                 }
859
860         DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2);
861
862         for (i = row1; i <= row2; i++)
863                 {
864                 for (j = col1; j <= col2; j++)
865                         {
866                         IconData *id = vficon_find_data(vf, i, j, NULL);
867                         if (id) vficon_select_util(vf, id, select);
868                         }
869                 }
870 }
871
872 gint vficon_index_is_selected(ViewFile *vf, gint row)
873 {
874         IconData *id = g_list_nth_data(vf->list, row);
875
876         if (!id) return FALSE;
877
878         return (id->selected & SELECTION_SELECTED);
879 }
880
881 guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
882 {
883         if (bytes)
884                 {
885                 gint64 b = 0;
886                 GList *work;
887
888                 work = VFICON(vf)->selection;
889                 while (work)
890                         {
891                         IconData *id = work->data;
892                         FileData *fd = id->fd;
893                         g_assert(fd->magick == 0x12345678);
894                         b += fd->size;
895
896                         work = work->next;
897                         }
898
899                 *bytes = b;
900                 }
901
902         return g_list_length(VFICON(vf)->selection);
903 }
904
905 GList *vficon_selection_get_list(ViewFile *vf)
906 {
907         GList *list = NULL;
908         GList *work;
909
910         work = VFICON(vf)->selection;
911         while (work)
912                 {
913                 IconData *id = work->data;
914                 FileData *fd = id->fd;
915                 g_assert(fd->magick == 0x12345678);
916
917                 list = g_list_prepend(list, file_data_ref(fd));
918
919                 work = work->next;
920                 }
921
922         list = g_list_reverse(list);
923
924         return list;
925 }
926
927 GList *vficon_selection_get_list_by_index(ViewFile *vf)
928 {
929         GList *list = NULL;
930         GList *work;
931
932         work = VFICON(vf)->selection;
933         while (work)
934                 {
935                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data)));
936                 work = work->next;
937                 }
938
939         return g_list_reverse(list);
940 }
941
942 static void vficon_select_by_id(ViewFile *vf, IconData *id)
943 {
944         if (!id) return;
945
946         if (!(id->selected & SELECTION_SELECTED))
947                 {
948                 vf_select_none(vf);
949                 vficon_select(vf, id);
950                 }
951
952         vficon_set_focus(vf, id);
953 }
954
955 void vficon_select_by_fd(ViewFile *vf, FileData *fd)
956 {
957         IconData *id = NULL;
958         GList *work;
959
960         if (!fd) return;
961         work = vf->list;
962         while (work && !id)
963                 {
964                 IconData *chk = work->data;
965                 work = work->next;
966                 if (chk->fd == fd) id = chk;
967                 }
968         vficon_select_by_id(vf, id);
969 }
970
971 void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
972 {
973         GList *work;
974         gint n = mark - 1;
975
976         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
977
978         work = vf->list;
979         while (work)
980                 {
981                 IconData *id = work->data;
982                 FileData *fd = id->fd;
983                 gboolean mark_val, selected;
984
985                 g_assert(fd->magick == 0x12345678);
986
987                 mark_val = file_data_get_mark(fd, n);
988                 selected = (id->selected & SELECTION_SELECTED);
989
990                 switch (mode)
991                         {
992                         case MTS_MODE_SET: selected = mark_val;
993                                 break;
994                         case MTS_MODE_OR: selected = mark_val | selected;
995                                 break;
996                         case MTS_MODE_AND: selected = mark_val & selected;
997                                 break;
998                         case MTS_MODE_MINUS: selected = !mark_val & selected;
999                                 break;
1000                         }
1001
1002                 vficon_select_util(vf, id, selected);
1003
1004                 work = work->next;
1005                 }
1006 }
1007
1008 void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1009 {
1010         GList *slist;
1011         GList *work;
1012         gint n = mark -1;
1013
1014         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1015
1016         slist = vf_selection_get_list(vf);
1017         work = slist;
1018         while (work)
1019                 {
1020                 FileData *fd = work->data;
1021
1022                 switch (mode)
1023                         {
1024                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1025                                 break;
1026                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1027                                 break;
1028                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1029                                 break;
1030                         }
1031                 work = work->next;
1032                 }
1033         filelist_free(slist);
1034 }
1035
1036 static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
1037 {
1038         GList *work;
1039         
1040         if (sel_fd->parent) sel_fd = sel_fd->parent;
1041         work = vf->list;
1042         
1043         while (work)
1044                 {
1045                 gint match;
1046                 IconData *id = work->data;
1047                 FileData *fd = id->fd;
1048                 work = work->next;
1049                 
1050
1051                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1052                 
1053                 if (match >= 0)
1054                         {
1055                         vficon_select(vf, id);
1056                         vficon_send_layout_select(vf, id);
1057                         break;
1058                         }
1059                 }
1060
1061 }
1062
1063
1064 /*
1065  *-------------------------------------------------------------------
1066  * focus
1067  *-------------------------------------------------------------------
1068  */
1069
1070 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gint relative)
1071 {
1072         gint new_row;
1073         gint new_col;
1074
1075         if (relative)
1076                 {
1077                 new_row = VFICON(vf)->focus_row;
1078                 new_col = VFICON(vf)->focus_column;
1079
1080                 new_row += row;
1081                 if (new_row < 0) new_row = 0;
1082                 if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1;
1083
1084                 while (col != 0)
1085                         {
1086                         if (col < 0)
1087                                 {
1088                                 new_col--;
1089                                 col++;
1090                                 }
1091                         else
1092                                 {
1093                                 new_col++;
1094                                 col--;
1095                                 }
1096
1097                         if (new_col < 0)
1098                                 {
1099                                 if (new_row > 0)
1100                                         {
1101                                         new_row--;
1102                                         new_col = VFICON(vf)->columns - 1;
1103                                         }
1104                                 else
1105                                         {
1106                                         new_col = 0;
1107                                         }
1108                                 }
1109                         if (new_col >= VFICON(vf)->columns)
1110                                 {
1111                                 if (new_row < VFICON(vf)->rows - 1)
1112                                         {
1113                                         new_row++;
1114                                         new_col = 0;
1115                                         }
1116                                 else
1117                                         {
1118                                         new_col = VFICON(vf)->columns - 1;
1119                                         }
1120                                 }
1121                         }
1122                 }
1123         else
1124                 {
1125                 new_row = row;
1126                 new_col = col;
1127
1128                 if (new_row >= VFICON(vf)->rows)
1129                         {
1130                         if (VFICON(vf)->rows > 0)
1131                                 new_row = VFICON(vf)->rows - 1;
1132                         else
1133                                 new_row = 0;
1134                         new_col = VFICON(vf)->columns - 1;
1135                         }
1136                 if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1;
1137                 }
1138
1139         if (new_row == VFICON(vf)->rows - 1)
1140                 {
1141                 gint l;
1142
1143                 /* if we moved beyond the last image, go to the last image */
1144
1145                 l = g_list_length(vf->list);
1146                 if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns;
1147                 if (new_col >= l) new_col = l - 1;
1148                 }
1149
1150         vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
1151 }
1152
1153 static void vficon_set_focus(ViewFile *vf, IconData *id)
1154 {
1155         GtkTreeIter iter;
1156         gint row, col;
1157
1158         if (g_list_find(vf->list, VFICON(vf)->focus_id))
1159                 {
1160                 if (id == VFICON(vf)->focus_id)
1161                         {
1162                         /* ensure focus row col are correct */
1163                         vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
1164                         return;
1165                         }
1166                 vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
1167                 }
1168
1169         if (!vficon_find_position(vf, id, &row, &col))
1170                 {
1171                 VFICON(vf)->focus_id = NULL;
1172                 VFICON(vf)->focus_row = -1;
1173                 VFICON(vf)->focus_column = -1;
1174                 return;
1175                 }
1176
1177         VFICON(vf)->focus_id = id;
1178         VFICON(vf)->focus_row = row;
1179         VFICON(vf)->focus_column = col;
1180         vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
1181
1182         if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL))
1183                 {
1184                 GtkTreePath *tpath;
1185                 GtkTreeViewColumn *column;
1186                 GtkTreeModel *store;
1187
1188                 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1189
1190                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1191                 tpath = gtk_tree_model_get_path(store, &iter);
1192                 /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
1193                 column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS);
1194                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE);
1195                 gtk_tree_path_free(tpath);
1196                 }
1197 }
1198
1199 #if 0
1200 static void vficon_update_focus(ViewFile *vf)
1201 {
1202         gint new_row = 0;
1203         gint new_col = 0;
1204
1205         if (VFICON(vf)->focus_id && vficon_find_position(vf, VFICON(vf)->focus_id, &new_row, &new_col))
1206                 {
1207                 /* first find the old focus, if it exists and is valid */
1208                 }
1209         else
1210                 {
1211                 /* (try to) stay where we were */
1212                 new_row = VFICON(vf)->focus_row;
1213                 new_col = VFICON(vf)->focus_column;
1214                 }
1215
1216         vficon_move_focus(vf, new_row, new_col, FALSE);
1217 }
1218 #endif
1219
1220 /* used to figure the page up/down distances */
1221 static gint page_height(ViewFile *vf)
1222 {
1223         GtkAdjustment *adj;
1224         gint page_size;
1225         gint row_height;
1226         gint ret;
1227
1228         adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview));
1229         page_size = (gint)adj->page_increment;
1230
1231         row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2;
1232         if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3;
1233
1234         ret = page_size / row_height;
1235         if (ret < 1) ret = 1;
1236
1237         return ret;
1238 }
1239
1240 /*
1241  *-------------------------------------------------------------------
1242  * keyboard
1243  *-------------------------------------------------------------------
1244  */
1245
1246 static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1247 {
1248         ViewFile *vf = data;
1249         GtkTreeModel *store;
1250         GtkTreeIter iter;
1251         gint column;
1252         GtkTreePath *tpath;
1253         gint cw, ch;
1254
1255         if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return;
1256         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1257         tpath = gtk_tree_model_get_path(store, &iter);
1258         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
1259         gtk_tree_path_free(tpath);
1260         *y += ch;
1261         popup_menu_position_clamp(menu, x, y, 0);
1262 }
1263
1264 gint vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1265 {
1266         ViewFile *vf = data;
1267         gint focus_row = 0;
1268         gint focus_col = 0;
1269         IconData *id;
1270         gint stop_signal;
1271
1272         stop_signal = TRUE;
1273         switch (event->keyval)
1274                 {
1275                 case GDK_Left: case GDK_KP_Left:
1276                         focus_col = -1;
1277                         break;
1278                 case GDK_Right: case GDK_KP_Right:
1279                         focus_col = 1;
1280                         break;
1281                 case GDK_Up: case GDK_KP_Up:
1282                         focus_row = -1;
1283                         break;
1284                 case GDK_Down: case GDK_KP_Down:
1285                         focus_row = 1;
1286                         break;
1287                 case GDK_Page_Up: case GDK_KP_Page_Up:
1288                         focus_row = -page_height(vf);
1289                         break;
1290                 case GDK_Page_Down: case GDK_KP_Page_Down:
1291                         focus_row = page_height(vf);
1292                         break;
1293                 case GDK_Home: case GDK_KP_Home:
1294                         focus_row = -VFICON(vf)->focus_row;
1295                         focus_col = -VFICON(vf)->focus_column;
1296                         break;
1297                 case GDK_End: case GDK_KP_End:
1298                         focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row;
1299                         focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
1300                         break;
1301                 case GDK_space:
1302                         id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1303                         if (id)
1304                                 {
1305                                 VFICON(vf)->click_id = id;
1306                                 if (event->state & GDK_CONTROL_MASK)
1307                                         {
1308                                         gint selected;
1309
1310                                         selected = id->selected & SELECTION_SELECTED;
1311                                         if (selected)
1312                                                 {
1313                                                 vficon_unselect(vf, id);
1314                                                 }
1315                                         else
1316                                                 {
1317                                                 vficon_select(vf, id);
1318                                                 vficon_send_layout_select(vf, id);
1319                                                 }
1320                                         }
1321                                 else
1322                                         {
1323                                         vf_select_none(vf);
1324                                         vficon_select(vf, id);
1325                                         vficon_send_layout_select(vf, id);
1326                                         }
1327                                 }
1328                         break;
1329                 case GDK_Menu:
1330                         id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1331                         VFICON(vf)->click_id = id;
1332
1333                         vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
1334                         tip_unschedule(vf);
1335
1336                         vf->popup = vf_pop_menu(vf);
1337                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
1338                         break;
1339                 default:
1340                         stop_signal = FALSE;
1341                         break;
1342                 }
1343
1344         if (focus_row != 0 || focus_col != 0)
1345                 {
1346                 IconData *new_id;
1347                 IconData *old_id;
1348
1349                 old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1350                 vficon_move_focus(vf, focus_row, focus_col, TRUE);
1351                 new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1352
1353                 if (new_id != old_id)
1354                         {
1355                         if (event->state & GDK_SHIFT_MASK)
1356                                 {
1357                                 if (!options->collections.rectangular_selection)
1358                                         {
1359                                         vficon_select_region_util(vf, old_id, new_id, FALSE);
1360                                         }
1361                                 else
1362                                         {
1363                                         vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE);
1364                                         }
1365                                 vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE);
1366                                 vficon_send_layout_select(vf, new_id);
1367                                 }
1368                         else if (event->state & GDK_CONTROL_MASK)
1369                                 {
1370                                 VFICON(vf)->click_id = new_id;
1371                                 }
1372                         else
1373                                 {
1374                                 VFICON(vf)->click_id = new_id;
1375                                 vf_select_none(vf);
1376                                 vficon_select(vf, new_id);
1377                                 vficon_send_layout_select(vf, new_id);
1378                                 }
1379                         }
1380                 }
1381
1382         if (stop_signal)
1383                 {
1384 #if 0
1385                 g_signal_stop_emission_by_name(GTK_OBJECT(widget), "key_press_event");
1386 #endif
1387                 tip_unschedule(vf);
1388                 }
1389
1390         return stop_signal;
1391 }
1392
1393 /*
1394  *-------------------------------------------------------------------
1395  * mouse
1396  *-------------------------------------------------------------------
1397  */
1398
1399 static gint vficon_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1400 {
1401         ViewFile *vf = data;
1402         IconData *id;
1403
1404         id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, NULL);
1405         tip_update(vf, id);
1406
1407         return FALSE;
1408 }
1409
1410 gint vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1411 {
1412         ViewFile *vf = data;
1413         GtkTreeIter iter;
1414         IconData *id;
1415
1416         tip_unschedule(vf);
1417
1418         id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1419
1420         VFICON(vf)->click_id = id;
1421         vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
1422
1423         switch (bevent->button)
1424                 {
1425                 case MOUSE_BUTTON_LEFT:
1426                         if (!GTK_WIDGET_HAS_FOCUS(vf->listview))
1427                                 {
1428                                 gtk_widget_grab_focus(vf->listview);
1429                                 }
1430 #if 1
1431                         if (bevent->type == GDK_2BUTTON_PRESS &&
1432                             vf->layout)
1433                                 {
1434                                 vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
1435                                 layout_image_full_screen_start(vf->layout);
1436                                 }
1437 #endif
1438                         break;
1439                 case MOUSE_BUTTON_RIGHT:
1440                         vf->popup = vf_pop_menu(vf);
1441                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
1442                         break;
1443                 default:
1444                         break;
1445                 }
1446
1447         return FALSE;
1448 }
1449
1450 gint vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1451 {
1452         ViewFile *vf = data;
1453         GtkTreeIter iter;
1454         IconData *id = NULL;
1455         gint was_selected;
1456
1457         tip_schedule(vf);
1458
1459         if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
1460                 {
1461                 id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1462                 }
1463
1464         if (VFICON(vf)->click_id)
1465                 {
1466                 vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
1467                 }
1468
1469         if (!id || VFICON(vf)->click_id != id) return TRUE;
1470
1471         was_selected = (id->selected & SELECTION_SELECTED);
1472
1473         switch (bevent->button)
1474                 {
1475                 case MOUSE_BUTTON_LEFT:
1476                         {
1477                         vficon_set_focus(vf, id);
1478
1479                         if (bevent->state & GDK_CONTROL_MASK)
1480                                 {
1481                                 gint select;
1482
1483                                 select = !(id->selected & SELECTION_SELECTED);
1484                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1485                                         {
1486                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select);
1487                                         }
1488                                 else
1489                                         {
1490                                         vficon_select_util(vf, id, select);
1491                                         }
1492                                 }
1493                         else
1494                                 {
1495                                 vf_select_none(vf);
1496
1497                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1498                                         {
1499                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE);
1500                                         }
1501                                 else
1502                                         {
1503                                         vficon_select_util(vf, id, TRUE);
1504                                         was_selected = FALSE;
1505                                         }
1506                                 }
1507                         }
1508                         break;
1509                 case MOUSE_BUTTON_MIDDLE:
1510                         {
1511                         vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED));
1512                         }
1513                         break;
1514                 default:
1515                         break;
1516                 }
1517
1518         if (!was_selected && (id->selected & SELECTION_SELECTED))
1519                 {
1520                 vficon_send_layout_select(vf, id);
1521                 }
1522
1523         return TRUE;
1524 }
1525
1526 static gint vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
1527 {
1528         ViewFile *vf = data;
1529
1530         tip_unschedule(vf);
1531         return FALSE;
1532 }
1533
1534 /*
1535  *-------------------------------------------------------------------
1536  * population
1537  *-------------------------------------------------------------------
1538  */
1539
1540 static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
1541 {
1542         GList *list;
1543
1544         gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
1545         g_list_free(list);
1546
1547         return FALSE;
1548 }
1549
1550 static void vficon_clear_store(ViewFile *vf)
1551 {
1552         GtkTreeModel *store;
1553
1554         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1555         gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
1556
1557         gtk_list_store_clear(GTK_LIST_STORE(store));
1558 }
1559
1560 static void vficon_set_thumb(ViewFile *vf, FileData *fd)
1561 {
1562         GtkTreeModel *store;
1563         GtkTreeIter iter;
1564         GList *list;
1565
1566         if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return;
1567
1568         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1569
1570         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1571         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1572 }
1573
1574 static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter)
1575 {
1576         GtkListStore *store;
1577         GList *list = NULL;
1578         gint i;
1579
1580         for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL);
1581
1582         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1583         gtk_list_store_append(store, iter);
1584         gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
1585
1586         return list;
1587 }
1588
1589 static void vficon_populate(ViewFile *vf, gint resize, gint keep_position)
1590 {
1591         GtkTreeModel *store;
1592         GtkTreePath *tpath;
1593         GList *work;
1594         IconData *visible_id = NULL;
1595         gint r, c;
1596         gint valid;
1597         GtkTreeIter iter;
1598
1599         vficon_verify_selections(vf);
1600
1601         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1602
1603         if (keep_position && GTK_WIDGET_REALIZED(vf->listview) &&
1604             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1605                 {
1606                 GtkTreeIter iter;
1607                 GList *list;
1608
1609                 gtk_tree_model_get_iter(store, &iter, tpath);
1610                 gtk_tree_path_free(tpath);
1611
1612                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1613                 if (list) visible_id = list->data;
1614                 }
1615
1616
1617         if (resize)
1618                 {
1619                 gint i;
1620                 gint thumb_width;
1621                 
1622                 vficon_clear_store(vf);
1623
1624                 thumb_width = vficon_get_icon_width(vf);
1625
1626                 for (i = 0; i < VFICON_MAX_COLUMNS; i++)
1627                         {
1628                         GtkTreeViewColumn *column;
1629                         GtkCellRenderer *cell;
1630                         GList *list;
1631
1632                         column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i);
1633                         gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns));
1634                         gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
1635
1636                         list = gtk_tree_view_column_get_cell_renderers(column);
1637                         cell = (list) ? list->data : NULL;
1638                         g_list_free(list);
1639
1640                         if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
1641                                 {
1642                                 g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
1643                                                              "fixed_height", options->thumbnails.max_height,
1644                                                              "show_text", VFICON(vf)->show_text,
1645                                                              "show_marks", vf->marks_enabled,
1646                                                              "num_marks", FILEDATA_MARKS_SIZE,
1647                                                              NULL);
1648                                 }
1649                         }
1650                 if (GTK_WIDGET_REALIZED(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1651                 }
1652
1653         r = -1;
1654         c = 0;
1655
1656         valid = gtk_tree_model_iter_children(store, &iter, NULL);
1657
1658         work = vf->list;
1659         while (work)
1660                 {
1661                 GList *list;
1662                 r++;
1663                 c = 0;
1664                 if (valid)
1665                         {
1666                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1667                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1668                         }
1669                 else
1670                         {
1671                         list = vficon_add_row(vf, &iter);
1672                         }
1673
1674                 while (list)
1675                         {
1676                         IconData *id;
1677
1678                         if (work)
1679                                 {
1680                                 id = work->data;
1681                                 work = work->next;
1682                                 c++;
1683
1684                                 id->row = r;
1685                                 }
1686                         else
1687                                 {
1688                                 id = NULL;
1689                                 }
1690
1691                         list->data = id;
1692                         list = list->next;
1693                         }
1694                 if (valid) valid = gtk_tree_model_iter_next(store, &iter);
1695                 }
1696
1697         r++;
1698         while (valid)
1699                 {
1700                 GList *list;
1701
1702                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1703                 valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1704                 g_list_free(list);
1705                 }
1706
1707         VFICON(vf)->rows = r;
1708
1709         if (visible_id &&
1710             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1711                 {
1712                 GtkTreeIter iter;
1713                 GList *list;
1714
1715                 gtk_tree_model_get_iter(store, &iter, tpath);
1716                 gtk_tree_path_free(tpath);
1717
1718                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1719                 if (g_list_find(list, visible_id) == NULL &&
1720                     vficon_find_iter(vf, visible_id, &iter, NULL))
1721                         {
1722                         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1723                         }
1724                 }
1725
1726
1727         vf_send_update(vf);
1728         vficon_thumb_update(vf);
1729 }
1730
1731 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gint force)
1732 {
1733         gint new_cols;
1734         gint thumb_width;
1735
1736         thumb_width = vficon_get_icon_width(vf);
1737
1738         new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
1739         if (new_cols < 1) new_cols = 1;
1740
1741         if (!force && new_cols == VFICON(vf)->columns) return;
1742
1743         VFICON(vf)->columns = new_cols;
1744
1745         vficon_populate(vf, TRUE, TRUE);
1746
1747         DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows);
1748 }
1749
1750 #if 0
1751 static void vficon_sync(ViewFile *vf)
1752 {
1753         GtkTreeModel *store;
1754         GtkTreeIter iter;
1755         GList *work;
1756         gint r, c;
1757         gint valid;
1758
1759         if (VFICON(vf)->rows == 0) return;
1760
1761         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1762
1763         r = -1;
1764         c = 0;
1765
1766         valid = gtk_tree_model_iter_children(store, &iter, NULL);
1767
1768         work = vf->list;
1769         while (work)
1770                 {
1771                 GList *list;
1772                 r++;
1773                 c = 0;
1774                 if (valid)
1775                         {
1776                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1777                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1778                         }
1779                 else
1780                         {
1781                         list = vficon_add_row(vf, &iter);
1782                         }
1783
1784                 while (list)
1785                         {
1786                         IconData *id;
1787
1788                         if (work)
1789                                 {
1790                                 id = work->data;
1791                                 work = work->next;
1792                                 c++;
1793
1794                                 id->row = r;
1795                                 }
1796                         else
1797                                 {
1798                                 id = NULL;
1799                                 }
1800
1801                         list->data = id;
1802                         list = list->next;
1803                         }
1804                 if (valid) valid = gtk_tree_model_iter_next(store, &iter);
1805                 }
1806
1807         r++;
1808         while (valid)
1809                 {
1810                 GList *list;
1811
1812                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1813                 valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1814                 g_list_free(list);
1815                 }
1816
1817         VFICON(vf)->rows = r;
1818
1819         vficon_update_focus(vf);
1820 }
1821 #endif
1822
1823 #if 0
1824 static void vficon_sync_idle(ViewFile *vf)
1825 {
1826         if (VFICON(vf)->sync_idle_id == -1)
1827                 {
1828                 /* high priority, the view needs to be resynced before a redraw
1829                  * may contain invalid pointers at this time
1830                  */
1831                 VFICON(vf)->sync_idle_id = g_idle_add_full(G_PRIORITY_HIGH, vficon_sync_idle_cb, vf, NULL);
1832                 }
1833 }
1834 #endif
1835
1836 static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
1837 {
1838         ViewFile *vf = data;
1839
1840         vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE);
1841 }
1842
1843 /*
1844  *-----------------------------------------------------------------------------
1845  * misc
1846  *-----------------------------------------------------------------------------
1847  */
1848
1849 void vficon_sort_set(ViewFile *vf, SortType type, gint ascend)
1850 {
1851         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1852
1853         vf->sort_method = type;
1854         vf->sort_ascend = ascend;
1855
1856         if (!vf->list) return;
1857
1858         vf_refresh(vf);
1859 }
1860
1861 /*
1862  *-----------------------------------------------------------------------------
1863  * thumb updates
1864  *-----------------------------------------------------------------------------
1865  */
1866
1867 static gint vficon_thumb_next(ViewFile *vf);
1868
1869 static gdouble vficon_thumb_progress(ViewFile *vf)
1870 {
1871         gint count = 0;
1872         gint done = 0;
1873         
1874         GList *work = vf->list;
1875         while (work)
1876                 {
1877                 IconData *id = work->data;
1878                 FileData *fd = id->fd;
1879                 work = work->next;
1880
1881                 if (fd->thumb_pixbuf) done++;
1882                 count++;
1883                 }
1884         DEBUG_1("thumb progress: %d of %d", done, count);
1885         return (gdouble)done / count;
1886 }
1887
1888 static void vficon_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1889 {
1890         if (vf->func_thumb_status)
1891                 {
1892                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1893                 }
1894 }
1895
1896 static void vficon_thumb_cleanup(ViewFile *vf)
1897 {
1898         vficon_thumb_status(vf, 0.0, NULL);
1899
1900         vf->thumbs_running = FALSE;
1901
1902         thumb_loader_free(vf->thumbs_loader);
1903         vf->thumbs_loader = NULL;
1904
1905         vf->thumbs_filedata = NULL;
1906 }
1907
1908 static void vficon_thumb_stop(ViewFile *vf)
1909 {
1910         if (vf->thumbs_running) vficon_thumb_cleanup(vf);
1911 }
1912
1913 static void vficon_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1914 {
1915         if (!fd) return;
1916
1917         vficon_set_thumb(vf, fd);
1918
1919         vficon_thumb_status(vf, vficon_thumb_progress(vf), _("Loading thumbs..."));
1920 }
1921
1922 static void vficon_thumb_error_cb(ThumbLoader *tl, gpointer data)
1923 {
1924         ViewFile *vf = data;
1925
1926         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1927                 {
1928                 vficon_thumb_do(vf, tl, vf->thumbs_filedata);
1929                 }
1930
1931         while (vficon_thumb_next(vf));
1932 }
1933
1934 static void vficon_thumb_done_cb(ThumbLoader *tl, gpointer data)
1935 {
1936         ViewFile *vf = data;
1937
1938         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1939                 {
1940                 vficon_thumb_do(vf, tl, vf->thumbs_filedata);
1941                 }
1942
1943         while (vficon_thumb_next(vf));
1944 }
1945
1946 static gint vficon_thumb_next(ViewFile *vf)
1947 {
1948         GtkTreePath *tpath;
1949         FileData *fd = NULL;
1950
1951         if (!GTK_WIDGET_REALIZED(vf->listview))
1952                 {
1953                 vficon_thumb_status(vf, 0.0, NULL);
1954                 return FALSE;
1955                 }
1956
1957         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1958                 {
1959                 GtkTreeModel *store;
1960                 GtkTreeIter iter;
1961                 gint valid = TRUE;
1962
1963                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1964                 gtk_tree_model_get_iter(store, &iter, tpath);
1965                 gtk_tree_path_free(tpath);
1966
1967                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1968                         {
1969                         GList *list;
1970
1971                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1972
1973                         while (!fd && list)
1974                                 {
1975                                 IconData *id = list->data;
1976                                 if (id && !id->fd->thumb_pixbuf) fd = id->fd;
1977                                 list = list->next;
1978                                 }
1979
1980                         valid = gtk_tree_model_iter_next(store, &iter);
1981                         }
1982                 }
1983
1984         /* then find first undone */
1985
1986         if (!fd)
1987                 {
1988                 GList *work = vf->list;
1989                 while (work && !fd)
1990                         {
1991                         IconData *id = work->data;
1992                         FileData *fd_p = id->fd;
1993                         work = work->next;
1994
1995                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1996                         }
1997                 }
1998
1999         if (!fd)
2000                 {
2001                 /* done */
2002                 vficon_thumb_cleanup(vf);
2003                 return FALSE;
2004                 }
2005
2006         vf->thumbs_filedata = fd;
2007
2008         thumb_loader_free(vf->thumbs_loader);
2009
2010         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
2011         thumb_loader_set_callbacks(vf->thumbs_loader,
2012                                    vficon_thumb_done_cb,
2013                                    vficon_thumb_error_cb,
2014                                    NULL,
2015                                    vf);
2016
2017         if (!thumb_loader_start(vf->thumbs_loader, fd))
2018                 {
2019                 /* set icon to unknown, continue */
2020                 DEBUG_1("thumb loader start failed %s", fd->path);
2021                 vficon_thumb_do(vf, vf->thumbs_loader, fd);
2022
2023                 return TRUE;
2024                 }
2025
2026         return FALSE;
2027 }
2028
2029 static void vficon_thumb_update(ViewFile *vf)
2030 {
2031         vficon_thumb_stop(vf);
2032
2033         vficon_thumb_status(vf, 0.0, _("Loading thumbs..."));
2034         vf->thumbs_running = TRUE;
2035
2036         while (vficon_thumb_next(vf));
2037 }
2038
2039 /*
2040  *-----------------------------------------------------------------------------
2041  * row stuff
2042  *-----------------------------------------------------------------------------
2043  */
2044
2045 FileData *vficon_index_get_data(ViewFile *vf, gint row)
2046 {
2047         IconData *id;
2048
2049         id = g_list_nth_data(vf->list, row);
2050         return id ? id->fd : NULL;
2051 }
2052
2053 gint vficon_index_by_path(ViewFile *vf, const gchar *path)
2054 {
2055         gint p = 0;
2056         GList *work;
2057
2058         if (!path) return -1;
2059
2060         work = vf->list;
2061         while (work)
2062                 {
2063                 IconData *id = work->data;
2064                 FileData *fd = id->fd;
2065                 if (strcmp(path, fd->path) == 0) return p;
2066                 work = work->next;
2067                 p++;
2068                 }
2069
2070         return -1;
2071 }
2072
2073 gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
2074 {
2075         gint p = 0;
2076         GList *work;
2077
2078         if (!in_fd) return -1;
2079
2080         work = vf->list;
2081         while (work)
2082                 {
2083                 IconData *id = work->data;
2084                 FileData *fd = id->fd;
2085                 if (fd == in_fd) return p;
2086                 work = work->next;
2087                 p++;
2088                 }
2089
2090         return -1;
2091 }
2092
2093 static gint vficon_index_by_id(ViewFile *vf, IconData *in_id)
2094 {
2095         gint p = 0;
2096         GList *work;
2097
2098         if (!in_id) return -1;
2099
2100         work = vf->list;
2101         while (work)
2102                 {
2103                 IconData *id = work->data;
2104                 if (id == in_id) return p;
2105                 work = work->next;
2106                 p++;
2107                 }
2108
2109         return -1;
2110 }
2111
2112 guint vficon_count(ViewFile *vf, gint64 *bytes)
2113 {
2114         if (bytes)
2115                 {
2116                 gint64 b = 0;
2117                 GList *work;
2118
2119                 work = vf->list;
2120                 while (work)
2121                         {
2122                         IconData *id = work->data;
2123                         FileData *fd = id->fd;
2124                         work = work->next;
2125
2126                         b += fd->size;
2127                         }
2128
2129                 *bytes = b;
2130                 }
2131
2132         return g_list_length(vf->list);
2133 }
2134
2135 GList *vficon_get_list(ViewFile *vf)
2136 {
2137         GList *list = NULL;
2138         GList *work;
2139
2140         work = vf->list;
2141         while (work)
2142                 {
2143                 IconData *id = work->data;
2144                 FileData *fd = id->fd;
2145                 work = work->next;
2146
2147                 list = g_list_prepend(list, file_data_ref(fd));
2148                 }
2149
2150         return g_list_reverse(list);
2151 }
2152
2153 /*
2154  *-----------------------------------------------------------------------------
2155  *
2156  *-----------------------------------------------------------------------------
2157  */
2158
2159 static gint vficon_refresh_real(ViewFile *vf, gint keep_position)
2160 {
2161         gint ret = TRUE;
2162         GList *work, *work_fd;
2163         IconData *focus_id;
2164         GList *new_filelist = NULL;
2165         FileData *first_selected = NULL;
2166         GList *new_iconlist = NULL;
2167
2168         focus_id = VFICON(vf)->focus_id;
2169
2170         if (vf->dir_fd)
2171                 {
2172                 ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
2173                 new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
2174                 }
2175
2176         vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
2177         new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
2178
2179         if (VFICON(vf)->selection)
2180                 {
2181                 first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd;
2182                 file_data_ref(first_selected);
2183                 g_list_free(VFICON(vf)->selection);
2184                 VFICON(vf)->selection = NULL;
2185                 
2186
2187                 }
2188
2189         /* check for same files from old_list */
2190         work = vf->list;
2191         work_fd = new_filelist;
2192         while (work || work_fd)
2193                 {
2194                 IconData *id = NULL;
2195                 FileData *fd = NULL;
2196                 FileData *new_fd = NULL;
2197                 gint match;
2198                 
2199                 if (work && work_fd)
2200                         {
2201                         id = work->data;
2202                         fd = id->fd;
2203                         
2204                         new_fd = work_fd->data;
2205                         
2206                         if (fd == new_fd)
2207                                 {
2208                                 /* not changed, go to next */
2209                                 work = work->next;
2210                                 work_fd = work_fd->next;
2211                                 if (id->selected & SELECTION_SELECTED) 
2212                                         {
2213                                         VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id);
2214                                         }
2215                                 continue;
2216                                 }
2217                         
2218                         match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
2219                         if (match == 0) g_warning("multiple fd for the same path");
2220                         }
2221                 else if (work)
2222                         {
2223                         id = work->data;
2224                         fd = id->fd;
2225                         match = -1;
2226                         }
2227                 else /* work_fd */
2228                         {
2229                         new_fd = work_fd->data;
2230                         match = 1;
2231                         }
2232                 
2233                 if (match < 0)
2234                         {
2235                         /* file no longer exists, delete from vf->list */
2236                         GList *to_delete = work;
2237                         work = work->next;
2238                         if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
2239                         if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL;
2240                         file_data_unref(fd);
2241                         g_free(id);
2242                         vf->list = g_list_delete_link(vf->list, to_delete);
2243                         }
2244                 else
2245                         {
2246                         /* new file, add to vf->list */
2247                         id = g_new0(IconData, 1);
2248
2249                         id->selected = SELECTION_NONE;
2250                         id->row = -1;
2251                         id->fd = file_data_ref(new_fd);
2252                         if (work)
2253                                 vf->list = g_list_insert_before(vf->list, work, id);
2254                         else
2255                                 new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */
2256                                 
2257                         work_fd = work_fd->next;
2258                         }
2259
2260                 }
2261
2262         if (new_iconlist)
2263                 {
2264                 vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist));
2265                 }
2266         
2267         VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
2268
2269         filelist_free(new_filelist);
2270
2271         vficon_populate(vf, TRUE, keep_position);
2272
2273         if (first_selected && !VFICON(vf)->selection)
2274                 {
2275                 /* all selected files disappeared */
2276                 vficon_select_closest(vf, first_selected);
2277                 }
2278         file_data_unref(first_selected);
2279         
2280         /* attempt to keep focus on same icon when refreshing */
2281         if (focus_id && g_list_find(vf->list, focus_id))
2282                 {
2283                 vficon_set_focus(vf, focus_id);
2284                 }
2285
2286         return ret;
2287 }
2288
2289 gint vficon_refresh(ViewFile *vf)
2290 {
2291         return vficon_refresh_real(vf, TRUE);
2292 }
2293
2294 /*
2295  *-----------------------------------------------------------------------------
2296  * draw, etc.
2297  *-----------------------------------------------------------------------------
2298  */
2299
2300 typedef struct _ColumnData ColumnData;
2301 struct _ColumnData
2302 {
2303         ViewFile *vf;
2304         gint number;
2305 };
2306
2307 static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
2308                                 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
2309 {
2310         ColumnData *cd = data;
2311         ViewFile *vf;
2312         GtkStyle *style;
2313         GList *list;
2314         GdkColor color_fg;
2315         GdkColor color_bg;
2316         IconData *id;
2317
2318         vf = cd->vf;
2319
2320         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
2321
2322         id = g_list_nth_data(list, cd->number);
2323
2324         if (id) g_assert(id->fd->magick == 0x12345678);
2325
2326         style = gtk_widget_get_style(vf->listview);
2327         if (id && id->selected & SELECTION_SELECTED)
2328                 {
2329                 memcpy(&color_fg, &style->text[GTK_STATE_SELECTED], sizeof(color_fg));
2330                 memcpy(&color_bg, &style->base[GTK_STATE_SELECTED], sizeof(color_bg));
2331                 }
2332         else
2333                 {
2334                 memcpy(&color_fg, &style->text[GTK_STATE_NORMAL], sizeof(color_fg));
2335                 memcpy(&color_bg, &style->base[GTK_STATE_NORMAL], sizeof(color_bg));
2336                 }
2337
2338         if (id && id->selected & SELECTION_PRELIGHT)
2339                 {
2340 #if 0
2341                 shift_color(&color_fg, -1, 0);
2342 #endif
2343                 shift_color(&color_bg, -1, 0);
2344                 }
2345
2346         if (GQV_IS_CELL_RENDERER_ICON(cell))
2347                 {
2348                 if (id)
2349                         {
2350                         gchar *name_sidecars = (gchar *)id->fd->name;
2351                         gchar *sidecars = NULL;
2352
2353                         if (id->fd->sidecar_files)
2354                                 {
2355                                 sidecars = file_data_sc_list_to_string(id->fd);
2356                                 name_sidecars = g_strdup_printf("%s %s", id->fd->name, sidecars);
2357                                 }
2358
2359                         g_object_set(cell,      "pixbuf", id->fd->thumb_pixbuf,
2360                                                 "text", name_sidecars,
2361                                                 "marks", file_data_get_marks(id->fd),
2362                                                 "show_marks", vf->marks_enabled,
2363                                                 "cell-background-gdk", &color_bg,
2364                                                 "cell-background-set", TRUE,
2365                                                 "foreground-gdk", &color_fg,
2366                                                 "foreground-set", TRUE,
2367                                                 "has-focus", (VFICON(vf)->focus_id == id), NULL);
2368                         if (sidecars)
2369                                 {
2370                                 g_free(sidecars);
2371                                 g_free(name_sidecars);
2372                                 }
2373                         }
2374                 else
2375                         {
2376                         g_object_set(cell,      "pixbuf", NULL,
2377                                                 "text", NULL,
2378                                                 "show_marks", FALSE,
2379                                                 "cell-background-set", FALSE,
2380                                                 "foreground-set", FALSE,
2381                                                 "has-focus", FALSE, NULL);
2382                         }
2383                 }
2384 }
2385
2386 static void vficon_append_column(ViewFile *vf, gint n)
2387 {
2388         ColumnData *cd;
2389         GtkTreeViewColumn *column;
2390         GtkCellRenderer *renderer;
2391
2392         column = gtk_tree_view_column_new();
2393         gtk_tree_view_column_set_min_width(column, 0);
2394
2395         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2396         gtk_tree_view_column_set_alignment(column, 0.5);
2397
2398         renderer = gqv_cell_renderer_icon_new();
2399         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2400         g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
2401                                          "ypad", THUMB_BORDER_PADDING,
2402                                          "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
2403
2404         g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
2405         g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
2406
2407         cd = g_new0(ColumnData, 1);
2408         cd->vf = vf;
2409         cd->number = n;
2410         gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
2411
2412         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2413         
2414         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
2415 }
2416
2417 /*
2418  *-----------------------------------------------------------------------------
2419  * base
2420  *-----------------------------------------------------------------------------
2421  */
2422
2423 gint vficon_set_fd(ViewFile *vf, FileData *dir_fd)
2424 {
2425         gint ret;
2426
2427         if (!dir_fd) return FALSE;
2428         if (vf->dir_fd == dir_fd) return TRUE;
2429
2430         file_data_unref(vf->dir_fd);
2431         vf->dir_fd = file_data_ref(dir_fd);
2432
2433         g_list_free(VFICON(vf)->selection);
2434         VFICON(vf)->selection = NULL;
2435
2436         iconlist_free(vf->list);
2437         vf->list = NULL;
2438
2439         /* NOTE: populate will clear the store for us */
2440         ret = vficon_refresh_real(vf, FALSE);
2441
2442         VFICON(vf)->focus_id = NULL;
2443         vficon_move_focus(vf, 0, 0, FALSE);
2444
2445         return ret;
2446 }
2447
2448 void vficon_destroy_cb(GtkWidget *widget, gpointer data)
2449 {
2450         ViewFile *vf = data;
2451
2452         vf_refresh_idle_cancel(vf);
2453         
2454         file_data_unregister_notify_func(vf_notify_cb, vf);
2455
2456         tip_unschedule(vf);
2457
2458         vficon_thumb_cleanup(vf);
2459
2460         iconlist_free(vf->list);
2461         g_list_free(VFICON(vf)->selection);
2462 }
2463
2464 ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
2465 {
2466         GtkListStore *store;
2467         GtkTreeSelection *selection;
2468         gint i;
2469
2470         vf->info = g_new0(ViewFileInfoIcon, 1);
2471
2472         VFICON(vf)->selection = NULL;
2473         VFICON(vf)->prev_selection = NULL;
2474
2475         VFICON(vf)->tip_window = NULL;
2476         VFICON(vf)->tip_delay_id = -1;
2477
2478         VFICON(vf)->focus_row = 0;
2479         VFICON(vf)->focus_column = 0;
2480         VFICON(vf)->focus_id = NULL;
2481
2482         VFICON(vf)->show_text = options->show_icon_names;
2483
2484         store = gtk_list_store_new(1, G_TYPE_POINTER);
2485         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2486         g_object_unref(store);
2487
2488         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2489         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
2490
2491         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2492         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2493
2494         for (i = 0; i < VFICON_MAX_COLUMNS; i++)
2495                 {
2496                 vficon_append_column(vf, i);
2497                 }
2498
2499         /* zero width column to hide tree view focus, we draw it ourselves */
2500         vficon_append_column(vf, i);
2501         /* end column to fill white space */
2502         vficon_append_column(vf, i);
2503
2504         g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
2505                          G_CALLBACK(vficon_sized_cb), vf);
2506
2507         gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
2508                               GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
2509
2510         g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
2511                          G_CALLBACK(vficon_motion_cb), vf);
2512         g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
2513                          G_CALLBACK(vficon_leave_cb), vf);
2514
2515         /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
2516         vficon_populate_at_new_size(vf, 1, 1, FALSE);
2517
2518         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2519
2520         return vf;
2521 }
2522
2523 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */