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