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