eea06b2fba47c8c5b96ba799f3bddae4ea9772ab
[geeqie.git] / src / view_file / view_file_icon.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "view_file_icon.h"
24
25 #include "bar.h"
26 #include "cellrenderericon.h"
27 #include "collect.h"
28 #include "collect-io.h"
29 #include "collect-table.h"
30 #include "dnd.h"
31 #include "editors.h"
32 #include "img-view.h"
33 #include "filedata.h"
34 #include "layout.h"
35 #include "layout_image.h"
36 #include "menu.h"
37 #include "metadata.h"
38 #include "thumb.h"
39 #include "utilops.h"
40 #include "ui_fileops.h"
41 #include "ui_menu.h"
42 #include "ui_tree_edit.h"
43 #include "uri_utils.h"
44 #include "view_file.h"
45
46 #include <gdk/gdkkeysyms.h> /* for keyboard values */
47
48
49 /* between these, the icon width is increased by thumb_max_width / 2 */
50 #define THUMB_MIN_ICON_WIDTH 128
51 #define THUMB_MAX_ICON_WIDTH 160
52 #define THUMB_MIN_ICON_WIDTH_WITH_MARKS TOGGLE_SPACING * FILEDATA_MARKS_SIZE
53
54 #define VFICON_MAX_COLUMNS 32
55 #define THUMB_BORDER_PADDING 2
56
57 #define VFICON_TIP_DELAY 500
58
59 enum {
60         FILE_COLUMN_POINTER = 0,
61         FILE_COLUMN_COUNT
62 };
63
64 static void vficon_toggle_filenames(ViewFile *vf);
65 static void vficon_selection_remove(ViewFile *vf, FileData *id, SelectionType mask, GtkTreeIter *iter);
66 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative);
67 static void vficon_set_focus(ViewFile *vf, FileData *fd);
68 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force);
69
70
71 /*
72  *-----------------------------------------------------------------------------
73  * pop-up menu
74  *-----------------------------------------------------------------------------
75  */
76
77 GList *vficon_selection_get_one(ViewFile *vf, FileData *fd)
78 {
79         return g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
80 }
81
82 GList *vficon_pop_menu_file_list(ViewFile *vf)
83 {
84         if (!VFICON(vf)->click_fd) return NULL;
85
86         if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
87                 {
88                 return vf_selection_get_list(vf);
89                 }
90
91         return vficon_selection_get_one(vf, VFICON(vf)->click_fd);
92 }
93
94 void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
95 {
96         ViewFile *vf = data;
97
98         if (!VFICON(vf)->click_fd) return;
99
100         if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
101                 {
102                 GList *list;
103
104                 list = vf_selection_get_list(vf);
105                 view_window_new_from_list(list);
106                 filelist_free(list);
107                 }
108         else
109                 {
110                 view_window_new(VFICON(vf)->click_fd);
111                 }
112 }
113
114 void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
115 {
116         ViewFile *vf = data;
117
118         file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview);
119 }
120
121 void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
122 {
123         ViewFile *vf = data;
124
125         vficon_toggle_filenames(vf);
126 }
127
128 void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
129 {
130         ViewFile *vf = data;
131
132         vf_refresh(vf);
133 }
134
135 void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
136 {
137         ViewFile *vf = data;
138         vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
139         VFICON(vf)->click_fd = NULL;
140         vf->popup = NULL;
141 }
142
143 /*
144  *-------------------------------------------------------------------
145  * signals
146  *-------------------------------------------------------------------
147  */
148
149 static void vficon_send_layout_select(ViewFile *vf, FileData *fd)
150 {
151         FileData *read_ahead_fd = NULL;
152         FileData *sel_fd;
153         FileData *cur_fd;
154
155         if (!vf->layout || !fd) return;
156
157         sel_fd = fd;
158
159         cur_fd = layout_image_get_fd(vf->layout);
160         if (sel_fd == cur_fd) return; /* no change */
161
162         if (options->image.enable_read_ahead)
163                 {
164                 gint row;
165
166                 row = g_list_index(vf->list, fd);
167                 if (row > vficon_index_by_fd(vf, cur_fd) &&
168                     (guint) (row + 1) < vf_count(vf, NULL))
169                         {
170                         read_ahead_fd = vf_index_get_data(vf, row + 1);
171                         }
172                 else if (row > 0)
173                         {
174                         read_ahead_fd = vf_index_get_data(vf, row - 1);
175                         }
176                 }
177
178         layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
179 }
180
181 static void vficon_toggle_filenames(ViewFile *vf)
182 {
183         GtkAllocation allocation;
184         VFICON(vf)->show_text = !VFICON(vf)->show_text;
185         options->show_icon_names = VFICON(vf)->show_text;
186
187         gtk_widget_get_allocation(vf->listview, &allocation);
188         vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
189 }
190
191 static gint vficon_get_icon_width(ViewFile *vf)
192 {
193         gint width;
194
195         if (!VFICON(vf)->show_text && !vf->marks_enabled ) return options->thumbnails.max_width;
196
197         width = options->thumbnails.max_width + options->thumbnails.max_width / 2;
198         if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
199         if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width;
200         if (vf->marks_enabled && width < THUMB_MIN_ICON_WIDTH_WITH_MARKS) width = THUMB_MIN_ICON_WIDTH_WITH_MARKS;
201
202         return width;
203 }
204
205 /*
206  *-------------------------------------------------------------------
207  * misc utils
208  *-------------------------------------------------------------------
209  */
210
211 static gboolean vficon_find_position(ViewFile *vf, FileData *fd, gint *row, gint *col)
212 {
213         gint n;
214
215         n = g_list_index(vf->list, fd);
216
217         if (n < 0) return FALSE;
218
219         *row = n / VFICON(vf)->columns;
220         *col = n - (*row * VFICON(vf)->columns);
221
222         return TRUE;
223 }
224
225 static gboolean vficon_find_iter(ViewFile *vf, FileData *fd, GtkTreeIter *iter, gint *column)
226 {
227         GtkTreeModel *store;
228         gint row, col;
229
230         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
231         if (!vficon_find_position(vf, fd, &row, &col)) return FALSE;
232         if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
233         if (column) *column = col;
234
235         return TRUE;
236 }
237
238 static FileData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
239 {
240         GtkTreeModel *store;
241         GtkTreeIter p;
242
243         if (row < 0 || col < 0) return NULL;
244
245         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
246         if (gtk_tree_model_iter_nth_child(store, &p, NULL, row))
247                 {
248                 GList *list;
249
250                 gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1);
251                 if (!list) return NULL;
252
253                 if (iter) *iter = p;
254
255                 return g_list_nth_data(list, col);
256                 }
257
258         return NULL;
259 }
260
261 static FileData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
262 {
263         GtkTreePath *tpath;
264         GtkTreeViewColumn *column;
265
266         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
267                                           &tpath, &column, NULL, NULL))
268                 {
269                 GtkTreeModel *store;
270                 GtkTreeIter row;
271                 GList *list;
272                 gint n;
273
274                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
275                 gtk_tree_model_get_iter(store, &row, tpath);
276                 gtk_tree_path_free(tpath);
277
278                 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
279
280                 n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number"));
281                 if (list)
282                         {
283                         if (iter) *iter = row;
284                         return g_list_nth_data(list, n);
285                         }
286                 }
287
288         return NULL;
289 }
290
291 static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
292 {
293         ViewFile *vf = data;
294         GtkTreeModel *store;
295         GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
296         GtkTreeIter row;
297         gint column;
298         GList *list;
299         guint toggled_mark;
300         FileData *fd;
301
302         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
303         if (!path || !gtk_tree_model_get_iter(store, &row, path)) return;
304
305         gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
306
307         column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number"));
308         g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL);
309
310         fd = g_list_nth_data(list, column);
311         if (fd)
312                 {
313                 file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark));
314                 }
315 }
316
317
318 /*
319  *-------------------------------------------------------------------
320  * tooltip type window
321  *-------------------------------------------------------------------
322  */
323
324 static void tip_show(ViewFile *vf)
325 {
326         GtkWidget *label;
327         gint x, y;
328 #if GTK_CHECK_VERSION(3,0,0)
329         GdkDisplay *display;
330         GdkDeviceManager *device_manager;
331         GdkDevice *device;
332 #endif
333
334         if (VFICON(vf)->tip_window) return;
335
336 #if GTK_CHECK_VERSION(3,0,0)
337         device_manager = gdk_display_get_device_manager(gdk_window_get_display(
338                                                 gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview))));
339         device = gdk_device_manager_get_client_pointer(device_manager);
340         gdk_window_get_device_position(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)),
341                                                 device, &x, &y, NULL);
342 #else
343         gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL);
344 #endif
345
346         VFICON(vf)->tip_fd = vficon_find_data_by_coord(vf, x, y, NULL);
347         if (!VFICON(vf)->tip_fd) return;
348
349         VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
350         gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE);
351         gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2);
352
353         label = gtk_label_new(VFICON(vf)->tip_fd->name);
354
355         g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label);
356         gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label);
357         gtk_widget_show(label);
358
359 #if GTK_CHECK_VERSION(3,0,0)
360         display = gdk_display_get_default();
361         device_manager = gdk_display_get_device_manager(display);
362         device = gdk_device_manager_get_client_pointer(device_manager);
363         gdk_device_get_position(device, NULL, &x, &y);
364 #else
365         gdk_window_get_pointer(NULL, &x, &y, NULL);
366 #endif
367
368         if (!gtk_widget_get_realized(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window);
369         gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
370         gtk_widget_show(VFICON(vf)->tip_window);
371 }
372
373 static void tip_hide(ViewFile *vf)
374 {
375         if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window);
376         VFICON(vf)->tip_window = NULL;
377 }
378
379 static gboolean tip_schedule_cb(gpointer data)
380 {
381         ViewFile *vf = data;
382         GtkWidget *window;
383
384         if (!VFICON(vf)->tip_delay_id) return FALSE;
385
386         window = gtk_widget_get_toplevel(vf->listview);
387
388         if (gtk_widget_get_sensitive(window) &&
389             gtk_window_has_toplevel_focus(GTK_WINDOW(window)))
390                 {
391                 tip_show(vf);
392                 }
393
394         VFICON(vf)->tip_delay_id = 0;
395         return FALSE;
396 }
397
398 static void tip_schedule(ViewFile *vf)
399 {
400         tip_hide(vf);
401
402         if (VFICON(vf)->tip_delay_id)
403                 {
404                 g_source_remove(VFICON(vf)->tip_delay_id);
405                 VFICON(vf)->tip_delay_id = 0;
406                 }
407
408         if (!VFICON(vf)->show_text)
409                 {
410                 VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf);
411                 }
412 }
413
414 static void tip_unschedule(ViewFile *vf)
415 {
416         tip_hide(vf);
417
418         if (VFICON(vf)->tip_delay_id)
419                 {
420                 g_source_remove(VFICON(vf)->tip_delay_id);
421                 VFICON(vf)->tip_delay_id = 0;
422                 }
423 }
424
425 static void tip_update(ViewFile *vf, FileData *fd)
426 {
427 #if GTK_CHECK_VERSION(3,0,0)
428         GdkDisplay *display = gdk_display_get_default();
429         GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
430         GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager);
431 #endif
432
433         if (VFICON(vf)->tip_window)
434                 {
435                 gint x, y;
436
437 #if GTK_CHECK_VERSION(3,0,0)
438                 gdk_device_get_position(device, NULL, &x, &y);
439 #else
440                 gdk_window_get_pointer(NULL, &x, &y, NULL);
441 #endif
442                 gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
443
444                 if (fd != VFICON(vf)->tip_fd)
445                         {
446                         GtkWidget *label;
447
448                         VFICON(vf)->tip_fd = fd;
449
450                         if (!VFICON(vf)->tip_fd)
451                                 {
452                                 tip_hide(vf);
453                                 tip_schedule(vf);
454                                 return;
455                                 }
456
457                         label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label");
458                         gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_fd->name);
459                         }
460                 }
461         else
462                 {
463                 tip_schedule(vf);
464                 }
465 }
466
467 /*
468  *-------------------------------------------------------------------
469  * dnd
470  *-------------------------------------------------------------------
471  */
472
473 static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
474                            GtkSelectionData *selection_data, guint info,
475                            guint time, gpointer data)
476 {
477         ViewFile *vf = data;
478         GList *list = NULL;
479
480         if (!VFICON(vf)->click_fd) return;
481
482         if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
483                 {
484                 list = vf_selection_get_list(vf);
485                 }
486         else
487                 {
488                 list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_fd));
489                 }
490
491         if (!list) return;
492         uri_selection_data_set_uris_from_filelist(selection_data, list);
493         filelist_free(list);
494 }
495
496 static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
497                                       int x, int y, GtkSelectionData *selection,
498                                       guint info, guint time, gpointer data)
499 {
500         ViewFile *vf = data;
501
502         if (info == TARGET_TEXT_PLAIN) {
503                 FileData *fd = vficon_find_data_by_coord(vf, x, y, NULL);
504
505                 if (fd) {
506                         /* Add keywords to file */
507                         gchar *str = (gchar *) gtk_selection_data_get_text(selection);
508                         GList *kw_list = string_to_keywords_list(str);
509
510                         metadata_append_list(fd, KEYWORD_KEY, kw_list);
511                         string_list_free(kw_list);
512                         g_free(str);
513                 }
514         }
515 }
516
517 static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
518 {
519         ViewFile *vf = data;
520
521         tip_unschedule(vf);
522
523         if (VFICON(vf)->click_fd && VFICON(vf)->click_fd->thumb_pixbuf)
524                 {
525                 gint items;
526
527                 if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
528                         items = g_list_length(VFICON(vf)->selection);
529                 else
530                         items = 1;
531
532                 dnd_set_drag_icon(widget, context, VFICON(vf)->click_fd->thumb_pixbuf, items);
533                 }
534 }
535
536 static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
537 {
538         ViewFile *vf = data;
539
540         vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
541
542         if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
543                 {
544                 vf_refresh(vf);
545                 }
546
547         tip_unschedule(vf);
548 }
549
550 void vficon_dnd_init(ViewFile *vf)
551 {
552         gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
553                             dnd_file_drag_types, dnd_file_drag_types_count,
554                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
555         gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
556                             dnd_file_drag_types, dnd_file_drag_types_count,
557                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
558
559         g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
560                          G_CALLBACK(vficon_dnd_get), vf);
561         g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
562                          G_CALLBACK(vficon_dnd_begin), vf);
563         g_signal_connect(G_OBJECT(vf->listview), "drag_end",
564                          G_CALLBACK(vficon_dnd_end), vf);
565         g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
566                          G_CALLBACK(vficon_drag_data_received), vf);
567 }
568
569 /*
570  *-------------------------------------------------------------------
571  * cell updates
572  *-------------------------------------------------------------------
573  */
574
575 static void vficon_selection_set(ViewFile *vf, FileData *fd, SelectionType value, GtkTreeIter *iter)
576 {
577         GtkTreeModel *store;
578         GList *list;
579
580         if (!fd) return;
581
582         if (fd->selected == value) return;
583         fd->selected = value;
584
585         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
586         if (iter)
587                 {
588                 gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
589                 if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1);
590                 }
591         else
592                 {
593                 GtkTreeIter row;
594
595                 if (vficon_find_iter(vf, fd, &row, NULL))
596                         {
597                         gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
598                         if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
599                         }
600                 }
601 }
602
603 static void vficon_selection_add(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
604 {
605         if (!fd) return;
606
607         vficon_selection_set(vf, fd, fd->selected | mask, iter);
608 }
609
610 static void vficon_selection_remove(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
611 {
612         if (!fd) return;
613
614         vficon_selection_set(vf, fd, fd->selected & ~mask, iter);
615 }
616
617 void vficon_marks_set(ViewFile *vf, gint enable)
618 {
619         GtkAllocation allocation;
620         gtk_widget_get_allocation(vf->listview, &allocation);
621         vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
622 }
623
624 /*
625  *-------------------------------------------------------------------
626  * selections
627  *-------------------------------------------------------------------
628  */
629
630 static void vficon_verify_selections(ViewFile *vf)
631 {
632         GList *work;
633
634         work = VFICON(vf)->selection;
635         while (work)
636                 {
637                 FileData *fd = work->data;
638                 work = work->next;
639
640                 if (vficon_index_by_fd(vf, fd) >= 0) continue;
641
642                 VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
643                 }
644 }
645
646 void vficon_select_all(ViewFile *vf)
647 {
648         GList *work;
649
650         g_list_free(VFICON(vf)->selection);
651         VFICON(vf)->selection = NULL;
652
653         work = vf->list;
654         while (work)
655                 {
656                 FileData *fd = work->data;
657                 work = work->next;
658
659                 VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
660                 vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
661                 }
662
663         vf_send_update(vf);
664 }
665
666 void vficon_select_none(ViewFile *vf)
667 {
668         GList *work;
669
670         work = VFICON(vf)->selection;
671         while (work)
672                 {
673                 FileData *fd = work->data;
674                 work = work->next;
675
676                 vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
677                 }
678
679         g_list_free(VFICON(vf)->selection);
680         VFICON(vf)->selection = NULL;
681
682         vf_send_update(vf);
683 }
684
685 void vficon_select_invert(ViewFile *vf)
686 {
687         GList *work;
688
689         work = vf->list;
690         while (work)
691                 {
692                 FileData *fd = work->data;
693                 work = work->next;
694
695                 if (fd->selected & SELECTION_SELECTED)
696                         {
697                         VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
698                         vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
699                         }
700                 else
701                         {
702                         VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
703                         vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
704                         }
705                 }
706
707         vf_send_update(vf);
708 }
709
710 static void vficon_select(ViewFile *vf, FileData *fd)
711 {
712         VFICON(vf)->prev_selection = fd;
713
714         if (!fd || fd->selected & SELECTION_SELECTED) return;
715
716         VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
717         vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
718
719         vf_send_update(vf);
720 }
721
722 static void vficon_unselect(ViewFile *vf, FileData *fd)
723 {
724         VFICON(vf)->prev_selection = fd;
725
726         if (!fd || !(fd->selected & SELECTION_SELECTED) ) return;
727
728         VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
729         vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
730
731         vf_send_update(vf);
732 }
733
734 static void vficon_select_util(ViewFile *vf, FileData *fd, gboolean select)
735 {
736         if (select)
737                 {
738                 vficon_select(vf, fd);
739                 }
740         else
741                 {
742                 vficon_unselect(vf, fd);
743                 }
744 }
745
746 static void vficon_select_region_util(ViewFile *vf, FileData *start, FileData *end, gboolean select)
747 {
748         gint row1, col1;
749         gint row2, col2;
750         gint t;
751         gint i, j;
752
753         if (!vficon_find_position(vf, start, &row1, &col1) ||
754             !vficon_find_position(vf, end, &row2, &col2) ) return;
755
756         VFICON(vf)->prev_selection = end;
757
758         if (!options->collections.rectangular_selection)
759                 {
760                 GList *work;
761
762                 if (g_list_index(vf->list, start) > g_list_index(vf->list, end))
763                         {
764                         FileData *tmp = start;
765                         start = end;
766                         end = tmp;
767                         }
768
769                 work = g_list_find(vf->list, start);
770                 while (work)
771                         {
772                         FileData *fd = work->data;
773                         vficon_select_util(vf, fd, select);
774
775                         if (work->data != end)
776                                 work = work->next;
777                         else
778                                 work = NULL;
779                         }
780                 return;
781                 }
782
783         // rectangular_selection==true.
784         if (row2 < row1)
785                 {
786                 t = row1;
787                 row1 = row2;
788                 row2 = t;
789                 }
790         if (col2 < col1)
791                 {
792                 t = col1;
793                 col1 = col2;
794                 col2 = t;
795                 }
796
797         DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2);
798
799         for (i = row1; i <= row2; i++)
800                 {
801                 for (j = col1; j <= col2; j++)
802                         {
803                         FileData *fd = vficon_find_data(vf, i, j, NULL);
804                         if (fd) vficon_select_util(vf, fd, select);
805                         }
806                 }
807 }
808
809 gboolean vficon_index_is_selected(ViewFile *vf, gint row)
810 {
811         FileData *fd = g_list_nth_data(vf->list, row);
812
813         if (!fd) return FALSE;
814
815         return (fd->selected & SELECTION_SELECTED);
816 }
817
818 guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
819 {
820         if (bytes)
821                 {
822                 gint64 b = 0;
823                 GList *work;
824
825                 work = VFICON(vf)->selection;
826                 while (work)
827                         {
828                         FileData *fd = work->data;
829                         g_assert(fd->magick == FD_MAGICK);
830                         b += fd->size;
831
832                         work = work->next;
833                         }
834
835                 *bytes = b;
836                 }
837
838         return g_list_length(VFICON(vf)->selection);
839 }
840
841 GList *vficon_selection_get_list(ViewFile *vf)
842 {
843         GList *list = NULL;
844         GList *work, *work2;
845
846         work = VFICON(vf)->selection;
847         while (work)
848                 {
849                 FileData *fd = work->data;
850                 g_assert(fd->magick == FD_MAGICK);
851
852                 list = g_list_prepend(list, file_data_ref(fd));
853
854                 work2 = fd->sidecar_files;
855                 while (work2)
856                         {
857                         fd = work2->data;
858                         list = g_list_prepend(list, file_data_ref(fd));
859                         work2 = work2->next;
860                         }
861
862                 work = work->next;
863                 }
864
865         list = g_list_reverse(list);
866
867         return list;
868 }
869
870 GList *vficon_selection_get_list_by_index(ViewFile *vf)
871 {
872         GList *list = NULL;
873         GList *work;
874
875         work = VFICON(vf)->selection;
876         while (work)
877                 {
878                 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data)));
879                 work = work->next;
880                 }
881
882         return g_list_reverse(list);
883 }
884
885 void vficon_select_by_fd(ViewFile *vf, FileData *fd)
886 {
887         if (!fd) return;
888         if (!g_list_find(vf->list, fd)) return;
889
890         if (!(fd->selected & SELECTION_SELECTED))
891                 {
892                 vf_select_none(vf);
893                 vficon_select(vf, fd);
894                 }
895
896         vficon_set_focus(vf, fd);
897 }
898
899 void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
900 {
901         GList *work;
902         gint n = mark - 1;
903
904         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
905
906         work = vf->list;
907         while (work)
908                 {
909                 FileData *fd = work->data;
910                 gboolean mark_val, selected;
911
912                 g_assert(fd->magick == FD_MAGICK);
913
914                 mark_val = file_data_get_mark(fd, n);
915                 selected = fd->selected & SELECTION_SELECTED;
916
917                 switch (mode)
918                         {
919                         case MTS_MODE_SET: selected = mark_val;
920                                 break;
921                         case MTS_MODE_OR: selected = mark_val || selected;
922                                 break;
923                         case MTS_MODE_AND: selected = mark_val && selected;
924                                 break;
925                         case MTS_MODE_MINUS: selected = !mark_val && selected;
926                                 break;
927                         }
928
929                 vficon_select_util(vf, fd, selected);
930
931                 work = work->next;
932                 }
933 }
934
935 void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
936 {
937         GList *slist;
938         GList *work;
939         gint n = mark -1;
940
941         g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
942
943         slist = vf_selection_get_list(vf);
944         work = slist;
945         while (work)
946                 {
947                 FileData *fd = work->data;
948
949                 switch (mode)
950                         {
951                         case STM_MODE_SET: file_data_set_mark(fd, n, 1);
952                                 break;
953                         case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
954                                 break;
955                         case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
956                                 break;
957                         }
958                 work = work->next;
959                 }
960         filelist_free(slist);
961 }
962
963 static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
964 {
965         GList *work;
966         FileData *fd = NULL;
967
968         if (sel_fd->parent) sel_fd = sel_fd->parent;
969         work = vf->list;
970
971         while (work)
972                 {
973                 gint match;
974
975                 fd = work->data;
976                 work = work->next;
977
978                 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
979
980                 if (match >= 0) break;
981                 }
982
983         if (fd)
984                 {
985                 vficon_select(vf, fd);
986                 vficon_send_layout_select(vf, fd);
987                 }
988 }
989
990
991 /*
992  *-------------------------------------------------------------------
993  * focus
994  *-------------------------------------------------------------------
995  */
996
997 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative)
998 {
999         gint new_row;
1000         gint new_col;
1001
1002         if (relative)
1003                 {
1004                 new_row = VFICON(vf)->focus_row;
1005                 new_col = VFICON(vf)->focus_column;
1006
1007                 new_row += row;
1008                 if (new_row < 0) new_row = 0;
1009                 if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1;
1010
1011                 while (col != 0)
1012                         {
1013                         if (col < 0)
1014                                 {
1015                                 new_col--;
1016                                 col++;
1017                                 }
1018                         else
1019                                 {
1020                                 new_col++;
1021                                 col--;
1022                                 }
1023
1024                         if (new_col < 0)
1025                                 {
1026                                 if (new_row > 0)
1027                                         {
1028                                         new_row--;
1029                                         new_col = VFICON(vf)->columns - 1;
1030                                         }
1031                                 else
1032                                         {
1033                                         new_col = 0;
1034                                         }
1035                                 }
1036                         if (new_col >= VFICON(vf)->columns)
1037                                 {
1038                                 if (new_row < VFICON(vf)->rows - 1)
1039                                         {
1040                                         new_row++;
1041                                         new_col = 0;
1042                                         }
1043                                 else
1044                                         {
1045                                         new_col = VFICON(vf)->columns - 1;
1046                                         }
1047                                 }
1048                         }
1049                 }
1050         else
1051                 {
1052                 new_row = row;
1053                 new_col = col;
1054
1055                 if (new_row >= VFICON(vf)->rows)
1056                         {
1057                         if (VFICON(vf)->rows > 0)
1058                                 new_row = VFICON(vf)->rows - 1;
1059                         else
1060                                 new_row = 0;
1061                         new_col = VFICON(vf)->columns - 1;
1062                         }
1063                 if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1;
1064                 }
1065
1066         if (new_row == VFICON(vf)->rows - 1)
1067                 {
1068                 gint l;
1069
1070                 /* if we moved beyond the last image, go to the last image */
1071
1072                 l = g_list_length(vf->list);
1073                 if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns;
1074                 if (new_col >= l) new_col = l - 1;
1075                 }
1076
1077         vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
1078 }
1079
1080 static void vficon_set_focus(ViewFile *vf, FileData *fd)
1081 {
1082         GtkTreeIter iter;
1083         gint row, col;
1084
1085         if (g_list_find(vf->list, VFICON(vf)->focus_fd))
1086                 {
1087                 if (fd == VFICON(vf)->focus_fd)
1088                         {
1089                         /* ensure focus row col are correct */
1090                         vficon_find_position(vf, VFICON(vf)->focus_fd, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
1091 #if GTK_CHECK_VERSION(3,0,0)
1092 /* FIXME: Refer to issue #467 on Github. The thumbnail position is not
1093  * preserved when the icon view is refreshed. Caused by an unknown call from
1094  * the idle loop. This patch hides the problem.
1095  */
1096                         if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
1097                                 {
1098                                 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1099                                 }
1100 #endif
1101                         return;
1102                         }
1103                 vficon_selection_remove(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
1104                 }
1105
1106         if (!vficon_find_position(vf, fd, &row, &col))
1107                 {
1108                 VFICON(vf)->focus_fd = NULL;
1109                 VFICON(vf)->focus_row = -1;
1110                 VFICON(vf)->focus_column = -1;
1111                 return;
1112                 }
1113
1114         VFICON(vf)->focus_fd = fd;
1115         VFICON(vf)->focus_row = row;
1116         VFICON(vf)->focus_column = col;
1117         vficon_selection_add(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
1118
1119         if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
1120                 {
1121                 GtkTreePath *tpath;
1122                 GtkTreeViewColumn *column;
1123                 GtkTreeModel *store;
1124
1125                 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1126
1127                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1128                 tpath = gtk_tree_model_get_path(store, &iter);
1129                 /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
1130                 column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS);
1131                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE);
1132                 gtk_tree_path_free(tpath);
1133                 }
1134 }
1135
1136 /* used to figure the page up/down distances */
1137 static gint page_height(ViewFile *vf)
1138 {
1139         GtkAdjustment *adj;
1140         gint page_size;
1141         gint row_height;
1142         gint ret;
1143
1144         adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview));
1145         page_size = (gint)gtk_adjustment_get_page_increment(adj);
1146
1147         row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2;
1148         if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3;
1149
1150         ret = page_size / row_height;
1151         if (ret < 1) ret = 1;
1152
1153         return ret;
1154 }
1155
1156 /*
1157  *-------------------------------------------------------------------
1158  * keyboard
1159  *-------------------------------------------------------------------
1160  */
1161
1162 static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1163 {
1164         ViewFile *vf = data;
1165         GtkTreeModel *store;
1166         GtkTreeIter iter;
1167         gint column;
1168         GtkTreePath *tpath;
1169         gint cw, ch;
1170
1171         if (!vficon_find_iter(vf, VFICON(vf)->click_fd, &iter, &column)) return;
1172         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1173         tpath = gtk_tree_model_get_path(store, &iter);
1174         tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
1175         gtk_tree_path_free(tpath);
1176         *y += ch;
1177         popup_menu_position_clamp(menu, x, y, 0);
1178 }
1179
1180 gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1181 {
1182         ViewFile *vf = data;
1183         gint focus_row = 0;
1184         gint focus_col = 0;
1185         FileData *fd;
1186         gboolean stop_signal;
1187
1188         stop_signal = TRUE;
1189         switch (event->keyval)
1190                 {
1191                 case GDK_KEY_Left: case GDK_KEY_KP_Left:
1192                         focus_col = -1;
1193                         break;
1194                 case GDK_KEY_Right: case GDK_KEY_KP_Right:
1195                         focus_col = 1;
1196                         break;
1197                 case GDK_KEY_Up: case GDK_KEY_KP_Up:
1198                         focus_row = -1;
1199                         break;
1200                 case GDK_KEY_Down: case GDK_KEY_KP_Down:
1201                         focus_row = 1;
1202                         break;
1203                 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
1204                         focus_row = -page_height(vf);
1205                         break;
1206                 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
1207                         focus_row = page_height(vf);
1208                         break;
1209                 case GDK_KEY_Home: case GDK_KEY_KP_Home:
1210                         focus_row = -VFICON(vf)->focus_row;
1211                         focus_col = -VFICON(vf)->focus_column;
1212                         break;
1213                 case GDK_KEY_End: case GDK_KEY_KP_End:
1214                         focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row;
1215                         focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
1216                         break;
1217                 case GDK_KEY_space:
1218                         fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1219                         if (fd)
1220                                 {
1221                                 VFICON(vf)->click_fd = fd;
1222                                 if (event->state & GDK_CONTROL_MASK)
1223                                         {
1224                                         gint selected;
1225
1226                                         selected = fd->selected & SELECTION_SELECTED;
1227                                         if (selected)
1228                                                 {
1229                                                 vficon_unselect(vf, fd);
1230                                                 }
1231                                         else
1232                                                 {
1233                                                 vficon_select(vf, fd);
1234                                                 vficon_send_layout_select(vf, fd);
1235                                                 }
1236                                         }
1237                                 else
1238                                         {
1239                                         vf_select_none(vf);
1240                                         vficon_select(vf, fd);
1241                                         vficon_send_layout_select(vf, fd);
1242                                         }
1243                                 }
1244                         break;
1245                 case GDK_KEY_Menu:
1246                         fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1247                         VFICON(vf)->click_fd = fd;
1248
1249                         vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
1250                         tip_unschedule(vf);
1251
1252                         vf->popup = vf_pop_menu(vf);
1253                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
1254                         break;
1255                 default:
1256                         stop_signal = FALSE;
1257                         break;
1258                 }
1259
1260         if (focus_row != 0 || focus_col != 0)
1261                 {
1262                 FileData *new_fd;
1263                 FileData *old_fd;
1264
1265                 old_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1266                 vficon_move_focus(vf, focus_row, focus_col, TRUE);
1267                 new_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
1268
1269                 if (new_fd != old_fd)
1270                         {
1271                         if (event->state & GDK_SHIFT_MASK)
1272                                 {
1273                                 if (!options->collections.rectangular_selection)
1274                                         {
1275                                         vficon_select_region_util(vf, old_fd, new_fd, FALSE);
1276                                         }
1277                                 else
1278                                         {
1279                                         vficon_select_region_util(vf, VFICON(vf)->click_fd, old_fd, FALSE);
1280                                         }
1281                                 vficon_select_region_util(vf, VFICON(vf)->click_fd, new_fd, TRUE);
1282                                 vficon_send_layout_select(vf, new_fd);
1283                                 }
1284                         else if (event->state & GDK_CONTROL_MASK)
1285                                 {
1286                                 VFICON(vf)->click_fd = new_fd;
1287                                 }
1288                         else
1289                                 {
1290                                 VFICON(vf)->click_fd = new_fd;
1291                                 vf_select_none(vf);
1292                                 vficon_select(vf, new_fd);
1293                                 vficon_send_layout_select(vf, new_fd);
1294                                 }
1295                         }
1296                 }
1297
1298         if (stop_signal)
1299                 {
1300                 tip_unschedule(vf);
1301                 }
1302
1303         return stop_signal;
1304 }
1305
1306 /*
1307  *-------------------------------------------------------------------
1308  * mouse
1309  *-------------------------------------------------------------------
1310  */
1311
1312 static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
1313 {
1314         ViewFile *vf = data;
1315         FileData *fd;
1316
1317         fd = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL);
1318         tip_update(vf, fd);
1319
1320         return FALSE;
1321 }
1322
1323 gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1324 {
1325         ViewFile *vf = data;
1326         GtkTreeIter iter;
1327         FileData *fd;
1328
1329         tip_unschedule(vf);
1330
1331         fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1332
1333         VFICON(vf)->click_fd = fd;
1334         vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
1335
1336         switch (bevent->button)
1337                 {
1338                 case MOUSE_BUTTON_LEFT:
1339                         if (!gtk_widget_has_focus(vf->listview))
1340                                 {
1341                                 gtk_widget_grab_focus(vf->listview);
1342                                 }
1343
1344                         if (bevent->type == GDK_2BUTTON_PRESS && vf->layout)
1345                                 {
1346                                 vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
1347                                 layout_image_full_screen_start(vf->layout);
1348                                 }
1349                         break;
1350                 case MOUSE_BUTTON_RIGHT:
1351                         vf->popup = vf_pop_menu(vf);
1352                         gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
1353                         break;
1354                 default:
1355                         break;
1356                 }
1357
1358         return FALSE;
1359 }
1360
1361 gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1362 {
1363         ViewFile *vf = data;
1364         GtkTreeIter iter;
1365         FileData *fd = NULL;
1366         gboolean was_selected;
1367
1368         tip_schedule(vf);
1369
1370         if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
1371                 {
1372                 fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
1373                 }
1374
1375         if (VFICON(vf)->click_fd)
1376                 {
1377                 vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
1378                 }
1379
1380         if (!fd || VFICON(vf)->click_fd != fd) return TRUE;
1381
1382         was_selected = !!(fd->selected & SELECTION_SELECTED);
1383
1384         switch (bevent->button)
1385                 {
1386                 case MOUSE_BUTTON_LEFT:
1387                         {
1388                         vficon_set_focus(vf, fd);
1389
1390                         if (bevent->state & GDK_CONTROL_MASK)
1391                                 {
1392                                 gboolean select;
1393
1394                                 select = !(fd->selected & SELECTION_SELECTED);
1395                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1396                                         {
1397                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, select);
1398                                         }
1399                                 else
1400                                         {
1401                                         vficon_select_util(vf, fd, select);
1402                                         }
1403                                 }
1404                         else
1405                                 {
1406                                 vf_select_none(vf);
1407
1408                                 if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
1409                                         {
1410                                         vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, TRUE);
1411                                         }
1412                                 else
1413                                         {
1414                                         vficon_select_util(vf, fd, TRUE);
1415                                         was_selected = FALSE;
1416                                         }
1417                                 }
1418                         }
1419                         break;
1420                 case MOUSE_BUTTON_MIDDLE:
1421                         {
1422                         vficon_select_util(vf, fd, !(fd->selected & SELECTION_SELECTED));
1423                         }
1424                         break;
1425                 default:
1426                         break;
1427                 }
1428
1429         if (!was_selected && (fd->selected & SELECTION_SELECTED))
1430                 {
1431                 vficon_send_layout_select(vf, fd);
1432                 }
1433
1434         return TRUE;
1435 }
1436
1437 static gboolean vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
1438 {
1439         ViewFile *vf = data;
1440
1441         tip_unschedule(vf);
1442         return FALSE;
1443 }
1444
1445 /*
1446  *-------------------------------------------------------------------
1447  * population
1448  *-------------------------------------------------------------------
1449  */
1450
1451 static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
1452 {
1453         GList *list;
1454
1455         gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
1456
1457         /* it seems that gtk_list_store_clear may call some callbacks
1458            that use the column. Set the pointer to NULL to be safe. */
1459         gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, NULL, -1);
1460         g_list_free(list);
1461
1462         return FALSE;
1463 }
1464
1465 static void vficon_clear_store(ViewFile *vf)
1466 {
1467         GtkTreeModel *store;
1468
1469         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1470         gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
1471
1472         gtk_list_store_clear(GTK_LIST_STORE(store));
1473 }
1474
1475 static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter)
1476 {
1477         GtkListStore *store;
1478         GList *list = NULL;
1479         gint i;
1480
1481         for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL);
1482
1483         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1484         gtk_list_store_append(store, iter);
1485         gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
1486
1487         return list;
1488 }
1489
1490 static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_position)
1491 {
1492         GtkTreeModel *store;
1493         GtkTreePath *tpath;
1494         GList *work;
1495         FileData *visible_fd = NULL;
1496         gint r, c;
1497         gboolean valid;
1498         GtkTreeIter iter;
1499
1500         vficon_verify_selections(vf);
1501
1502         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1503
1504         if (keep_position && gtk_widget_get_realized(vf->listview) &&
1505             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1506                 {
1507                 GtkTreeIter iter;
1508                 GList *list;
1509
1510                 gtk_tree_model_get_iter(store, &iter, tpath);
1511                 gtk_tree_path_free(tpath);
1512
1513                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1514                 if (list) visible_fd = list->data;
1515                 }
1516
1517
1518         if (resize)
1519                 {
1520                 gint i;
1521                 gint thumb_width;
1522
1523                 vficon_clear_store(vf);
1524
1525                 thumb_width = vficon_get_icon_width(vf);
1526
1527                 for (i = 0; i < VFICON_MAX_COLUMNS; i++)
1528                         {
1529                         GtkTreeViewColumn *column;
1530                         GtkCellRenderer *cell;
1531                         GList *list;
1532
1533                         column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i);
1534                         gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns));
1535                         gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
1536
1537                         list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1538                         cell = (list) ? list->data : NULL;
1539                         g_list_free(list);
1540
1541                         if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
1542                                 {
1543                                 g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
1544                                                              "fixed_height", options->thumbnails.max_height,
1545                                                              "show_text", VFICON(vf)->show_text,
1546                                                              "show_marks", vf->marks_enabled,
1547                                                              "num_marks", FILEDATA_MARKS_SIZE,
1548                                                              NULL);
1549                                 }
1550                         }
1551                 if (gtk_widget_get_realized(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1552                 }
1553
1554         r = -1;
1555         c = 0;
1556
1557         valid = gtk_tree_model_iter_children(store, &iter, NULL);
1558
1559         work = vf->list;
1560         while (work)
1561                 {
1562                 GList *list;
1563                 r++;
1564                 c = 0;
1565                 if (valid)
1566                         {
1567                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1568                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1569                         }
1570                 else
1571                         {
1572                         list = vficon_add_row(vf, &iter);
1573                         }
1574
1575                 while (list)
1576                         {
1577                         FileData *fd;
1578
1579                         if (work)
1580                                 {
1581                                 fd = work->data;
1582                                 work = work->next;
1583                                 c++;
1584                                 }
1585                         else
1586                                 {
1587                                 fd = NULL;
1588                                 }
1589
1590                         list->data = fd;
1591                         list = list->next;
1592                         }
1593                 if (valid) valid = gtk_tree_model_iter_next(store, &iter);
1594                 }
1595
1596         r++;
1597         while (valid)
1598                 {
1599                 GList *list;
1600
1601                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1602                 valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1603                 g_list_free(list);
1604                 }
1605
1606         VFICON(vf)->rows = r;
1607
1608         if (visible_fd &&
1609             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1610                 {
1611                 GtkTreeIter iter;
1612                 GList *list;
1613
1614                 gtk_tree_model_get_iter(store, &iter, tpath);
1615                 gtk_tree_path_free(tpath);
1616
1617                 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1618                 if (g_list_find(list, visible_fd) == NULL &&
1619                     vficon_find_iter(vf, visible_fd, &iter, NULL))
1620                         {
1621                         tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
1622                         }
1623                 }
1624
1625
1626         vf_send_update(vf);
1627         vf_thumb_update(vf);
1628 }
1629
1630 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force)
1631 {
1632         gint new_cols;
1633         gint thumb_width;
1634
1635         thumb_width = vficon_get_icon_width(vf);
1636
1637         new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
1638         if (new_cols < 1) new_cols = 1;
1639
1640         if (!force && new_cols == VFICON(vf)->columns) return;
1641
1642         VFICON(vf)->columns = new_cols;
1643
1644         vficon_populate(vf, TRUE, TRUE);
1645
1646         DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows);
1647 }
1648
1649 static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
1650 {
1651         ViewFile *vf = data;
1652
1653         vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE);
1654 }
1655
1656 /*
1657  *-----------------------------------------------------------------------------
1658  * misc
1659  *-----------------------------------------------------------------------------
1660  */
1661
1662 void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1663 {
1664         if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1665
1666         vf->sort_method = type;
1667         vf->sort_ascend = ascend;
1668
1669         if (!vf->list) return;
1670
1671         vf_refresh(vf);
1672 }
1673
1674 /*
1675  *-----------------------------------------------------------------------------
1676  * thumb updates
1677  *-----------------------------------------------------------------------------
1678  */
1679
1680 void vficon_thumb_progress_count(GList *list, gint *count, gint *done)
1681 {
1682         GList *work = list;
1683         while (work)
1684                 {
1685                 FileData *fd = work->data;
1686                 work = work->next;
1687
1688                 if (fd->thumb_pixbuf) (*done)++;
1689                 (*count)++;
1690                 }
1691 }
1692
1693 void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
1694 {
1695         GtkTreeModel *store;
1696         GtkTreeIter iter;
1697         GList *list;
1698
1699         if (!g_list_find(vf->list, fd)) return;
1700         if (!vficon_find_iter(vf, fd, &iter, NULL)) return;
1701
1702         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1703
1704         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1705         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1706 }
1707
1708 /* Returns the next fd without a loaded pixbuf, so the thumb-loader can load the pixbuf for it. */
1709 FileData *vficon_thumb_next_fd(ViewFile *vf)
1710 {
1711         GtkTreePath *tpath;
1712
1713         /* First see if there are visible files that don't have a loaded thumb... */
1714         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1715                 {
1716                 GtkTreeModel *store;
1717                 GtkTreeIter iter;
1718                 gboolean valid = TRUE;
1719
1720                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1721                 gtk_tree_model_get_iter(store, &iter, tpath);
1722                 gtk_tree_path_free(tpath);
1723                 tpath = NULL;
1724
1725                 while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1726                         {
1727                         GList *list;
1728                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1729
1730                         // TODO(xsdg): for loop here.
1731                         for (; list; list = list->next)
1732                                 {
1733                                 FileData *fd = list->data;
1734                                 if (fd && !fd->thumb_pixbuf) return fd;
1735                                 }
1736
1737                         valid = gtk_tree_model_iter_next(store, &iter);
1738                         }
1739                 }
1740
1741         /* Then iterate through the entire list to load all of them. */
1742         GList *work;
1743         for (work = vf->list; work; work = work->next)
1744                 {
1745                 FileData *fd = work->data;
1746
1747                 // Note: This implementation differs from view_file_list.c because sidecar files are not
1748                 // distinct list elements here, as they are in the list view.
1749                 if (!fd->thumb_pixbuf) return fd;
1750                 }
1751
1752         return NULL;
1753 }
1754
1755 /*
1756  *-----------------------------------------------------------------------------
1757  * row stuff
1758  *-----------------------------------------------------------------------------
1759  */
1760
1761 gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
1762 {
1763         gint p = 0;
1764         GList *work;
1765
1766         if (!in_fd) return -1;
1767
1768         work = vf->list;
1769         while (work)
1770                 {
1771                 FileData *fd = work->data;
1772                 if (fd == in_fd) return p;
1773                 work = work->next;
1774                 p++;
1775                 }
1776
1777         return -1;
1778 }
1779
1780 /*
1781  *-----------------------------------------------------------------------------
1782  *
1783  *-----------------------------------------------------------------------------
1784  */
1785
1786 static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
1787 {
1788         gboolean ret = TRUE;
1789         GList *work, *new_work;
1790         FileData *focus_fd;
1791         FileData *first_selected = NULL;
1792         GList *new_filelist = NULL;
1793         GList *new_fd_list = NULL;
1794
1795         focus_fd = VFICON(vf)->focus_fd;
1796
1797         if (vf->dir_fd)
1798                 {
1799                 ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
1800                 new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
1801                 }
1802
1803         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
1804         new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
1805
1806         if (VFICON(vf)->selection)
1807                 {
1808                 first_selected = VFICON(vf)->selection->data;
1809                 file_data_ref(first_selected);
1810                 g_list_free(VFICON(vf)->selection);
1811                 VFICON(vf)->selection = NULL;
1812                 }
1813
1814         /* iterate old list and new list, looking for differences */
1815         work = vf->list;
1816         new_work = new_filelist;
1817         while (work || new_work)
1818                 {
1819                 FileData *fd = NULL;
1820                 FileData *new_fd = NULL;
1821                 gint match;
1822
1823                 if (work && new_work)
1824                         {
1825                         fd = work->data;
1826                         new_fd = new_work->data;
1827
1828                         if (fd == new_fd)
1829                                 {
1830                                 /* not changed, go to next */
1831                                 work = work->next;
1832                                 new_work = new_work->next;
1833                                 if (fd->selected & SELECTION_SELECTED)
1834                                         {
1835                                         VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, fd);
1836                                         }
1837                                 continue;
1838                                 }
1839
1840                         match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
1841                         if (match == 0) g_warning("multiple fd for the same path");
1842                         }
1843                 else if (work)
1844                         {
1845                         /* old item was deleted */
1846                         fd = work->data;
1847                         match = -1;
1848                         }
1849                 else
1850                         {
1851                         /* new item was added */
1852                         new_fd = new_work->data;
1853                         match = 1;
1854                         }
1855
1856                 if (match < 0)
1857                         {
1858                         /* file no longer exists, delete from vf->list */
1859                         GList *to_delete = work;
1860                         work = work->next;
1861                         if (fd == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
1862                         if (fd == VFICON(vf)->click_fd) VFICON(vf)->click_fd = NULL;
1863                         file_data_unref(fd);
1864                         vf->list = g_list_delete_link(vf->list, to_delete);
1865                         }
1866                 else
1867                         {
1868                         /* new file, add to vf->list */
1869                         file_data_ref(new_fd);
1870                         new_fd->selected = SELECTION_NONE;
1871                         if (work)
1872                                 {
1873                                 vf->list = g_list_insert_before(vf->list, work, new_fd);
1874                                 }
1875                         else
1876                                 {
1877                                 /* it is faster to append all new entries together later */
1878                                 new_fd_list = g_list_prepend(new_fd_list, new_fd);
1879                                 }
1880
1881                         new_work = new_work->next;
1882                         }
1883                 }
1884
1885         if (new_fd_list)
1886                 {
1887                 vf->list = g_list_concat(vf->list, g_list_reverse(new_fd_list));
1888                 }
1889
1890         VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
1891
1892         filelist_free(new_filelist);
1893
1894         vficon_populate(vf, TRUE, keep_position);
1895
1896         if (first_selected && !VFICON(vf)->selection)
1897                 {
1898                 /* all selected files disappeared */
1899                 vficon_select_closest(vf, first_selected);
1900                 }
1901         file_data_unref(first_selected);
1902
1903         /* attempt to keep focus on same icon when refreshing */
1904         if (focus_fd && g_list_find(vf->list, focus_fd))
1905                 {
1906                 vficon_set_focus(vf, focus_fd);
1907                 }
1908
1909         return ret;
1910 }
1911
1912 gboolean vficon_refresh(ViewFile *vf)
1913 {
1914         return vficon_refresh_real(vf, TRUE);
1915 }
1916
1917 /*
1918  *-----------------------------------------------------------------------------
1919  * draw, etc.
1920  *-----------------------------------------------------------------------------
1921  */
1922
1923 typedef struct _ColumnData ColumnData;
1924 struct _ColumnData
1925 {
1926         ViewFile *vf;
1927         gint number;
1928 };
1929
1930 static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1931                                 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1932 {
1933         GList *list;
1934         FileData *fd;
1935         ColumnData *cd = data;
1936         ViewFile *vf = cd->vf;
1937
1938         if (!GQV_IS_CELL_RENDERER_ICON(cell)) return;
1939
1940         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
1941
1942         fd = g_list_nth_data(list, cd->number);
1943
1944         if (fd)
1945                 {
1946                 GdkColor color_fg;
1947                 GdkColor color_bg;
1948                 GtkStyle *style;
1949                 gchar *name_sidecars;
1950                 gchar *link;
1951                 GtkStateType state = GTK_STATE_NORMAL;
1952
1953                 g_assert(fd->magick == FD_MAGICK);
1954
1955                 link = islink(fd->path) ? GQ_LINK_STR : "";
1956                 if (fd->sidecar_files)
1957                         {
1958                         gchar *sidecars = file_data_sc_list_to_string(fd);
1959                         name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
1960                         g_free(sidecars);
1961                         }
1962                 else
1963                         {
1964                         gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
1965                         name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
1966                         }
1967
1968                 style = gtk_widget_get_style(vf->listview);
1969                 if (fd->selected & SELECTION_SELECTED)
1970                         {
1971                         state = GTK_STATE_SELECTED;
1972                         }
1973
1974                 memcpy(&color_fg, &style->text[state], sizeof(color_fg));
1975                 memcpy(&color_bg, &style->base[state], sizeof(color_bg));
1976
1977                 if (fd->selected & SELECTION_PRELIGHT)
1978                         {
1979                         shift_color(&color_bg, -1, 0);
1980                         }
1981
1982                 g_object_set(cell,      "pixbuf", fd->thumb_pixbuf,
1983                                         "text", name_sidecars,
1984                                         "marks", file_data_get_marks(fd),
1985                                         "show_marks", vf->marks_enabled,
1986                                         "cell-background-gdk", &color_bg,
1987                                         "cell-background-set", TRUE,
1988                                         "foreground-gdk", &color_fg,
1989                                         "foreground-set", TRUE,
1990                                         "has-focus", (VFICON(vf)->focus_fd == fd), NULL);
1991                 g_free(name_sidecars);
1992                 }
1993         else
1994                 {
1995                 g_object_set(cell,      "pixbuf", NULL,
1996                                         "text", NULL,
1997                                         "show_marks", FALSE,
1998                                         "cell-background-set", FALSE,
1999                                         "foreground-set", FALSE,
2000                                         "has-focus", FALSE, NULL);
2001                 }
2002 }
2003
2004 static void vficon_append_column(ViewFile *vf, gint n)
2005 {
2006         ColumnData *cd;
2007         GtkTreeViewColumn *column;
2008         GtkCellRenderer *renderer;
2009
2010         column = gtk_tree_view_column_new();
2011         gtk_tree_view_column_set_min_width(column, 0);
2012
2013         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2014         gtk_tree_view_column_set_alignment(column, 0.5);
2015
2016         renderer = gqv_cell_renderer_icon_new();
2017         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2018         g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
2019                                          "ypad", THUMB_BORDER_PADDING,
2020                                          "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
2021
2022         g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
2023         g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
2024
2025         cd = g_new0(ColumnData, 1);
2026         cd->vf = vf;
2027         cd->number = n;
2028         gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
2029
2030         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2031
2032         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
2033 }
2034
2035 /*
2036  *-----------------------------------------------------------------------------
2037  * base
2038  *-----------------------------------------------------------------------------
2039  */
2040
2041 gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd)
2042 {
2043         gboolean ret;
2044
2045         if (!dir_fd) return FALSE;
2046         if (vf->dir_fd == dir_fd) return TRUE;
2047
2048         file_data_unref(vf->dir_fd);
2049         vf->dir_fd = file_data_ref(dir_fd);
2050
2051         g_list_free(VFICON(vf)->selection);
2052         VFICON(vf)->selection = NULL;
2053
2054         g_list_free(vf->list);
2055         vf->list = NULL;
2056
2057         /* NOTE: populate will clear the store for us */
2058         ret = vficon_refresh_real(vf, FALSE);
2059
2060         VFICON(vf)->focus_fd = NULL;
2061         vficon_move_focus(vf, 0, 0, FALSE);
2062
2063         return ret;
2064 }
2065
2066 void vficon_destroy_cb(GtkWidget *widget, gpointer data)
2067 {
2068         ViewFile *vf = data;
2069
2070         vf_refresh_idle_cancel(vf);
2071
2072         file_data_unregister_notify_func(vf_notify_cb, vf);
2073
2074         tip_unschedule(vf);
2075
2076         vf_thumb_cleanup(vf);
2077
2078         g_list_free(vf->list);
2079         g_list_free(VFICON(vf)->selection);
2080 }
2081
2082 ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
2083 {
2084         GtkListStore *store;
2085         GtkTreeSelection *selection;
2086         gint i;
2087
2088         vf->info = g_new0(ViewFileInfoIcon, 1);
2089
2090         VFICON(vf)->show_text = options->show_icon_names;
2091
2092         store = gtk_list_store_new(1, G_TYPE_POINTER);
2093         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2094         g_object_unref(store);
2095
2096         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2097         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
2098
2099         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2100         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2101
2102         for (i = 0; i < VFICON_MAX_COLUMNS; i++)
2103                 {
2104                 vficon_append_column(vf, i);
2105                 }
2106
2107         /* zero width column to hide tree view focus, we draw it ourselves */
2108         vficon_append_column(vf, i);
2109         /* end column to fill white space */
2110         vficon_append_column(vf, i);
2111
2112         g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
2113                          G_CALLBACK(vficon_sized_cb), vf);
2114
2115         gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
2116                               GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
2117
2118         g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
2119                          G_CALLBACK(vficon_motion_cb), vf);
2120         g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
2121                          G_CALLBACK(vficon_leave_cb), vf);
2122
2123         /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
2124         vficon_populate_at_new_size(vf, 1, 1, FALSE);
2125
2126         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2127
2128         return vf;
2129 }
2130
2131 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */