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