Partial fix 435: Duplicates in file selection
[geeqie.git] / src / view_file.c
1 /*
2  * Copyright (C) 2008 - 2016 The Geeqie Team
3  *
4  * Author: Laurent Monin
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22 #include "view_file.h"
23
24 #include "dupe.h"
25 #include "editors.h"
26 #include "layout.h"
27 #include "menu.h"
28 #include "thumb.h"
29 #include "ui_menu.h"
30 #include "ui_fileops.h"
31 #include "utilops.h"
32 #include "view_file_list.h"
33 #include "view_file_icon.h"
34
35 /*
36  *-----------------------------------------------------------------------------
37  * signals
38  *-----------------------------------------------------------------------------
39  */
40
41 void vf_send_update(ViewFile *vf)
42 {
43         if (vf->func_status) vf->func_status(vf, vf->data_status);
44 }
45
46 /*
47  *-----------------------------------------------------------------------------
48  * misc
49  *-----------------------------------------------------------------------------
50  */
51
52 void vf_sort_set(ViewFile *vf, SortType type, gboolean ascend)
53 {
54         switch (vf->type)
55         {
56         case FILEVIEW_LIST: vflist_sort_set(vf, type, ascend); break;
57         case FILEVIEW_ICON: vficon_sort_set(vf, type, ascend); break;
58         }
59 }
60
61 /*
62  *-----------------------------------------------------------------------------
63  * row stuff
64  *-----------------------------------------------------------------------------
65  */
66
67 FileData *vf_index_get_data(ViewFile *vf, gint row)
68 {
69         FileData *fd = NULL;
70
71         switch (vf->type)
72         {
73         case FILEVIEW_LIST: fd = vflist_index_get_data(vf, row); break;
74         case FILEVIEW_ICON: fd = vficon_index_get_data(vf, row); break;
75         }
76
77         return fd;
78 }
79
80 gint vf_index_by_fd(ViewFile *vf, FileData *fd)
81 {
82         gint index = -1;
83
84         switch (vf->type)
85         {
86         case FILEVIEW_LIST: index = vflist_index_by_fd(vf, fd); break;
87         case FILEVIEW_ICON: index = vficon_index_by_fd(vf, fd); break;
88         }
89
90         return index;
91 }
92
93 guint vf_count(ViewFile *vf, gint64 *bytes)
94 {
95         guint count = 0;
96
97         switch (vf->type)
98         {
99         case FILEVIEW_LIST: count = vflist_count(vf, bytes); break;
100         case FILEVIEW_ICON: count = vficon_count(vf, bytes); break;
101         }
102
103         return count;
104 }
105
106 GList *vf_get_list(ViewFile *vf)
107 {
108         GList *list = NULL;
109
110         switch (vf->type)
111         {
112         case FILEVIEW_LIST: list = vflist_get_list(vf); break;
113         case FILEVIEW_ICON: list = vficon_get_list(vf); break;
114         }
115
116         return list;
117 }
118
119
120 /*
121  *-------------------------------------------------------------------
122  * keyboard
123  *-------------------------------------------------------------------
124  */
125
126 static gboolean vf_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
127 {
128         ViewFile *vf = data;
129         gboolean ret = FALSE;
130
131         switch (vf->type)
132         {
133         case FILEVIEW_LIST: ret = vflist_press_key_cb(widget, event, data); break;
134         case FILEVIEW_ICON: ret = vficon_press_key_cb(widget, event, data); break;
135         }
136
137         return ret;
138 }
139
140 /*
141  *-------------------------------------------------------------------
142  * mouse
143  *-------------------------------------------------------------------
144  */
145
146 static gboolean vf_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
147 {
148         ViewFile *vf = data;
149         gboolean ret = FALSE;
150
151         switch (vf->type)
152         {
153         case FILEVIEW_LIST: ret = vflist_press_cb(widget, bevent, data); break;
154         case FILEVIEW_ICON: ret = vficon_press_cb(widget, bevent, data); break;
155         }
156
157         return ret;
158 }
159
160 static gboolean vf_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
161 {
162         ViewFile *vf = data;
163         gboolean ret = FALSE;
164
165         switch (vf->type)
166         {
167         case FILEVIEW_LIST: ret = vflist_release_cb(widget, bevent, data); break;
168         case FILEVIEW_ICON: ret = vficon_release_cb(widget, bevent, data); break;
169         }
170
171         return ret;
172 }
173
174
175 /*
176  *-----------------------------------------------------------------------------
177  * selections
178  *-----------------------------------------------------------------------------
179  */
180
181 gboolean vf_index_is_selected(ViewFile *vf, gint row)
182 {
183         gboolean ret = FALSE;
184
185         switch (vf->type)
186         {
187         case FILEVIEW_LIST: ret = vflist_index_is_selected(vf, row); break;
188         case FILEVIEW_ICON: ret = vficon_index_is_selected(vf, row); break;
189         }
190
191         return ret;
192 }
193
194
195 guint vf_selection_count(ViewFile *vf, gint64 *bytes)
196 {
197         guint count = 0;
198
199         switch (vf->type)
200         {
201         case FILEVIEW_LIST: count = vflist_selection_count(vf, bytes); break;
202         case FILEVIEW_ICON: count = vficon_selection_count(vf, bytes); break;
203         }
204
205         return count;
206 }
207
208 GList *vf_selection_get_list(ViewFile *vf)
209 {
210         GList *list = NULL;
211
212         switch (vf->type)
213         {
214         case FILEVIEW_LIST: list = vflist_selection_get_list(vf); break;
215         case FILEVIEW_ICON: list = vficon_selection_get_list(vf); break;
216         }
217
218         return list;
219 }
220
221 GList *vf_selection_get_list_by_index(ViewFile *vf)
222 {
223         GList *list = NULL;
224
225         switch (vf->type)
226         {
227         case FILEVIEW_LIST: list = vflist_selection_get_list_by_index(vf); break;
228         case FILEVIEW_ICON: list = vficon_selection_get_list_by_index(vf); break;
229         }
230
231         return list;
232 }
233
234 void vf_select_all(ViewFile *vf)
235 {
236         switch (vf->type)
237         {
238         case FILEVIEW_LIST: vflist_select_all(vf); break;
239         case FILEVIEW_ICON: vficon_select_all(vf); break;
240         }
241 }
242
243 void vf_select_none(ViewFile *vf)
244 {
245         switch (vf->type)
246         {
247         case FILEVIEW_LIST: vflist_select_none(vf); break;
248         case FILEVIEW_ICON: vficon_select_none(vf); break;
249         }
250 }
251
252 void vf_select_invert(ViewFile *vf)
253 {
254         switch (vf->type)
255         {
256         case FILEVIEW_LIST: vflist_select_invert(vf); break;
257         case FILEVIEW_ICON: vficon_select_invert(vf); break;
258         }
259 }
260
261 void vf_select_by_fd(ViewFile *vf, FileData *fd)
262 {
263         switch (vf->type)
264         {
265         case FILEVIEW_LIST: vflist_select_by_fd(vf, fd); break;
266         case FILEVIEW_ICON: vficon_select_by_fd(vf, fd); break;
267         }
268 }
269
270 void vf_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
271 {
272         switch (vf->type)
273         {
274         case FILEVIEW_LIST: vflist_mark_to_selection(vf, mark, mode); break;
275         case FILEVIEW_ICON: vficon_mark_to_selection(vf, mark, mode); break;
276         }
277 }
278
279 void vf_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
280 {
281         switch (vf->type)
282         {
283         case FILEVIEW_LIST: vflist_selection_to_mark(vf, mark, mode); break;
284         case FILEVIEW_ICON: vficon_selection_to_mark(vf, mark, mode); break;
285         }
286 }
287
288 /*
289  *-----------------------------------------------------------------------------
290  * dnd
291  *-----------------------------------------------------------------------------
292  */
293
294
295 static void vf_dnd_init(ViewFile *vf)
296 {
297         switch (vf->type)
298         {
299         case FILEVIEW_LIST: vflist_dnd_init(vf); break;
300         case FILEVIEW_ICON: vficon_dnd_init(vf); break;
301         }
302 }
303
304 /*
305  *-----------------------------------------------------------------------------
306  * pop-up menu
307  *-----------------------------------------------------------------------------
308  */
309
310 GList *vf_pop_menu_file_list(ViewFile *vf)
311 {
312         GList *ret = NULL;
313
314         switch (vf->type)
315         {
316         case FILEVIEW_LIST: ret = vflist_pop_menu_file_list(vf); break;
317         case FILEVIEW_ICON: ret = vficon_pop_menu_file_list(vf); break;
318         }
319
320         return ret;
321 }
322
323 GList *vf_selection_get_one(ViewFile *vf, FileData *fd)
324 {
325         GList *ret = NULL;
326
327         switch (vf->type)
328         {
329         case FILEVIEW_LIST: ret = vflist_selection_get_one(vf, fd); break;
330         case FILEVIEW_ICON: ret = vficon_selection_get_one(vf, fd); break;
331         }
332
333         return ret;
334 }
335
336 static void vf_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
337 {
338         ViewFile *vf;
339         const gchar *key = data;
340
341         vf = submenu_item_get_data(widget);
342
343         if (!vf) return;
344
345         file_util_start_editor_from_filelist(key, vf_pop_menu_file_list(vf), vf->dir_fd->path, vf->listview);
346 }
347
348 static void vf_pop_menu_view_cb(GtkWidget *widget, gpointer data)
349 {
350         ViewFile *vf = data;
351
352         switch (vf->type)
353         {
354         case FILEVIEW_LIST: vflist_pop_menu_view_cb(widget, data); break;
355         case FILEVIEW_ICON: vficon_pop_menu_view_cb(widget, data); break;
356         }
357 }
358
359 static void vf_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
360 {
361         ViewFile *vf = data;
362
363         file_util_copy(NULL, vf_pop_menu_file_list(vf), NULL, vf->listview);
364 }
365
366 static void vf_pop_menu_move_cb(GtkWidget *widget, gpointer data)
367 {
368         ViewFile *vf = data;
369
370         file_util_move(NULL, vf_pop_menu_file_list(vf), NULL, vf->listview);
371 }
372
373 static void vf_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
374 {
375         ViewFile *vf = data;
376
377         switch (vf->type)
378         {
379         case FILEVIEW_LIST: vflist_pop_menu_rename_cb(widget, data); break;
380         case FILEVIEW_ICON: vficon_pop_menu_rename_cb(widget, data); break;
381         }
382 }
383
384 static void vf_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
385 {
386         ViewFile *vf = data;
387
388         file_util_delete(NULL, vf_pop_menu_file_list(vf), vf->listview);
389 }
390
391 static void vf_pop_menu_copy_path_cb(GtkWidget *widget, gpointer data)
392 {
393         ViewFile *vf = data;
394
395         file_util_copy_path_list_to_clipboard(vf_pop_menu_file_list(vf));
396 }
397
398 static void vf_pop_menu_enable_grouping_cb(GtkWidget *widget, gpointer data)
399 {
400         ViewFile *vf = data;
401
402         file_data_disable_grouping_list(vf_pop_menu_file_list(vf), FALSE);
403 }
404
405 static void vf_pop_menu_duplicates_cb(GtkWidget *widget, gpointer data)
406 {
407         ViewFile *vf = data;
408         DupeWindow *dw;
409
410         dw = dupe_window_new(DUPE_MATCH_NAME);
411         dupe_window_add_files(dw, vf_pop_menu_file_list(vf), FALSE);
412 }
413
414 static void vf_pop_menu_disable_grouping_cb(GtkWidget *widget, gpointer data)
415 {
416         ViewFile *vf = data;
417
418         file_data_disable_grouping_list(vf_pop_menu_file_list(vf), TRUE);
419 }
420
421 static void vf_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
422 {
423         ViewFile *vf;
424         SortType type;
425
426         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
427
428         vf = submenu_item_get_data(widget);
429         if (!vf) return;
430
431         type = (SortType)GPOINTER_TO_INT(data);
432
433         if (vf->layout)
434                 {
435                 layout_sort_set(vf->layout, type, vf->sort_ascend);
436                 }
437         else
438                 {
439                 vf_sort_set(vf, type, vf->sort_ascend);
440                 }
441 }
442
443 static void vf_pop_menu_sort_ascend_cb(GtkWidget *widget, gpointer data)
444 {
445         ViewFile *vf = data;
446
447         if (vf->layout)
448                 {
449                 layout_sort_set(vf->layout, vf->sort_method, !vf->sort_ascend);
450                 }
451         else
452                 {
453                 vf_sort_set(vf, vf->sort_method, !vf->sort_ascend);
454                 }
455 }
456
457 static void vf_pop_menu_sel_mark_cb(GtkWidget *widget, gpointer data)
458 {
459         ViewFile *vf = data;
460         vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_SET);
461 }
462
463 static void vf_pop_menu_sel_mark_and_cb(GtkWidget *widget, gpointer data)
464 {
465         ViewFile *vf = data;
466         vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_AND);
467 }
468
469 static void vf_pop_menu_sel_mark_or_cb(GtkWidget *widget, gpointer data)
470 {
471         ViewFile *vf = data;
472         vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_OR);
473 }
474
475 static void vf_pop_menu_sel_mark_minus_cb(GtkWidget *widget, gpointer data)
476 {
477         ViewFile *vf = data;
478         vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_MINUS);
479 }
480
481 static void vf_pop_menu_set_mark_sel_cb(GtkWidget *widget, gpointer data)
482 {
483         ViewFile *vf = data;
484         vf_selection_to_mark(vf, vf->active_mark, STM_MODE_SET);
485 }
486
487 static void vf_pop_menu_res_mark_sel_cb(GtkWidget *widget, gpointer data)
488 {
489         ViewFile *vf = data;
490         vf_selection_to_mark(vf, vf->active_mark, STM_MODE_RESET);
491 }
492
493 static void vf_pop_menu_toggle_mark_sel_cb(GtkWidget *widget, gpointer data)
494 {
495         ViewFile *vf = data;
496         vf_selection_to_mark(vf, vf->active_mark, STM_MODE_TOGGLE);
497 }
498
499 static void vf_pop_menu_toggle_view_type_cb(GtkWidget *widget, gpointer data)
500 {
501         ViewFile *vf = data;
502         FileViewType new_type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "menu_item_radio_data"));
503         if (!vf->layout) return;
504
505         layout_views_set(vf->layout, vf->layout->options.dir_view_type, new_type);
506 }
507
508 static void vf_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
509 {
510         ViewFile *vf = data;
511
512         switch (vf->type)
513         {
514         case FILEVIEW_LIST: vflist_pop_menu_refresh_cb(widget, data); break;
515         case FILEVIEW_ICON: vficon_pop_menu_refresh_cb(widget, data); break;
516         }
517 }
518
519 static void vf_popup_destroy_cb(GtkWidget *widget, gpointer data)
520 {
521         ViewFile *vf = data;
522
523         switch (vf->type)
524         {
525         case FILEVIEW_LIST: vflist_popup_destroy_cb(widget, data); break;
526         case FILEVIEW_ICON: vficon_popup_destroy_cb(widget, data); break;
527         }
528
529         filelist_free(vf->editmenu_fd_list);
530         vf->editmenu_fd_list = NULL;
531 }
532
533 GtkWidget *vf_pop_menu(ViewFile *vf)
534 {
535         GtkWidget *menu;
536         GtkWidget *item;
537         GtkWidget *submenu;
538         gboolean active = FALSE;
539
540         switch (vf->type)
541         {
542         case FILEVIEW_LIST:
543                 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
544                 active = (VFLIST(vf)->click_fd != NULL);
545                 break;
546         case FILEVIEW_ICON:
547                 active = (VFICON(vf)->click_id != NULL);
548                 break;
549         }
550
551         menu = popup_menu_short_lived();
552
553         g_signal_connect(G_OBJECT(menu), "destroy",
554                          G_CALLBACK(vf_popup_destroy_cb), vf);
555
556         if (vf->clicked_mark > 0)
557                 {
558                 gint mark = vf->clicked_mark;
559                 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark);
560                 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark);
561                 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark);
562                 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark);
563                 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark);
564                 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark);
565                 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark);
566
567                 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
568
569                 vf->active_mark = mark;
570                 vf->clicked_mark = 0;
571
572                 menu_item_add_sensitive(menu, str_set_mark, active,
573                                         G_CALLBACK(vf_pop_menu_set_mark_sel_cb), vf);
574
575                 menu_item_add_sensitive(menu, str_res_mark, active,
576                                         G_CALLBACK(vf_pop_menu_res_mark_sel_cb), vf);
577
578                 menu_item_add_sensitive(menu, str_toggle_mark, active,
579                                         G_CALLBACK(vf_pop_menu_toggle_mark_sel_cb), vf);
580
581                 menu_item_add_divider(menu);
582
583                 menu_item_add_sensitive(menu, str_sel_mark, active,
584                                         G_CALLBACK(vf_pop_menu_sel_mark_cb), vf);
585                 menu_item_add_sensitive(menu, str_sel_mark_or, active,
586                                         G_CALLBACK(vf_pop_menu_sel_mark_or_cb), vf);
587                 menu_item_add_sensitive(menu, str_sel_mark_and, active,
588                                         G_CALLBACK(vf_pop_menu_sel_mark_and_cb), vf);
589                 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
590                                         G_CALLBACK(vf_pop_menu_sel_mark_minus_cb), vf);
591
592                 menu_item_add_divider(menu);
593
594                 g_free(str_set_mark);
595                 g_free(str_res_mark);
596                 g_free(str_toggle_mark);
597                 g_free(str_sel_mark);
598                 g_free(str_sel_mark_and);
599                 g_free(str_sel_mark_or);
600                 g_free(str_sel_mark_minus);
601                 }
602
603         vf->editmenu_fd_list = vf_pop_menu_file_list(vf);
604         submenu_add_edit(menu, &item, G_CALLBACK(vf_pop_menu_edit_cb), vf, vf->editmenu_fd_list);
605         gtk_widget_set_sensitive(item, active);
606
607         menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
608                                       G_CALLBACK(vf_pop_menu_view_cb), vf);
609
610         menu_item_add_divider(menu);
611         menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
612                                       G_CALLBACK(vf_pop_menu_copy_cb), vf);
613         menu_item_add_sensitive(menu, _("_Move..."), active,
614                                 G_CALLBACK(vf_pop_menu_move_cb), vf);
615         menu_item_add_sensitive(menu, _("_Rename..."), active,
616                                 G_CALLBACK(vf_pop_menu_rename_cb), vf);
617         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
618                                       G_CALLBACK(vf_pop_menu_delete_cb), vf);
619         menu_item_add_sensitive(menu, _("_Copy path"), active,
620                                 G_CALLBACK(vf_pop_menu_copy_path_cb), vf);
621
622         menu_item_add_sensitive(menu, _("Enable file _grouping"), active,
623                                 G_CALLBACK(vf_pop_menu_enable_grouping_cb), vf);
624         menu_item_add_sensitive(menu, _("Disable file groupi_ng"), active,
625                                 G_CALLBACK(vf_pop_menu_disable_grouping_cb), vf);
626
627         menu_item_add_divider(menu);
628         menu_item_add_stock_sensitive(menu, _("_Find duplicates..."), GTK_STOCK_FIND, active,
629                                 G_CALLBACK(vf_pop_menu_duplicates_cb), vf);
630         menu_item_add_divider(menu);
631
632         submenu = submenu_add_sort(NULL, G_CALLBACK(vf_pop_menu_sort_cb), vf,
633                                    FALSE, FALSE, TRUE, vf->sort_method);
634         menu_item_add_divider(submenu);
635         menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
636                             G_CALLBACK(vf_pop_menu_sort_ascend_cb), vf);
637
638         item = menu_item_add(menu, _("_Sort"), NULL, NULL);
639         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
640
641         item = menu_item_add_radio(menu, _("View as _List"), GINT_TO_POINTER(FILEVIEW_LIST), vf->type == FILEVIEW_LIST,
642                                            G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
643
644         item = menu_item_add_radio(menu, _("View as _Icons"), GINT_TO_POINTER(FILEVIEW_ICON), vf->type == FILEVIEW_ICON,
645                                            G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
646
647         switch (vf->type)
648         {
649         case FILEVIEW_LIST:
650                 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST(vf)->thumbs_enabled,
651                                     G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
652                 break;
653         case FILEVIEW_ICON:
654                 menu_item_add_check(menu, _("Show filename _text"), VFICON(vf)->show_text,
655                                     G_CALLBACK(vficon_pop_menu_show_names_cb), vf);
656                 break;
657         }
658
659         menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vf_pop_menu_refresh_cb), vf);
660
661         return menu;
662 }
663
664 gboolean vf_refresh(ViewFile *vf)
665 {
666         gboolean ret = FALSE;
667
668         switch (vf->type)
669         {
670         case FILEVIEW_LIST: ret = vflist_refresh(vf); break;
671         case FILEVIEW_ICON: ret = vficon_refresh(vf); break;
672         }
673
674         return ret;
675 }
676
677 gboolean vf_set_fd(ViewFile *vf, FileData *dir_fd)
678 {
679         gboolean ret = FALSE;
680
681         switch (vf->type)
682         {
683         case FILEVIEW_LIST: ret = vflist_set_fd(vf, dir_fd); break;
684         case FILEVIEW_ICON: ret = vficon_set_fd(vf, dir_fd); break;
685         }
686
687         return ret;
688 }
689
690 static void vf_destroy_cb(GtkWidget *widget, gpointer data)
691 {
692         ViewFile *vf = data;
693
694         switch (vf->type)
695         {
696         case FILEVIEW_LIST: vflist_destroy_cb(widget, data); break;
697         case FILEVIEW_ICON: vficon_destroy_cb(widget, data); break;
698         }
699
700         if (vf->popup)
701                 {
702                 g_signal_handlers_disconnect_matched(G_OBJECT(vf->popup), G_SIGNAL_MATCH_DATA,
703                                                      0, 0, 0, NULL, vf);
704                 gtk_widget_destroy(vf->popup);
705                 }
706
707         file_data_unref(vf->dir_fd);
708         g_free(vf->info);
709         g_free(vf);
710 }
711
712 static void vf_marks_filter_toggle_cb(GtkWidget *widget, gpointer data)
713 {
714         ViewFile *vf = data;
715         vf_refresh_idle(vf);
716 }
717
718
719 static GtkWidget *vf_marks_filter_init(ViewFile *vf)
720 {
721         GtkWidget *frame = gtk_frame_new(NULL);
722         GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
723
724         gint i;
725
726         for (i = 0; i < FILEDATA_MARKS_SIZE ; i++)
727                 {
728                 GtkWidget *check = gtk_check_button_new();
729                 gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0);
730                 g_signal_connect(G_OBJECT(check), "toggled",
731                          G_CALLBACK(vf_marks_filter_toggle_cb), vf);
732
733                 gtk_widget_show(check);
734                 vf->filter_check[i] = check;
735                 }
736         gtk_container_add(GTK_CONTAINER(frame), hbox);
737         gtk_widget_show(hbox);
738         return frame;
739 }
740
741 void vf_mark_filter_toggle(ViewFile *vf, gint mark)
742 {
743         gint n = mark - 1;
744         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vf->filter_check[n]),
745                                      !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vf->filter_check[n])));
746 }
747
748 ViewFile *vf_new(FileViewType type, FileData *dir_fd)
749 {
750         ViewFile *vf;
751
752         vf = g_new0(ViewFile, 1);
753
754         vf->type = type;
755         vf->sort_method = SORT_NAME;
756         vf->sort_ascend = TRUE;
757
758         vf->scrolled = gtk_scrolled_window_new(NULL, NULL);
759         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vf->scrolled), GTK_SHADOW_IN);
760         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vf->scrolled),
761                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
762
763         vf->filter = vf_marks_filter_init(vf);
764
765         vf->widget = gtk_vbox_new(FALSE, 0);
766         gtk_box_pack_start(GTK_BOX(vf->widget), vf->filter, FALSE, FALSE, 0);
767         gtk_box_pack_start(GTK_BOX(vf->widget), vf->scrolled, TRUE, TRUE, 0);
768         gtk_widget_show(vf->scrolled);
769
770         g_signal_connect(G_OBJECT(vf->widget), "destroy",
771                          G_CALLBACK(vf_destroy_cb), vf);
772
773         switch (type)
774         {
775         case FILEVIEW_LIST: vf = vflist_new(vf, dir_fd); break;
776         case FILEVIEW_ICON: vf = vficon_new(vf, dir_fd); break;
777         }
778
779         vf_dnd_init(vf);
780
781         g_signal_connect(G_OBJECT(vf->listview), "key_press_event",
782                          G_CALLBACK(vf_press_key_cb), vf);
783         g_signal_connect(G_OBJECT(vf->listview), "button_press_event",
784                          G_CALLBACK(vf_press_cb), vf);
785         g_signal_connect(G_OBJECT(vf->listview), "button_release_event",
786                          G_CALLBACK(vf_release_cb), vf);
787
788         gtk_container_add(GTK_CONTAINER(vf->scrolled), vf->listview);
789         gtk_widget_show(vf->listview);
790
791         if (dir_fd) vf_set_fd(vf, dir_fd);
792
793         return vf;
794 }
795
796 void vf_set_status_func(ViewFile *vf, void (*func)(ViewFile *vf, gpointer data), gpointer data)
797 {
798         vf->func_status = func;
799         vf->data_status = data;
800 }
801
802 void vf_set_thumb_status_func(ViewFile *vf, void (*func)(ViewFile *vf, gdouble val, const gchar *text, gpointer data), gpointer data)
803 {
804         vf->func_thumb_status = func;
805         vf->data_thumb_status = data;
806 }
807
808 void vf_thumb_set(ViewFile *vf, gboolean enable)
809 {
810         switch (vf->type)
811         {
812         case FILEVIEW_LIST: vflist_thumb_set(vf, enable); break;
813         case FILEVIEW_ICON: /*vficon_thumb_set(vf, enable);*/ break;
814         }
815 }
816
817
818 static gboolean vf_thumb_next(ViewFile *vf);
819
820 static gdouble vf_thumb_progress(ViewFile *vf)
821 {
822         gint count = 0;
823         gint done = 0;
824
825         switch (vf->type)
826         {
827         case FILEVIEW_LIST: vflist_thumb_progress_count(vf->list, &count, &done); break;
828         case FILEVIEW_ICON: vficon_thumb_progress_count(vf->list, &count, &done); break;
829         }
830
831         DEBUG_1("thumb progress: %d of %d", done, count);
832         return (gdouble)done / count;
833 }
834
835 static void vf_set_thumb_fd(ViewFile *vf, FileData *fd)
836 {
837         switch (vf->type)
838         {
839         case FILEVIEW_LIST: vflist_set_thumb_fd(vf, fd); break;
840         case FILEVIEW_ICON: vficon_set_thumb_fd(vf, fd); break;
841         }
842 }
843
844 static void vf_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
845 {
846         if (vf->func_thumb_status)
847                 {
848                 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
849                 }
850 }
851
852 static void vf_thumb_do(ViewFile *vf, FileData *fd)
853 {
854         if (!fd) return;
855
856         vf_set_thumb_fd(vf, fd);
857         vf_thumb_status(vf, vf_thumb_progress(vf), _("Loading thumbs..."));
858 }
859
860 void vf_thumb_cleanup(ViewFile *vf)
861 {
862         vf_thumb_status(vf, 0.0, NULL);
863
864         vf->thumbs_running = FALSE;
865
866         thumb_loader_free(vf->thumbs_loader);
867         vf->thumbs_loader = NULL;
868
869         vf->thumbs_filedata = NULL;
870 }
871
872 void vf_thumb_stop(ViewFile *vf)
873 {
874         if (vf->thumbs_running) vf_thumb_cleanup(vf);
875 }
876
877 static void vf_thumb_common_cb(ThumbLoader *tl, gpointer data)
878 {
879         ViewFile *vf = data;
880
881         if (vf->thumbs_filedata && vf->thumbs_loader == tl)
882                 {
883                 vf_thumb_do(vf, vf->thumbs_filedata);
884                 }
885
886         while (vf_thumb_next(vf));
887 }
888
889 static void vf_thumb_error_cb(ThumbLoader *tl, gpointer data)
890 {
891         vf_thumb_common_cb(tl, data);
892 }
893
894 static void vf_thumb_done_cb(ThumbLoader *tl, gpointer data)
895 {
896         vf_thumb_common_cb(tl, data);
897 }
898
899 static gboolean vf_thumb_next(ViewFile *vf)
900 {
901         FileData *fd = NULL;
902
903         if (!gtk_widget_get_realized(vf->listview))
904                 {
905                 vf_thumb_status(vf, 0.0, NULL);
906                 return FALSE;
907                 }
908
909         switch (vf->type)
910         {
911         case FILEVIEW_LIST: fd = vflist_thumb_next_fd(vf); break;
912         case FILEVIEW_ICON: fd = vficon_thumb_next_fd(vf); break;
913         }
914
915         if (!fd)
916                 {
917                 /* done */
918                 vf_thumb_cleanup(vf);
919                 return FALSE;
920                 }
921
922         vf->thumbs_filedata = fd;
923
924         thumb_loader_free(vf->thumbs_loader);
925
926         vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
927         thumb_loader_set_callbacks(vf->thumbs_loader,
928                                    vf_thumb_done_cb,
929                                    vf_thumb_error_cb,
930                                    NULL,
931                                    vf);
932
933         if (!thumb_loader_start(vf->thumbs_loader, fd))
934                 {
935                 /* set icon to unknown, continue */
936                 DEBUG_1("thumb loader start failed %s", fd->path);
937                 vf_thumb_do(vf, fd);
938
939                 return TRUE;
940                 }
941
942         return FALSE;
943 }
944
945 static void vf_thumb_reset_all(ViewFile *vf)
946 {
947         switch (vf->type)
948         {
949         case FILEVIEW_LIST: vflist_thumb_reset_all(vf); break;
950         case FILEVIEW_ICON: vficon_thumb_reset_all(vf); break;
951         }
952 }
953
954 void vf_thumb_update(ViewFile *vf)
955 {
956         vf_thumb_stop(vf);
957
958         if (vf->type == FILEVIEW_LIST && !VFLIST(vf)->thumbs_enabled) return;
959
960         vf_thumb_status(vf, 0.0, _("Loading thumbs..."));
961         vf->thumbs_running = TRUE;
962
963         if (thumb_format_changed)
964                 {
965                 vf_thumb_reset_all(vf);
966                 thumb_format_changed = FALSE;
967                 }
968
969         while (vf_thumb_next(vf));
970 }
971
972
973 void vf_marks_set(ViewFile *vf, gboolean enable)
974 {
975         if (vf->marks_enabled == enable) return;
976
977         vf->marks_enabled = enable;
978
979         switch (vf->type)
980         {
981         case FILEVIEW_LIST: vflist_marks_set(vf, enable); break;
982         case FILEVIEW_ICON: vficon_marks_set(vf, enable); break;
983         }
984         if (enable)
985                 gtk_widget_show(vf->filter);
986         else
987                 gtk_widget_hide(vf->filter);
988
989         vf_refresh_idle(vf);
990 }
991
992 guint vf_marks_get_filter(ViewFile *vf)
993 {
994         guint ret = 0;
995         gint i;
996         if (!vf->marks_enabled) return 0;
997
998         for (i = 0; i < FILEDATA_MARKS_SIZE ; i++)
999                 {
1000                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vf->filter_check[i])))
1001                         {
1002                         ret |= 1 << i;
1003                         }
1004                 }
1005         return ret;
1006 }
1007
1008 void vf_set_layout(ViewFile *vf, LayoutWindow *layout)
1009 {
1010         vf->layout = layout;
1011 }
1012
1013
1014 /*
1015  *-----------------------------------------------------------------------------
1016  * maintenance (for rename, move, remove)
1017  *-----------------------------------------------------------------------------
1018  */
1019
1020 static gboolean vf_refresh_idle_cb(gpointer data)
1021 {
1022         ViewFile *vf = data;
1023
1024         vf_refresh(vf);
1025         vf->refresh_idle_id = 0;
1026         return FALSE;
1027 }
1028
1029 void vf_refresh_idle_cancel(ViewFile *vf)
1030 {
1031         if (vf->refresh_idle_id)
1032                 {
1033                 g_source_remove(vf->refresh_idle_id);
1034                 vf->refresh_idle_id = 0;
1035                 }
1036 }
1037
1038
1039 void vf_refresh_idle(ViewFile *vf)
1040 {
1041         if (!vf->refresh_idle_id)
1042                 {
1043                 vf->time_refresh_set = time(NULL);
1044                 /* file operations run with G_PRIORITY_DEFAULT_IDLE */
1045                 vf->refresh_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE + 50, vf_refresh_idle_cb, vf, NULL);
1046                 }
1047         else if (time(NULL) - vf->time_refresh_set > 1)
1048                 {
1049                 /* more than 1 sec since last update - increase priority */
1050                 vf_refresh_idle_cancel(vf);
1051                 vf->time_refresh_set = time(NULL);
1052                 vf->refresh_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE - 50, vf_refresh_idle_cb, vf, NULL);
1053                 }
1054 }
1055
1056 void vf_notify_cb(FileData *fd, NotifyType type, gpointer data)
1057 {
1058         ViewFile *vf = data;
1059         gboolean refresh;
1060
1061         NotifyType interested = NOTIFY_CHANGE | NOTIFY_REREAD | NOTIFY_GROUPING;
1062         if (vf->marks_enabled) interested |= NOTIFY_MARKS | NOTIFY_METADATA;
1063         /* FIXME: NOTIFY_METADATA should be checked by the keyword-to-mark functions and converted to NOTIFY_MARKS only if there was a change */
1064
1065         if (!(type & interested) || vf->refresh_idle_id || !vf->dir_fd) return;
1066
1067         refresh = (fd == vf->dir_fd);
1068
1069         if (!refresh)
1070                 {
1071                 gchar *base = remove_level_from_path(fd->path);
1072                 refresh = (strcmp(base, vf->dir_fd->path) == 0);
1073                 g_free(base);
1074                 }
1075
1076         if ((type & NOTIFY_CHANGE) && fd->change)
1077                 {
1078                 if (!refresh && fd->change->dest)
1079                         {
1080                         gchar *dest_base = remove_level_from_path(fd->change->dest);
1081                         refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
1082                         g_free(dest_base);
1083                         }
1084
1085                 if (!refresh && fd->change->source)
1086                         {
1087                         gchar *source_base = remove_level_from_path(fd->change->source);
1088                         refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
1089                         g_free(source_base);
1090                         }
1091                 }
1092
1093         if (refresh)
1094                 {
1095                 DEBUG_1("Notify vf: %s %04x", fd->path, type);
1096                 vf_refresh_idle(vf);
1097                 }
1098 }
1099
1100 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */