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