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