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