Read metadata in the idle loop
[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_read_metadata_progress_count(GList *list, gint *count, gint *done)
1694 {
1695         GList *work = list;
1696         while (work)
1697                 {
1698                 FileData *fd = work->data;
1699                 work = work->next;
1700
1701                 if (fd->metadata_in_idle_loaded) (*done)++;
1702                 (*count)++;
1703                 }
1704 }
1705
1706 void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
1707 {
1708         GtkTreeModel *store;
1709         GtkTreeIter iter;
1710         GList *list;
1711
1712         if (!g_list_find(vf->list, fd)) return;
1713         if (!vficon_find_iter(vf, fd, &iter, NULL)) return;
1714
1715         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1716
1717         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1718         gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1719 }
1720
1721 /* Returns the next fd without a loaded pixbuf, so the thumb-loader can load the pixbuf for it. */
1722 FileData *vficon_thumb_next_fd(ViewFile *vf)
1723 {
1724         GtkTreePath *tpath;
1725
1726         /* First see if there are visible files that don't have a loaded thumb... */
1727         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1728                 {
1729                 GtkTreeModel *store;
1730                 GtkTreeIter iter;
1731                 gboolean valid = TRUE;
1732
1733                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1734                 gtk_tree_model_get_iter(store, &iter, tpath);
1735                 gtk_tree_path_free(tpath);
1736                 tpath = NULL;
1737
1738                 while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1739                         {
1740                         GList *list;
1741                         gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1742
1743                         // TODO(xsdg): for loop here.
1744                         for (; list; list = list->next)
1745                                 {
1746                                 FileData *fd = list->data;
1747                                 if (fd && !fd->thumb_pixbuf) return fd;
1748                                 }
1749
1750                         valid = gtk_tree_model_iter_next(store, &iter);
1751                         }
1752                 }
1753
1754         /* Then iterate through the entire list to load all of them. */
1755         GList *work;
1756         for (work = vf->list; work; work = work->next)
1757                 {
1758                 FileData *fd = work->data;
1759
1760                 // Note: This implementation differs from view_file_list.c because sidecar files are not
1761                 // distinct list elements here, as they are in the list view.
1762                 if (!fd->thumb_pixbuf) return fd;
1763                 }
1764
1765         return NULL;
1766 }
1767
1768 /*
1769  *-----------------------------------------------------------------------------
1770  * row stuff
1771  *-----------------------------------------------------------------------------
1772  */
1773
1774 gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
1775 {
1776         gint p = 0;
1777         GList *work;
1778
1779         if (!in_fd) return -1;
1780
1781         work = vf->list;
1782         while (work)
1783                 {
1784                 FileData *fd = work->data;
1785                 if (fd == in_fd) return p;
1786                 work = work->next;
1787                 p++;
1788                 }
1789
1790         return -1;
1791 }
1792
1793 /*
1794  *-----------------------------------------------------------------------------
1795  *
1796  *-----------------------------------------------------------------------------
1797  */
1798
1799 static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
1800 {
1801         gboolean ret = TRUE;
1802         GList *work, *new_work;
1803         FileData *focus_fd;
1804         FileData *first_selected = NULL;
1805         GList *new_filelist = NULL;
1806         GList *new_fd_list = NULL;
1807
1808         focus_fd = VFICON(vf)->focus_fd;
1809
1810         if (vf->dir_fd)
1811                 {
1812                 ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
1813                 new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
1814                 }
1815
1816         vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
1817         new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
1818
1819         if (VFICON(vf)->selection)
1820                 {
1821                 first_selected = VFICON(vf)->selection->data;
1822                 file_data_ref(first_selected);
1823                 g_list_free(VFICON(vf)->selection);
1824                 VFICON(vf)->selection = NULL;
1825                 }
1826
1827         /* iterate old list and new list, looking for differences */
1828         work = vf->list;
1829         new_work = new_filelist;
1830         while (work || new_work)
1831                 {
1832                 FileData *fd = NULL;
1833                 FileData *new_fd = NULL;
1834                 gint match;
1835
1836                 if (work && new_work)
1837                         {
1838                         fd = work->data;
1839                         new_fd = new_work->data;
1840
1841                         if (fd == new_fd)
1842                                 {
1843                                 /* not changed, go to next */
1844                                 work = work->next;
1845                                 new_work = new_work->next;
1846                                 if (fd->selected & SELECTION_SELECTED)
1847                                         {
1848                                         VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, fd);
1849                                         }
1850                                 continue;
1851                                 }
1852
1853                         match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend);
1854                         if (match == 0) g_warning("multiple fd for the same path");
1855                         }
1856                 else if (work)
1857                         {
1858                         /* old item was deleted */
1859                         fd = work->data;
1860                         match = -1;
1861                         }
1862                 else
1863                         {
1864                         /* new item was added */
1865                         new_fd = new_work->data;
1866                         match = 1;
1867                         }
1868
1869                 if (match < 0)
1870                         {
1871                         /* file no longer exists, delete from vf->list */
1872                         GList *to_delete = work;
1873                         work = work->next;
1874                         if (fd == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
1875                         if (fd == VFICON(vf)->click_fd) VFICON(vf)->click_fd = NULL;
1876                         file_data_unref(fd);
1877                         vf->list = g_list_delete_link(vf->list, to_delete);
1878                         }
1879                 else
1880                         {
1881                         /* new file, add to vf->list */
1882                         file_data_ref(new_fd);
1883                         new_fd->selected = SELECTION_NONE;
1884                         if (work)
1885                                 {
1886                                 vf->list = g_list_insert_before(vf->list, work, new_fd);
1887                                 }
1888                         else
1889                                 {
1890                                 /* it is faster to append all new entries together later */
1891                                 new_fd_list = g_list_prepend(new_fd_list, new_fd);
1892                                 }
1893
1894                         new_work = new_work->next;
1895                         }
1896                 }
1897
1898         if (new_fd_list)
1899                 {
1900                 vf->list = g_list_concat(vf->list, g_list_reverse(new_fd_list));
1901                 }
1902
1903         VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
1904
1905         filelist_free(new_filelist);
1906
1907         vficon_populate(vf, TRUE, keep_position);
1908
1909         if (first_selected && !VFICON(vf)->selection)
1910                 {
1911                 /* all selected files disappeared */
1912                 vficon_select_closest(vf, first_selected);
1913                 }
1914         file_data_unref(first_selected);
1915
1916         /* attempt to keep focus on same icon when refreshing */
1917         if (focus_fd && g_list_find(vf->list, focus_fd))
1918                 {
1919                 vficon_set_focus(vf, focus_fd);
1920                 }
1921
1922         return ret;
1923 }
1924
1925 gboolean vficon_refresh(ViewFile *vf)
1926 {
1927         return vficon_refresh_real(vf, TRUE);
1928 }
1929
1930 /*
1931  *-----------------------------------------------------------------------------
1932  * draw, etc.
1933  *-----------------------------------------------------------------------------
1934  */
1935
1936 typedef struct _ColumnData ColumnData;
1937 struct _ColumnData
1938 {
1939         ViewFile *vf;
1940         gint number;
1941 };
1942
1943 static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1944                                 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1945 {
1946         GList *list;
1947         FileData *fd;
1948         ColumnData *cd = data;
1949         ViewFile *vf = cd->vf;
1950
1951         if (!GQV_IS_CELL_RENDERER_ICON(cell)) return;
1952
1953         gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
1954
1955         fd = g_list_nth_data(list, cd->number);
1956
1957         if (fd)
1958                 {
1959                 GdkColor color_fg;
1960                 GdkColor color_bg;
1961                 GtkStyle *style;
1962                 gchar *name_sidecars;
1963                 gchar *link;
1964                 GtkStateType state = GTK_STATE_NORMAL;
1965
1966                 g_assert(fd->magick == FD_MAGICK);
1967
1968                 link = islink(fd->path) ? GQ_LINK_STR : "";
1969                 if (fd->sidecar_files)
1970                         {
1971                         gchar *sidecars = file_data_sc_list_to_string(fd);
1972                         name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
1973                         g_free(sidecars);
1974                         }
1975                 else
1976                         {
1977                         gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
1978                         name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
1979                         }
1980
1981                 style = gtk_widget_get_style(vf->listview);
1982                 if (fd->selected & SELECTION_SELECTED)
1983                         {
1984                         state = GTK_STATE_SELECTED;
1985                         }
1986
1987                 memcpy(&color_fg, &style->text[state], sizeof(color_fg));
1988                 memcpy(&color_bg, &style->base[state], sizeof(color_bg));
1989
1990                 if (fd->selected & SELECTION_PRELIGHT)
1991                         {
1992                         shift_color(&color_bg, -1, 0);
1993                         }
1994
1995                 g_object_set(cell,      "pixbuf", fd->thumb_pixbuf,
1996                                         "text", name_sidecars,
1997                                         "marks", file_data_get_marks(fd),
1998                                         "show_marks", vf->marks_enabled,
1999                                         "cell-background-gdk", &color_bg,
2000                                         "cell-background-set", TRUE,
2001                                         "foreground-gdk", &color_fg,
2002                                         "foreground-set", TRUE,
2003                                         "has-focus", (VFICON(vf)->focus_fd == fd), NULL);
2004                 g_free(name_sidecars);
2005                 }
2006         else
2007                 {
2008                 g_object_set(cell,      "pixbuf", NULL,
2009                                         "text", NULL,
2010                                         "show_marks", FALSE,
2011                                         "cell-background-set", FALSE,
2012                                         "foreground-set", FALSE,
2013                                         "has-focus", FALSE, NULL);
2014                 }
2015 }
2016
2017 static void vficon_append_column(ViewFile *vf, gint n)
2018 {
2019         ColumnData *cd;
2020         GtkTreeViewColumn *column;
2021         GtkCellRenderer *renderer;
2022
2023         column = gtk_tree_view_column_new();
2024         gtk_tree_view_column_set_min_width(column, 0);
2025
2026         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2027         gtk_tree_view_column_set_alignment(column, 0.5);
2028
2029         renderer = gqv_cell_renderer_icon_new();
2030         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2031         g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
2032                                          "ypad", THUMB_BORDER_PADDING,
2033                                          "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
2034
2035         g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
2036         g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n));
2037
2038         cd = g_new0(ColumnData, 1);
2039         cd->vf = vf;
2040         cd->number = n;
2041         gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
2042
2043         gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2044
2045         g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf);
2046 }
2047
2048 /*
2049  *-----------------------------------------------------------------------------
2050  * base
2051  *-----------------------------------------------------------------------------
2052  */
2053
2054 gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd)
2055 {
2056         gboolean ret;
2057
2058         if (!dir_fd) return FALSE;
2059         if (vf->dir_fd == dir_fd) return TRUE;
2060
2061         file_data_unref(vf->dir_fd);
2062         vf->dir_fd = file_data_ref(dir_fd);
2063
2064         g_list_free(VFICON(vf)->selection);
2065         VFICON(vf)->selection = NULL;
2066
2067         g_list_free(vf->list);
2068         vf->list = NULL;
2069
2070         /* NOTE: populate will clear the store for us */
2071         ret = vficon_refresh_real(vf, FALSE);
2072
2073         VFICON(vf)->focus_fd = NULL;
2074         vficon_move_focus(vf, 0, 0, FALSE);
2075
2076         return ret;
2077 }
2078
2079 void vficon_destroy_cb(GtkWidget *widget, gpointer data)
2080 {
2081         ViewFile *vf = data;
2082
2083         vf_refresh_idle_cancel(vf);
2084
2085         file_data_unregister_notify_func(vf_notify_cb, vf);
2086
2087         tip_unschedule(vf);
2088
2089         vf_thumb_cleanup(vf);
2090
2091         g_list_free(vf->list);
2092         g_list_free(VFICON(vf)->selection);
2093 }
2094
2095 ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd)
2096 {
2097         GtkListStore *store;
2098         GtkTreeSelection *selection;
2099         gint i;
2100
2101         vf->info = g_new0(ViewFileInfoIcon, 1);
2102
2103         VFICON(vf)->show_text = options->show_icon_names;
2104
2105         store = gtk_list_store_new(1, G_TYPE_POINTER);
2106         vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2107         g_object_unref(store);
2108
2109         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2110         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
2111
2112         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2113         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2114
2115         for (i = 0; i < VFICON_MAX_COLUMNS; i++)
2116                 {
2117                 vficon_append_column(vf, i);
2118                 }
2119
2120         /* zero width column to hide tree view focus, we draw it ourselves */
2121         vficon_append_column(vf, i);
2122         /* end column to fill white space */
2123         vficon_append_column(vf, i);
2124
2125         g_signal_connect(G_OBJECT(vf->listview), "size_allocate",
2126                          G_CALLBACK(vficon_sized_cb), vf);
2127
2128         gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
2129                               GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
2130
2131         g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event",
2132                          G_CALLBACK(vficon_motion_cb), vf);
2133         g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event",
2134                          G_CALLBACK(vficon_leave_cb), vf);
2135
2136         /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */
2137         vficon_populate_at_new_size(vf, 1, 1, FALSE);
2138
2139         file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2140
2141         return vf;
2142 }
2143
2144 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */