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