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