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