Add year 2009 to copyright info everywhere.
[geeqie.git] / src / view_file_icon.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2009 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((gchar *)selection->data, selection->length);
569                         GList *kw_list = string_to_keywords_list(str);
570                         
571                         metadata_append_list(fd, KEYWORD_KEY, kw_list);
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         IconData *id = NULL;
1040         
1041         if (sel_fd->parent) sel_fd = sel_fd->parent;
1042         work = vf->list;
1043         
1044         while (work)
1045                 {
1046                 gint match;
1047                 FileData *fd;
1048                 
1049                 id = work->data;
1050                 fd = id->fd;
1051                 work = work->next;
1052
1053                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1054                 
1055                 if (match >= 0) break;
1056                 }
1057         
1058         if (id)
1059                 {
1060                 vficon_select(vf, id);
1061                 vficon_send_layout_select(vf, id);
1062                 }
1063 }
1064
1065
1066 /*
1067  *-------------------------------------------------------------------
1068  * focus
1069  *-------------------------------------------------------------------
1070  */
1071
1072 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gint relative)
1073 {
1074         gint new_row;
1075         gint new_col;
1076
1077         if (relative)
1078                 {
1079                 new_row = VFICON(vf)->focus_row;
1080                 new_col = VFICON(vf)->focus_column;
1081
1082                 new_row += row;
1083                 if (new_row < 0) new_row = 0;
1084                 if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1;
1085
1086                 while (col != 0)
1087                         {
1088                         if (col < 0)
1089                                 {
1090                                 new_col--;
1091                                 col++;
1092                                 }
1093                         else
1094                                 {
1095                                 new_col++;
1096                                 col--;
1097                                 }
1098
1099                         if (new_col < 0)
1100                                 {
1101                                 if (new_row > 0)
1102                                         {
1103                                         new_row--;
1104                                         new_col = VFICON(vf)->columns - 1;
1105                                         }
1106                                 else
1107                                         {
1108                                         new_col = 0;
1109                                         }
1110                                 }
1111                         if (new_col >= VFICON(vf)->columns)
1112                                 {
1113                                 if (new_row < VFICON(vf)->rows - 1)
1114                                         {
1115                                         new_row++;
1116                                         new_col = 0;
1117                                         }
1118                                 else
1119                                         {
1120                                         new_col = VFICON(vf)->columns - 1;
1121                                         }
1122                                 }
1123                         }
1124                 }
1125         else
1126                 {
1127                 new_row = row;
1128                 new_col = col;
1129
1130                 if (new_row >= VFICON(vf)->rows)
1131                         {
1132                         if (VFICON(vf)->rows > 0)
1133                                 new_row = VFICON(vf)->rows - 1;
1134                         else
1135                                 new_row = 0;
1136                         new_col = VFICON(vf)->columns - 1;
1137                         }
1138                 if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1;
1139                 }
1140
1141         if (new_row == VFICON(vf)->rows - 1)
1142                 {
1143                 gint l;
1144
1145                 /* if we moved beyond the last image, go to the last image */
1146
1147                 l = g_list_length(vf->list);
1148                 if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns;
1149                 if (new_col >= l) new_col = l - 1;
1150                 }
1151
1152         vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
1153 }
1154
1155 static void vficon_set_focus(ViewFile *vf, IconData *id)
1156 {
1157         GtkTreeIter iter;
1158         gint row, col;
1159
1160         if (g_list_find(vf->list, VFICON(vf)->focus_id))
1161                 {
1162                 if (id == VFICON(vf)->focus_id)
1163                         {
1164                         /* ensure focus row col are correct */
1165                         vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
1166                         return;
1167                         }
1168                 vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
1169                 }
1170
1171         if (!vficon_find_position(vf, id, &row, &col))
1172                 {
1173                 VFICON(vf)->focus_id = NULL;
1174                 VFICON(vf)->focus_row = -1;
1175                 VFICON(vf)->focus_column = -1;
1176                 return;
1177                 }
1178
1179         VFICON(vf)->focus_id = id;
1180         VFICON(vf)->focus_row = row;
1181         VFICON(vf)->focus_column = col;
1182         vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
1183
1184         if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL))
1185                 {
1186                 GtkTreePath *tpath;
1187                 GtkTreeViewColumn *column;
1188                 GtkTreeModel *store;
1189
1190                 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1191
1192                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1193                 tpath = gtk_tree_model_get_path(store, &iter);
1194                 /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
1195                 column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS);
1196                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE);
1197                 gtk_tree_path_free(tpath);
1198                 }
1199 }
1200
1201 #if 0
1202 static void vficon_update_focus(ViewFile *vf)
1203 {
1204         gint new_row = 0;
1205         gint new_col = 0;
1206
1207         if (VFICON(vf)->focus_id && vficon_find_position(vf, VFICON(vf)->focus_id, &new_row, &new_col))
1208                 {
1209                 /* first find the old focus, if it exists and is valid */
1210                 }
1211         else
1212                 {
1213                 /* (try to) stay where we were */
1214                 new_row = VFICON(vf)->focus_row;
1215                 new_col = VFICON(vf)->focus_column;
1216                 }
1217
1218         vficon_move_focus(vf, new_row, new_col, FALSE);
1219 }
1220 #endif
1221
1222 /* used to figure the page up/down distances */
1223 static gint page_height(ViewFile *vf)
1224 {
1225         GtkAdjustment *adj;
1226         gint page_size;
1227         gint row_height;
1228         gint ret;
1229
1230         adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview));
1231         page_size = (gint)adj->page_increment;
1232
1233         row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2;
1234         if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3;
1235
1236         ret = page_size / row_height;
1237         if (ret < 1) ret = 1;
1238
1239         return ret;
1240 }
1241
1242 /*
1243  *-------------------------------------------------------------------
1244  * keyboard
1245  *-------------------------------------------------------------------
1246  */
1247
1248 static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1249 {
1250         ViewFile *vf = data;
1251         GtkTreeModel *store;
1252         GtkTreeIter iter;
1253         gint column;
1254         GtkTreePath *tpath;
1255         gint cw, ch;
1256
1257         if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return;
1258         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1259         tpath = gtk_tree_model_get_path(store, &iter);
1260         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
1261         gtk_tree_path_free(tpath);
1262         *y += ch;
1263         popup_menu_position_clamp(menu, x, y, 0);
1264 }
1265
1266 gint vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1267 {
1268         ViewFile *vf = data;
1269         gint focus_row = 0;
1270         gint focus_col = 0;
1271         IconData *id;
1272         gint stop_signal;
1273
1274         stop_signal = TRUE;
1275         switch (event->keyval)
1276                 {
1277                 case GDK_Left: case GDK_KP_Left:
1278                         focus_col = -1;
1279                         break;
1280                 case GDK_Right: case GDK_KP_Right:
1281                         focus_col = 1;
1282                         break;
1283                 case GDK_Up: case GDK_KP_Up:
1284                         focus_row = -1;
1285                         break;
1286                 case GDK_Down: case GDK_KP_Down:
1287                         focus_row = 1;
1288                         break;
1289                 case GDK_Page_Up: case GDK_KP_Page_Up:
1290                         focus_row = -page_height(vf);
1291                         break;
1292                 case GDK_Page_Down: case GDK_KP_Page_Down:
1293                         focus_row = page_height(vf);
1294                         break;
1295                 case GDK_Home: case GDK_KP_Home:
1296                         focus_row = -VFICON(vf)->focus_row;
1297                         focus_col = -VFICON(vf)->focus_column;
1298                         break;
1299                 case GDK_End: case GDK_KP_End:
1300                         focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row;
1301                         focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
1302                         break;
1303                 case GDK_space:
1304                         id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1305                         if (id)
1306                                 {
1307                                 VFICON(vf)->click_id = id;
1308                                 if (event->state & GDK_CONTROL_MASK)
1309                                         {
1310                                         gint selected;
1311
1312                                         selected = id->selected & SELECTION_SELECTED;
1313                                         if (selected)
1314                                                 {
1315                                                 vficon_unselect(vf, id);
1316                                                 }
1317                                         else
1318                                                 {
1319                                                 vficon_select(vf, id);
1320                                                 vficon_send_layout_select(vf, id);
1321                                                 }
1322                                         }
1323                                 else
1324                                         {
1325                                         vf_select_none(vf);
1326                                         vficon_select(vf, id);
1327                                         vficon_send_layout_select(vf, id);
1328                                         }
1329                                 }
1330                         break;
1331                 case GDK_Menu:
1332                         id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1333                         VFICON(vf)->click_id = id;
1334
1335                         vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
1336                         tip_unschedule(vf);
1337
1338                         vf->popup = vf_pop_menu(vf);
1339                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
1340                         break;
1341                 default:
1342                         stop_signal = FALSE;
1343                         break;
1344                 }
1345
1346         if (focus_row != 0 || focus_col != 0)
1347                 {
1348                 IconData *new_id;
1349                 IconData *old_id;
1350
1351                 old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1352                 vficon_move_focus(vf, focus_row, focus_col, TRUE);
1353                 new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1354
1355                 if (new_id != old_id)
1356                         {
1357                         if (event->state & GDK_SHIFT_MASK)
1358                                 {
1359                                 if (!options->collections.rectangular_selection)
1360                                         {
1361                                         vficon_select_region_util(vf, old_id, new_id, FALSE);
1362                                         }
1363                                 else
1364                                         {
1365                                         vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE);
1366                                         }
1367                                 vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE);
1368                                 vficon_send_layout_select(vf, new_id);
1369                                 }
1370                         else if (event->state & GDK_CONTROL_MASK)
1371                                 {
1372                                 VFICON(vf)->click_id = new_id;
1373                                 }
1374                         else
1375                                 {
1376                                 VFICON(vf)->click_id = new_id;
1377                                 vf_select_none(vf);
1378                                 vficon_select(vf, new_id);
1379                                 vficon_send_layout_select(vf, new_id);
1380                                 }
1381                         }
1382                 }
1383
1384         if (stop_signal)
1385                 {
1386 #if 0
1387                 g_signal_stop_emission_by_name(GTK_OBJECT(widget), "key_press_event");
1388 #endif
1389                 tip_unschedule(vf);
1390                 }
1391
1392         return stop_signal;
1393 }
1394
1395 /*
1396  *-------------------------------------------------------------------
1397  * mouse
1398  *-------------------------------------------------------------------
1399  */
1400
1401 static gint vficon_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1402 {
1403         ViewFile *vf = data;
1404         IconData *id;
1405
1406         id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, NULL);
1407         tip_update(vf, id);
1408
1409         return FALSE;
1410 }
1411
1412 gint vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1413 {
1414         ViewFile *vf = data;
1415         GtkTreeIter iter;
1416         IconData *id;
1417
1418         tip_unschedule(vf);
1419
1420         id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1421
1422         VFICON(vf)->click_id = id;
1423         vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
1424
1425         switch (bevent->button)
1426                 {
1427                 case MOUSE_BUTTON_LEFT:
1428                         if (!GTK_WIDGET_HAS_FOCUS(vf->listview))
1429                                 {
1430                                 gtk_widget_grab_focus(vf->listview);
1431                                 }
1432 #if 1
1433                         if (bevent->type == GDK_2BUTTON_PRESS &&
1434                             vf->layout)
1435                                 {
1436                                 vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
1437                                 layout_image_full_screen_start(vf->layout);
1438                                 }
1439 #endif
1440                         break;
1441                 case MOUSE_BUTTON_RIGHT:
1442                         vf->popup = vf_pop_menu(vf);
1443                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
1444                         break;
1445                 default:
1446                         break;
1447                 }
1448
1449         return FALSE;
1450 }
1451
1452 gint vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1453 {
1454         ViewFile *vf = data;
1455         GtkTreeIter iter;
1456         IconData *id = NULL;
1457         gint was_selected;
1458
1459         tip_schedule(vf);
1460
1461         if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
1462                 {
1463                 id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1464                 }
1465
1466         if (VFICON(vf)->click_id)
1467                 {
1468                 vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
1469                 }
1470
1471         if (!id || VFICON(vf)->click_id != id) return TRUE;
1472
1473         was_selected = (id->selected & SELECTION_SELECTED);
1474
1475         switch (bevent->button)
1476                 {
1477                 case MOUSE_BUTTON_LEFT:
1478                         {
1479                         vficon_set_focus(vf, id);
1480
1481                         if (bevent->state & GDK_CONTROL_MASK)
1482                                 {
1483                                 gint select;
1484
1485                                 select = !(id->selected & SELECTION_SELECTED);
1486                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1487                                         {
1488                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select);
1489                                         }
1490                                 else
1491                                         {
1492                                         vficon_select_util(vf, id, select);
1493                                         }
1494                                 }
1495                         else
1496                                 {
1497                                 vf_select_none(vf);
1498
1499                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1500                                         {
1501                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE);
1502                                         }
1503                                 else
1504                                         {
1505                                         vficon_select_util(vf, id, TRUE);
1506                                         was_selected = FALSE;
1507                                         }
1508                                 }
1509                         }
1510                         break;
1511                 case MOUSE_BUTTON_MIDDLE:
1512                         {
1513                         vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED));
1514                         }
1515                         break;
1516                 default:
1517                         break;
1518                 }
1519
1520         if (!was_selected && (id->selected & SELECTION_SELECTED))
1521                 {
1522                 vficon_send_layout_select(vf, id);
1523                 }
1524
1525         return TRUE;
1526 }
1527
1528 static gint vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
1529 {
1530         ViewFile *vf = data;
1531
1532         tip_unschedule(vf);
1533         return FALSE;
1534 }
1535
1536 /*
1537  *-------------------------------------------------------------------
1538  * population
1539  *-------------------------------------------------------------------
1540  */
1541
1542 static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
1543 {
1544         GList *list;
1545
1546         gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
1547         g_list_free(list);
1548
1549         return FALSE;
1550 }
1551
1552 static void vficon_clear_store(ViewFile *vf)
1553 {
1554         GtkTreeModel *store;
1555
1556         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1557         gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
1558
1559         gtk_list_store_clear(GTK_LIST_STORE(store));
1560 }
1561
1562 static void vficon_set_thumb(ViewFile *vf, FileData *fd)
1563 {
1564         GtkTreeModel *store;
1565         GtkTreeIter iter;
1566         GList *list;
1567
1568         if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return;
1569
1570         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1571
1572         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1573         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1574 }
1575
1576 static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter)
1577 {
1578         GtkListStore *store;
1579         GList *list = NULL;
1580         gint i;
1581
1582         for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL);
1583
1584         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1585         gtk_list_store_append(store, iter);
1586         gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
1587
1588         return list;
1589 }
1590
1591 static void vficon_populate(ViewFile *vf, gint resize, gint keep_position)
1592 {
1593         GtkTreeModel *store;
1594         GtkTreePath *tpath;
1595         GList *work;
1596         IconData *visible_id = NULL;
1597         gint r, c;
1598         gint valid;
1599         GtkTreeIter iter;
1600
1601         vficon_verify_selections(vf);
1602
1603         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1604
1605         if (keep_position && GTK_WIDGET_REALIZED(vf->listview) &&
1606             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1607                 {
1608                 GtkTreeIter iter;
1609                 GList *list;
1610
1611                 gtk_tree_model_get_iter(store, &iter, tpath);
1612                 gtk_tree_path_free(tpath);
1613
1614                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1615                 if (list) visible_id = list->data;
1616                 }
1617
1618
1619         if (resize)
1620                 {
1621                 gint i;
1622                 gint thumb_width;
1623                 
1624                 vficon_clear_store(vf);
1625
1626                 thumb_width = vficon_get_icon_width(vf);
1627
1628                 for (i = 0; i < VFICON_MAX_COLUMNS; i++)
1629                         {
1630                         GtkTreeViewColumn *column;
1631                         GtkCellRenderer *cell;
1632                         GList *list;
1633
1634                         column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i);
1635                         gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns));
1636                         gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
1637
1638                         list = gtk_tree_view_column_get_cell_renderers(column);
1639                         cell = (list) ? list->data : NULL;
1640                         g_list_free(list);
1641
1642                         if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
1643                                 {
1644                                 g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
1645                                                              "fixed_height", options->thumbnails.max_height,
1646                                                              "show_text", VFICON(vf)->show_text,
1647                                                              "show_marks", vf->marks_enabled,
1648                                                              "num_marks", FILEDATA_MARKS_SIZE,
1649                                                              NULL);
1650                                 }
1651                         }
1652                 if (GTK_WIDGET_REALIZED(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1653                 }
1654
1655         r = -1;
1656         c = 0;
1657
1658         valid = gtk_tree_model_iter_children(store, &iter, NULL);
1659
1660         work = vf->list;
1661         while (work)
1662                 {
1663                 GList *list;
1664                 r++;
1665                 c = 0;
1666                 if (valid)
1667                         {
1668                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1669                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1670                         }
1671                 else
1672                         {
1673                         list = vficon_add_row(vf, &iter);
1674                         }
1675
1676                 while (list)
1677                         {
1678                         IconData *id;
1679
1680                         if (work)
1681                                 {
1682                                 id = work->data;
1683                                 work = work->next;
1684                                 c++;
1685
1686                                 id->row = r;
1687                                 }
1688                         else
1689                                 {
1690                                 id = NULL;
1691                                 }
1692
1693                         list->data = id;
1694                         list = list->next;
1695                         }
1696                 if (valid) valid = gtk_tree_model_iter_next(store, &iter);
1697                 }
1698
1699         r++;
1700         while (valid)
1701                 {
1702                 GList *list;
1703
1704                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1705                 valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1706                 g_list_free(list);
1707                 }
1708
1709         VFICON(vf)->rows = r;
1710
1711         if (visible_id &&
1712             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1713                 {
1714                 GtkTreeIter iter;
1715                 GList *list;
1716
1717                 gtk_tree_model_get_iter(store, &iter, tpath);
1718                 gtk_tree_path_free(tpath);
1719
1720                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1721                 if (g_list_find(list, visible_id) == NULL &&
1722                     vficon_find_iter(vf, visible_id, &iter, NULL))
1723                         {
1724                         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1725                         }
1726                 }
1727
1728
1729         vf_send_update(vf);
1730         vficon_thumb_update(vf);
1731 }
1732
1733 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gint force)
1734 {
1735         gint new_cols;
1736         gint thumb_width;
1737
1738         thumb_width = vficon_get_icon_width(vf);
1739
1740         new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
1741         if (new_cols < 1) new_cols = 1;
1742
1743         if (!force && new_cols == VFICON(vf)->columns) return;
1744
1745         VFICON(vf)->columns = new_cols;
1746
1747         vficon_populate(vf, TRUE, TRUE);
1748
1749         DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows);
1750 }
1751
1752 #if 0
1753 static void vficon_sync(ViewFile *vf)
1754 {
1755         GtkTreeModel *store;
1756         GtkTreeIter iter;
1757         GList *work;
1758         gint r, c;
1759         gint valid;
1760
1761         if (VFICON(vf)->rows == 0) return;
1762
1763         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1764
1765         r = -1;
1766         c = 0;
1767
1768         valid = gtk_tree_model_iter_children(store, &iter, NULL);
1769
1770         work = vf->list;
1771         while (work)
1772                 {
1773                 GList *list;
1774                 r++;
1775                 c = 0;
1776                 if (valid)
1777                         {
1778                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1779                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1780                         }
1781                 else
1782                         {
1783                         list = vficon_add_row(vf, &iter);
1784                         }
1785
1786                 while (list)
1787                         {
1788                         IconData *id;
1789
1790                         if (work)
1791                                 {
1792                                 id = work->data;
1793                                 work = work->next;
1794                                 c++;
1795
1796                                 id->row = r;
1797                                 }
1798                         else
1799                                 {
1800                                 id = NULL;
1801                                 }
1802
1803                         list->data = id;
1804                         list = list->next;
1805                         }
1806                 if (valid) valid = gtk_tree_model_iter_next(store, &iter);
1807                 }
1808
1809         r++;
1810         while (valid)
1811                 {
1812                 GList *list;
1813
1814                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1815                 valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1816                 g_list_free(list);
1817                 }
1818
1819         VFICON(vf)->rows = r;
1820
1821         vficon_update_focus(vf);
1822 }
1823 #endif
1824
1825 #if 0
1826 static void vficon_sync_idle(ViewFile *vf)
1827 {
1828         if (VFICON(vf)->sync_idle_id == -1)
1829                 {
1830                 /* high priority, the view needs to be resynced before a redraw
1831                  * may contain invalid pointers at this time
1832                  */
1833                 VFICON(vf)->sync_idle_id = g_idle_add_full(G_PRIORITY_HIGH, vficon_sync_idle_cb, vf, NULL);
1834                 }
1835 }
1836 #endif
1837
1838 static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
1839 {
1840         ViewFile *vf = data;
1841
1842         vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE);
1843 }
1844
1845 /*
1846  *-----------------------------------------------------------------------------
1847  * misc
1848  *-----------------------------------------------------------------------------
1849  */
1850
1851 void vficon_sort_set(ViewFile *vf, SortType type, gint ascend)
1852 {
1853         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1854
1855         vf->sort_method = type;
1856         vf->sort_ascend = ascend;
1857
1858         if (!vf->list) return;
1859
1860         vf_refresh(vf);
1861 }
1862
1863 /*
1864  *-----------------------------------------------------------------------------
1865  * thumb updates
1866  *-----------------------------------------------------------------------------
1867  */
1868
1869 static gint vficon_thumb_next(ViewFile *vf);
1870
1871 static gdouble vficon_thumb_progress(ViewFile *vf)
1872 {
1873         gint count = 0;
1874         gint done = 0;
1875         
1876         GList *work = vf->list;
1877         while (work)
1878                 {
1879                 IconData *id = work->data;
1880                 FileData *fd = id->fd;
1881                 work = work->next;
1882
1883                 if (fd->thumb_pixbuf) done++;
1884                 count++;
1885                 }
1886         DEBUG_1("thumb progress: %d of %d", done, count);
1887         return (gdouble)done / count;
1888 }
1889
1890 static void vficon_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1891 {
1892         if (vf->func_thumb_status)
1893                 {
1894                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1895                 }
1896 }
1897
1898 static void vficon_thumb_cleanup(ViewFile *vf)
1899 {
1900         vficon_thumb_status(vf, 0.0, NULL);
1901
1902         vf->thumbs_running = FALSE;
1903
1904         thumb_loader_free(vf->thumbs_loader);
1905         vf->thumbs_loader = NULL;
1906
1907         vf->thumbs_filedata = NULL;
1908 }
1909
1910 static void vficon_thumb_stop(ViewFile *vf)
1911 {
1912         if (vf->thumbs_running) vficon_thumb_cleanup(vf);
1913 }
1914
1915 static void vficon_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1916 {
1917         if (!fd) return;
1918
1919         vficon_set_thumb(vf, fd);
1920
1921         vficon_thumb_status(vf, vficon_thumb_progress(vf), _("Loading thumbs..."));
1922 }
1923
1924 static void vficon_thumb_error_cb(ThumbLoader *tl, gpointer data)
1925 {
1926         ViewFile *vf = data;
1927
1928         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1929                 {
1930                 vficon_thumb_do(vf, tl, vf->thumbs_filedata);
1931                 }
1932
1933         while (vficon_thumb_next(vf));
1934 }
1935
1936 static void vficon_thumb_done_cb(ThumbLoader *tl, gpointer data)
1937 {
1938         ViewFile *vf = data;
1939
1940         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1941                 {
1942                 vficon_thumb_do(vf, tl, vf->thumbs_filedata);
1943                 }
1944
1945         while (vficon_thumb_next(vf));
1946 }
1947
1948 static gint vficon_thumb_next(ViewFile *vf)
1949 {
1950         GtkTreePath *tpath;
1951         FileData *fd = NULL;
1952
1953         if (!GTK_WIDGET_REALIZED(vf->listview))
1954                 {
1955                 vficon_thumb_status(vf, 0.0, NULL);
1956                 return FALSE;
1957                 }
1958
1959         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1960                 {
1961                 GtkTreeModel *store;
1962                 GtkTreeIter iter;
1963                 gint valid = TRUE;
1964
1965                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1966                 gtk_tree_model_get_iter(store, &iter, tpath);
1967                 gtk_tree_path_free(tpath);
1968
1969                 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1970                         {
1971                         GList *list;
1972
1973                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1974
1975                         while (!fd && list)
1976                                 {
1977                                 IconData *id = list->data;
1978                                 if (id && !id->fd->thumb_pixbuf) fd = id->fd;
1979                                 list = list->next;
1980                                 }
1981
1982                         valid = gtk_tree_model_iter_next(store, &iter);
1983                         }
1984                 }
1985
1986         /* then find first undone */
1987
1988         if (!fd)
1989                 {
1990                 GList *work = vf->list;
1991                 while (work && !fd)
1992                         {
1993                         IconData *id = work->data;
1994                         FileData *fd_p = id->fd;
1995                         work = work->next;
1996
1997                         if (!fd_p->thumb_pixbuf) fd = fd_p;
1998                         }
1999                 }
2000
2001         if (!fd)
2002                 {
2003                 /* done */
2004                 vficon_thumb_cleanup(vf);
2005                 return FALSE;
2006                 }
2007
2008         vf->thumbs_filedata = fd;
2009
2010         thumb_loader_free(vf->thumbs_loader);
2011
2012         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
2013         thumb_loader_set_callbacks(vf->thumbs_loader,
2014                                    vficon_thumb_done_cb,
2015                                    vficon_thumb_error_cb,
2016                                    NULL,
2017                                    vf);
2018
2019         if (!thumb_loader_start(vf->thumbs_loader, fd))
2020                 {
2021                 /* set icon to unknown, continue */
2022                 DEBUG_1("thumb loader start failed %s", fd->path);
2023                 vficon_thumb_do(vf, vf->thumbs_loader, fd);
2024
2025                 return TRUE;
2026                 }
2027
2028         return FALSE;
2029 }
2030
2031 static void vficon_thumb_update(ViewFile *vf)
2032 {
2033         vficon_thumb_stop(vf);
2034
2035         vficon_thumb_status(vf, 0.0, _("Loading thumbs..."));
2036         vf->thumbs_running = TRUE;
2037
2038         while (vficon_thumb_next(vf));
2039 }
2040
2041 /*
2042  *-----------------------------------------------------------------------------
2043  * row stuff
2044  *-----------------------------------------------------------------------------
2045  */
2046
2047 FileData *vficon_index_get_data(ViewFile *vf, gint row)
2048 {
2049         IconData *id;
2050
2051         id = g_list_nth_data(vf->list, row);
2052         return id ? id->fd : NULL;
2053 }
2054
2055 gint vficon_index_by_path(ViewFile *vf, const gchar *path)
2056 {
2057         gint p = 0;
2058         GList *work;
2059
2060         if (!path) return -1;
2061
2062         work = vf->list;
2063         while (work)
2064                 {
2065                 IconData *id = work->data;
2066                 FileData *fd = id->fd;
2067                 if (strcmp(path, fd->path) == 0) return p;
2068                 work = work->next;
2069                 p++;
2070                 }
2071
2072         return -1;
2073 }
2074
2075 gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
2076 {
2077         gint p = 0;
2078         GList *work;
2079
2080         if (!in_fd) return -1;
2081
2082         work = vf->list;
2083         while (work)
2084                 {
2085                 IconData *id = work->data;
2086                 FileData *fd = id->fd;
2087                 if (fd == in_fd) return p;
2088                 work = work->next;
2089                 p++;
2090                 }
2091
2092         return -1;
2093 }
2094
2095 static gint vficon_index_by_id(ViewFile *vf, IconData *in_id)
2096 {
2097         gint p = 0;
2098         GList *work;
2099
2100         if (!in_id) return -1;
2101
2102         work = vf->list;
2103         while (work)
2104                 {
2105                 IconData *id = work->data;
2106                 if (id == in_id) return p;
2107                 work = work->next;
2108                 p++;
2109                 }
2110
2111         return -1;
2112 }
2113
2114 guint vficon_count(ViewFile *vf, gint64 *bytes)
2115 {
2116         if (bytes)
2117                 {
2118                 gint64 b = 0;
2119                 GList *work;
2120
2121                 work = vf->list;
2122                 while (work)
2123                         {
2124                         IconData *id = work->data;
2125                         FileData *fd = id->fd;
2126                         work = work->next;
2127
2128                         b += fd->size;
2129                         }
2130
2131                 *bytes = b;
2132                 }
2133
2134         return g_list_length(vf->list);
2135 }
2136
2137 GList *vficon_get_list(ViewFile *vf)
2138 {
2139         GList *list = NULL;
2140         GList *work;
2141
2142         work = vf->list;
2143         while (work)
2144                 {
2145                 IconData *id = work->data;
2146                 FileData *fd = id->fd;
2147                 work = work->next;
2148
2149                 list = g_list_prepend(list, file_data_ref(fd));
2150                 }
2151
2152         return g_list_reverse(list);
2153 }
2154
2155 /*
2156  *-----------------------------------------------------------------------------
2157  *
2158  *-----------------------------------------------------------------------------
2159  */
2160
2161 static gint vficon_refresh_real(ViewFile *vf, gint keep_position)
2162 {
2163         gint ret = TRUE;
2164         GList *work, *work_fd;
2165         IconData *focus_id;
2166         GList *new_filelist = NULL;
2167         FileData *first_selected = NULL;
2168         GList *new_iconlist = NULL;
2169
2170         focus_id = VFICON(vf)->focus_id;
2171
2172         if (vf->dir_fd)
2173                 {
2174                 ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
2175                 new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
2176                 }
2177
2178         vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
2179         new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
2180
2181         if (VFICON(vf)->selection)
2182                 {
2183                 first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd;
2184                 file_data_ref(first_selected);
2185                 g_list_free(VFICON(vf)->selection);
2186                 VFICON(vf)->selection = NULL;
2187                 
2188
2189                 }
2190
2191         /* check for same files from old_list */
2192         work = vf->list;
2193         work_fd = new_filelist;
2194         while (work || work_fd)
2195                 {
2196                 IconData *id = NULL;
2197                 FileData *fd = NULL;
2198                 FileData *new_fd = NULL;
2199                 gint match;
2200                 
2201                 if (work && work_fd)
2202                         {
2203                         id = work->data;
2204                         fd = id->fd;
2205                         
2206                         new_fd = work_fd->data;
2207                         
2208                         if (fd == new_fd)
2209                                 {
2210                                 /* not changed, go to next */
2211                                 work = work->next;
2212                                 work_fd = work_fd->next;
2213                                 if (id->selected & SELECTION_SELECTED) 
2214                                         {
2215                                         VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id);
2216                                         }
2217                                 continue;
2218                                 }
2219                         
2220                         match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
2221                         if (match == 0) g_warning("multiple fd for the same path");
2222                         }
2223                 else if (work)
2224                         {
2225                         id = work->data;
2226                         fd = id->fd;
2227                         match = -1;
2228                         }
2229                 else /* work_fd */
2230                         {
2231                         new_fd = work_fd->data;
2232                         match = 1;
2233                         }
2234                 
2235                 if (match < 0)
2236                         {
2237                         /* file no longer exists, delete from vf->list */
2238                         GList *to_delete = work;
2239                         work = work->next;
2240                         if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
2241                         if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL;
2242                         file_data_unref(fd);
2243                         g_free(id);
2244                         vf->list = g_list_delete_link(vf->list, to_delete);
2245                         }
2246                 else
2247                         {
2248                         /* new file, add to vf->list */
2249                         id = g_new0(IconData, 1);
2250
2251                         id->selected = SELECTION_NONE;
2252                         id->row = -1;
2253                         id->fd = file_data_ref(new_fd);
2254                         if (work)
2255                                 vf->list = g_list_insert_before(vf->list, work, id);
2256                         else
2257                                 new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */
2258                                 
2259                         work_fd = work_fd->next;
2260                         }
2261
2262                 }
2263
2264         if (new_iconlist)
2265                 {
2266                 vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist));
2267                 }
2268         
2269         VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
2270
2271         filelist_free(new_filelist);
2272
2273         vficon_populate(vf, TRUE, keep_position);
2274
2275         if (first_selected && !VFICON(vf)->selection)
2276                 {
2277                 /* all selected files disappeared */
2278                 vficon_select_closest(vf, first_selected);
2279                 }
2280         file_data_unref(first_selected);
2281         
2282         /* attempt to keep focus on same icon when refreshing */
2283         if (focus_id && g_list_find(vf->list, focus_id))
2284                 {
2285                 vficon_set_focus(vf, focus_id);
2286                 }
2287
2288         return ret;
2289 }
2290
2291 gint vficon_refresh(ViewFile *vf)
2292 {
2293         return vficon_refresh_real(vf, TRUE);
2294 }
2295
2296 /*
2297  *-----------------------------------------------------------------------------
2298  * draw, etc.
2299  *-----------------------------------------------------------------------------
2300  */
2301
2302 typedef struct _ColumnData ColumnData;
2303 struct _ColumnData
2304 {
2305         ViewFile *vf;
2306         gint number;
2307 };
2308
2309 static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
2310                                 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
2311 {
2312         ColumnData *cd = data;
2313         ViewFile *vf;
2314         GtkStyle *style;
2315         GList *list;
2316         GdkColor color_fg;
2317         GdkColor color_bg;
2318         IconData *id;
2319
2320         vf = cd->vf;
2321
2322         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
2323
2324         id = g_list_nth_data(list, cd->number);
2325
2326         if (id) g_assert(id->fd->magick == 0x12345678);
2327
2328         style = gtk_widget_get_style(vf->listview);
2329         if (id && id->selected & SELECTION_SELECTED)
2330                 {
2331                 memcpy(&color_fg, &style->text[GTK_STATE_SELECTED], sizeof(color_fg));
2332                 memcpy(&color_bg, &style->base[GTK_STATE_SELECTED], sizeof(color_bg));
2333                 }
2334         else
2335                 {
2336                 memcpy(&color_fg, &style->text[GTK_STATE_NORMAL], sizeof(color_fg));
2337                 memcpy(&color_bg, &style->base[GTK_STATE_NORMAL], sizeof(color_bg));
2338                 }
2339
2340         if (id && id->selected & SELECTION_PRELIGHT)
2341                 {
2342 #if 0
2343                 shift_color(&color_fg, -1, 0);
2344 #endif
2345                 shift_color(&color_bg, -1, 0);
2346                 }
2347
2348         if (GQV_IS_CELL_RENDERER_ICON(cell))
2349                 {
2350                 if (id)
2351                         {
2352                         gchar *name_sidecars;
2353                         gchar *sidecars = NULL;
2354                         gchar *link = islink(id->fd->path) ? GQ_LINK_STR : "";
2355
2356                         if (id->fd->sidecar_files)
2357                                 {
2358                                 sidecars = file_data_sc_list_to_string(id->fd);
2359                                 name_sidecars = g_strdup_printf("%s%s %s", link, id->fd->name, sidecars);
2360                                 }
2361                         else
2362                                 {
2363                                 name_sidecars = g_strdup_printf("%s%s", link, id->fd->name);
2364                                 }
2365
2366                         g_object_set(cell,      "pixbuf", id->fd->thumb_pixbuf,
2367                                                 "text", name_sidecars,
2368                                                 "marks", file_data_get_marks(id->fd),
2369                                                 "show_marks", vf->marks_enabled,
2370                                                 "cell-background-gdk", &color_bg,
2371                                                 "cell-background-set", TRUE,
2372                                                 "foreground-gdk", &color_fg,
2373                                                 "foreground-set", TRUE,
2374                                                 "has-focus", (VFICON(vf)->focus_id == id), NULL);
2375                         g_free(sidecars);
2376                         g_free(name_sidecars);
2377                         }
2378                 else
2379                         {
2380                         g_object_set(cell,      "pixbuf", NULL,
2381                                                 "text", NULL,
2382                                                 "show_marks", FALSE,
2383                                                 "cell-background-set", FALSE,
2384                                                 "foreground-set", FALSE,
2385                                                 "has-focus", FALSE, NULL);
2386                         }
2387                 }
2388 }
2389
2390 static void vficon_append_column(ViewFile *vf, gint n)
2391 {
2392         ColumnData *cd;
2393         GtkTreeViewColumn *column;
2394         GtkCellRenderer *renderer;
2395
2396         column = gtk_tree_view_column_new();
2397         gtk_tree_view_column_set_min_width(column, 0);
2398
2399         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2400         gtk_tree_view_column_set_alignment(column, 0.5);
2401
2402         renderer = gqv_cell_renderer_icon_new();
2403         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2404         g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
2405                                          "ypad", THUMB_BORDER_PADDING,
2406                                          "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
2407
2408         g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
2409         g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
2410
2411         cd = g_new0(ColumnData, 1);
2412         cd->vf = vf;
2413         cd->number = n;
2414         gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
2415
2416         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2417         
2418         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
2419 }
2420
2421 /*
2422  *-----------------------------------------------------------------------------
2423  * base
2424  *-----------------------------------------------------------------------------
2425  */
2426
2427 gint vficon_set_fd(ViewFile *vf, FileData *dir_fd)
2428 {
2429         gint ret;
2430
2431         if (!dir_fd) return FALSE;
2432         if (vf->dir_fd == dir_fd) return TRUE;
2433
2434         file_data_unref(vf->dir_fd);
2435         vf->dir_fd = file_data_ref(dir_fd);
2436
2437         g_list_free(VFICON(vf)->selection);
2438         VFICON(vf)->selection = NULL;
2439
2440         iconlist_free(vf->list);
2441         vf->list = NULL;
2442
2443         /* NOTE: populate will clear the store for us */
2444         ret = vficon_refresh_real(vf, FALSE);
2445
2446         VFICON(vf)->focus_id = NULL;
2447         vficon_move_focus(vf, 0, 0, FALSE);
2448
2449         return ret;
2450 }
2451
2452 void vficon_destroy_cb(GtkWidget *widget, gpointer data)
2453 {
2454         ViewFile *vf = data;
2455
2456         vf_refresh_idle_cancel(vf);
2457         
2458         file_data_unregister_notify_func(vf_notify_cb, vf);
2459
2460         tip_unschedule(vf);
2461
2462         vficon_thumb_cleanup(vf);
2463
2464         iconlist_free(vf->list);
2465         g_list_free(VFICON(vf)->selection);
2466 }
2467
2468 ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
2469 {
2470         GtkListStore *store;
2471         GtkTreeSelection *selection;
2472         gint i;
2473
2474         vf->info = g_new0(ViewFileInfoIcon, 1);
2475
2476         VFICON(vf)->selection = NULL;
2477         VFICON(vf)->prev_selection = NULL;
2478
2479         VFICON(vf)->tip_window = NULL;
2480         VFICON(vf)->tip_delay_id = -1;
2481
2482         VFICON(vf)->focus_row = 0;
2483         VFICON(vf)->focus_column = 0;
2484         VFICON(vf)->focus_id = NULL;
2485
2486         VFICON(vf)->show_text = options->show_icon_names;
2487
2488         store = gtk_list_store_new(1, G_TYPE_POINTER);
2489         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2490         g_object_unref(store);
2491
2492         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2493         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
2494
2495         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2496         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2497
2498         for (i = 0; i < VFICON_MAX_COLUMNS; i++)
2499                 {
2500                 vficon_append_column(vf, i);
2501                 }
2502
2503         /* zero width column to hide tree view focus, we draw it ourselves */
2504         vficon_append_column(vf, i);
2505         /* end column to fill white space */
2506         vficon_append_column(vf, i);
2507
2508         g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
2509                          G_CALLBACK(vficon_sized_cb), vf);
2510
2511         gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
2512                               GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
2513
2514         g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
2515                          G_CALLBACK(vficon_motion_cb), vf);
2516         g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
2517                          G_CALLBACK(vficon_leave_cb), vf);
2518
2519         /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
2520         vficon_populate_at_new_size(vf, 1, 1, FALSE);
2521
2522         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2523
2524         return vf;
2525 }
2526
2527 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */