1/*
2* Copyright (c) 2010-2013 Yorba Foundation
3*
4* This program is free software; you can redistribute it and/or
5* modify it under the terms of the GNU Lesser General Public
6* License as published by the Free Software Foundation; either
7* version 2.1 of the License, or (at your option) any later version.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12* General Public License for more details.
13*
14* You should have received a copy of the GNU General Public
15* License along with this program; if not, write to the
16* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17* Boston, MA 02110-1301 USA
18*/
19
20public class OfflinePage : CheckerboardPage {
21    public const string NAME = _ ("Missing Files");
22
23    private class OfflineView : Thumbnail {
24        public OfflineView (MediaSource source) {
25            base (source);
26
27            assert (source.is_offline ());
28        }
29    }
30
31    private class OfflineSearchViewFilter : DefaultSearchViewFilter {
32        public override uint get_criteria () {
33            return SearchFilterCriteria.TEXT | SearchFilterCriteria.FLAG |
34                   SearchFilterCriteria.MEDIA;
35        }
36    }
37
38    private OfflineSearchViewFilter search_filter = new OfflineSearchViewFilter ();
39    private MediaViewTracker tracker;
40    private Gtk.Menu page_context_menu;
41    private Gtk.Menu page_sidebar_menu;
42
43    public OfflinePage () {
44        base (NAME);
45
46        tracker = new MediaViewTracker (get_view ());
47
48        // monitor offline and initialize view with all items in it
49        LibraryPhoto.global.offline_contents_altered.connect (on_offline_contents_altered);
50        Video.global.offline_contents_altered.connect (on_offline_contents_altered);
51
52        on_offline_contents_altered (LibraryPhoto.global.get_offline_bin_contents (), null);
53        on_offline_contents_altered (Video.global.get_offline_bin_contents (), null);
54    }
55
56    ~OfflinePage () {
57        LibraryPhoto.global.offline_contents_altered.disconnect (on_offline_contents_altered);
58        Video.global.offline_contents_altered.disconnect (on_offline_contents_altered);
59    }
60
61    public override void add_toolbar_widgets (Gtk.ActionBar toolbar) {
62        var remove_button = new Gtk.Button.with_mnemonic (Resources.REMOVE_FROM_LIBRARY_MENU);
63        remove_button.margin_start = remove_button.margin_end = 3;
64        remove_button.tooltip_text = Resources.DELETE_FROM_LIBRARY_TOOLTIP;
65        var remove_tool = new Gtk.ToolItem ();
66        remove_tool.add (remove_button);
67        toolbar.pack_start (remove_tool);
68        var remove_action = get_action ("RemoveFromLibrary");
69        remove_action.bind_property ("sensitive", remove_button, "sensitive", BindingFlags.SYNC_CREATE);
70        remove_button.clicked.connect (() => remove_action.activate ());
71        base.add_toolbar_widgets (toolbar);
72    }
73
74    public override Gtk.Menu? get_page_sidebar_menu () {
75        if (page_sidebar_menu == null) {
76            page_sidebar_menu = new Gtk.Menu ();
77
78            var remove_menu_item = new Gtk.CheckMenuItem.with_mnemonic (Resources.REMOVE_FROM_LIBRARY_MENU);
79            var remove_action = get_action ("RemoveFromLibrary");
80            remove_action.bind_property ("sensitive", remove_menu_item, "sensitive", BindingFlags.SYNC_CREATE);
81            remove_menu_item.activate.connect (() => remove_action.activate ());
82
83            page_sidebar_menu.add (remove_menu_item);
84            page_sidebar_menu.show_all ();
85        }
86
87        return page_sidebar_menu;
88    }
89
90    public override Gtk.Menu? get_page_context_menu () {
91        if (page_context_menu == null) {
92            page_context_menu = new Gtk.Menu ();
93
94            var sidebar_menu_item = new Gtk.CheckMenuItem.with_mnemonic (_("S_idebar"));
95            var sidebar_action = get_common_action ("CommonDisplaySidebar");
96            sidebar_action.bind_property ("active", sidebar_menu_item, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
97
98            var metadata_menu_item = new Gtk.CheckMenuItem.with_mnemonic (_("Edit Photo In_fo"));
99            var metadata_action = get_common_action ("CommonDisplayMetadataSidebar");
100            metadata_action.bind_property ("active", metadata_menu_item, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
101
102            var sort_menu_item = new Gtk.MenuItem.with_mnemonic (_("Sort _Events"));
103
104            var ascending_menu_item = new Gtk.RadioMenuItem.with_mnemonic (null, _("_Ascending"));
105            var ascending_action = get_common_action ("CommonSortEventsAscending");
106            ascending_action.bind_property ("active", ascending_menu_item, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
107            ascending_menu_item.activate.connect (() => {
108                if (ascending_menu_item.active) {
109                    ascending_action.activate ();
110                }
111            });
112
113            var descending_menu_item = new Gtk.RadioMenuItem.with_mnemonic_from_widget (ascending_menu_item, _("D_escending"));
114            var descending_action = get_common_action ("CommonSortEventsDescending");
115            descending_action.bind_property ("active", descending_menu_item, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
116            descending_menu_item.activate.connect (() => {
117                if (descending_menu_item.active) {
118                    descending_action.activate ();
119                }
120            });
121
122            var sort_menu = new Gtk.Menu ();
123            sort_menu.add (ascending_menu_item);
124            sort_menu.add (descending_menu_item);
125            sort_menu_item.set_submenu (sort_menu);
126
127            var select_menu_item = new Gtk.MenuItem.with_mnemonic (Resources.SELECT_ALL_MENU);
128
129            var select_action = AppWindow.get_instance ().lookup_action (AppWindow.ACTION_SELECT_ALL);
130            select_action.bind_property ("enabled", select_menu_item, "sensitive", BindingFlags.SYNC_CREATE);
131            select_menu_item.activate.connect (() => select_action.activate (null));
132
133            page_context_menu.add (sidebar_menu_item);
134            page_context_menu.add (metadata_menu_item);
135            page_context_menu.add (new Gtk.SeparatorMenuItem ());
136            page_context_menu.add (sort_menu_item);
137            page_context_menu.add (new Gtk.SeparatorMenuItem ());
138            page_context_menu.add (select_menu_item);
139            page_context_menu.show_all ();
140        }
141
142        return page_context_menu;
143    }
144
145    protected override Gtk.ActionEntry[] init_collect_action_entries () {
146        Gtk.ActionEntry[] actions = base.init_collect_action_entries ();
147
148        Gtk.ActionEntry remove = { "RemoveFromLibrary", null, Resources.REMOVE_FROM_LIBRARY_MENU, "Delete",
149                                       Resources.DELETE_FROM_LIBRARY_TOOLTIP, on_remove_from_library
150                                     };
151        actions += remove;
152
153        return actions;
154    }
155
156    public override Core.ViewTracker? get_view_tracker () {
157        return tracker;
158    }
159
160    protected override void update_actions (int selected_count, int count) {
161        set_action_sensitive ("RemoveFromLibrary", selected_count > 0);
162
163        base.update_actions (selected_count, count);
164    }
165
166    private void on_offline_contents_altered (Gee.Collection<MediaSource>? added,
167            Gee.Collection<MediaSource>? removed) {
168        if (added != null) {
169            foreach (MediaSource source in added)
170                get_view ().add (new OfflineView (source));
171        }
172
173        if (removed != null) {
174            Marker marker = get_view ().start_marking ();
175            foreach (MediaSource source in removed)
176                marker.mark (get_view ().get_view_for_source (source));
177            get_view ().remove_marked (marker);
178        }
179    }
180
181    private void on_remove_from_library () {
182        Gee.Collection<MediaSource> sources =
183            (Gee.Collection<MediaSource>) get_view ().get_selected_sources ();
184        if (sources.size == 0)
185            return;
186
187        if (!remove_offline_dialog (AppWindow.get_instance (), sources.size))
188            return;
189
190        AppWindow.get_instance ().set_busy_cursor ();
191
192        ProgressDialog progress = null;
193        if (sources.size >= 20)
194            progress = new ProgressDialog (AppWindow.get_instance (), _ ("Deleting…"));
195
196        Gee.ArrayList<LibraryPhoto> photos = new Gee.ArrayList<LibraryPhoto> ();
197        Gee.ArrayList<Video> videos = new Gee.ArrayList<Video> ();
198        MediaSourceCollection.filter_media (sources, photos, videos);
199
200        if (progress != null) {
201            LibraryPhoto.global.remove_from_app (photos, false, progress.monitor);
202            Video.global.remove_from_app (videos, false, progress.monitor);
203        } else {
204            LibraryPhoto.global.remove_from_app (photos, false);
205            Video.global.remove_from_app (videos, false);
206        }
207
208        if (progress != null)
209            progress.close ();
210
211        AppWindow.get_instance ().set_normal_cursor ();
212    }
213
214    public override SearchViewFilter get_search_view_filter () {
215        return search_filter;
216    }
217}
218