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