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