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