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