1/*-
2 * Copyright (c) 2013-2019 elementary, Inc. (https://elementary.io)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (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
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Corentin Noël <corentin@elementaryos.org>
18 */
19
20public class Audience.Widgets.SettingsPopover : Gtk.Popover {
21    public bool is_setup = false;
22
23    private Gtk.ComboBoxText languages;
24    private Gtk.ComboBoxText subtitles;
25    private Gtk.FileChooserButton external_subtitle_file;
26    private ClutterGst.Playback playback;
27
28    public SettingsPopover (ClutterGst.Playback playback) {
29        this.playback = playback;
30        opacity = GLOBAL_OPACITY;
31
32        languages = new Gtk.ComboBoxText ();
33        subtitles = new Gtk.ComboBoxText ();
34
35        var all_files_filter = new Gtk.FileFilter ();
36        all_files_filter.set_filter_name (_("All files"));
37        all_files_filter.add_pattern ("*");
38
39        var subtitle_files_filter = new Gtk.FileFilter ();
40        subtitle_files_filter.set_filter_name (_("Subtitle files"));
41        subtitle_files_filter.add_mime_type ("application/smil"); // .smi
42        subtitle_files_filter.add_mime_type ("application/x-subrip"); // .srt
43        subtitle_files_filter.add_mime_type ("text/x-microdvd"); // .sub
44        subtitle_files_filter.add_mime_type ("text/x-ssa"); // .ssa & .ass
45        // exclude .asc, mimetype is generic "application/pgp-encrypted"
46
47        external_subtitle_file = new Gtk.FileChooserButton (_("External Subtitles"), Gtk.FileChooserAction.OPEN);
48        external_subtitle_file.add_filter (subtitle_files_filter);
49        external_subtitle_file.add_filter (all_files_filter);
50
51        var lang_label = new Gtk.Label (_("Audio:"));
52        lang_label.halign = Gtk.Align.END;
53
54        var sub_label = new Gtk.Label (_("Subtitles:"));
55        sub_label.halign = Gtk.Align.END;
56
57        var sub_ext_label = new Gtk.Label (_("External Subtitles:"));
58        sub_ext_label.halign = Gtk.Align.END;
59
60        var setupgrid = new Gtk.Grid ();
61        setupgrid.row_spacing = 6;
62        setupgrid.margin = 6;
63        setupgrid.attach (lang_label, 0, 1, 1, 1);
64        setupgrid.attach (languages, 1, 1, 1, 1);
65        setupgrid.attach (sub_label, 0, 2, 1, 1);
66        setupgrid.attach (subtitles, 1, 2, 1, 1);
67        setupgrid.attach (sub_ext_label, 0, 3, 1, 1);
68        setupgrid.attach (external_subtitle_file, 1, 3, 1, 1);
69        setupgrid.column_spacing = 12;
70
71        external_subtitle_file.file_set.connect (() => {
72            App.get_instance ().mainwindow.player_page.set_subtitle (external_subtitle_file.get_uri ());
73        });
74
75        unowned Gst.Pipeline pipeline = playback.get_pipeline () as Gst.Pipeline;
76        /* playback.subtitle_uri does not seem to notify so connect directly to the pipeline */
77        pipeline.notify["suburi"].connect (() => {
78            /* Easier to retrieve the uri from the playback than the pipeline */
79            external_subtitle_file.select_uri (playback.subtitle_uri ?? "");
80        });
81
82        subtitles.changed.connect (on_subtitles_changed);
83
84        languages.changed.connect (on_languages_changed);
85
86        add (setupgrid);
87    }
88
89    public void setup () {
90        if (!is_setup) {
91            is_setup = true;
92            setup_text ();
93            setup_audio ();
94        }
95    }
96
97    private void on_subtitles_changed () {
98        if (subtitles.active < 0) {
99            return;
100        }
101
102        if (subtitles.active_id == "none") {
103            playback.subtitle_track = -1;
104        } else {
105            playback.subtitle_track = subtitles.active;
106        }
107    }
108
109    private void on_languages_changed () {
110        if (languages.active < 0 || languages.active_id == "def") {
111            return;
112        }
113
114        playback.audio_stream = languages.active;
115    }
116
117    private void setup_text () {
118        subtitles.changed.disconnect (on_subtitles_changed);
119
120        if (subtitles.model.iter_n_children (null) > 0) {
121            subtitles.remove_all ();
122        }
123
124        uint track = 1;
125        playback.get_subtitle_tracks ().foreach ((lang) => {
126            // FIXME: Using Track since lang is actually a bad pointer :/
127            subtitles.append (lang, _("Track %u").printf (track++));
128        });
129        subtitles.append ("none", _("None"));
130
131        int count = subtitles.model.iter_n_children (null);
132        subtitles.sensitive = count > 1;
133        if (subtitles.sensitive && (playback.subtitle_track >= 0)) {
134            subtitles.active = playback.subtitle_track;
135        } else {
136            subtitles.active = count - 1;
137        }
138
139        subtitles.changed.connect (on_subtitles_changed);
140    }
141
142    private void setup_audio () {
143        languages.changed.disconnect (on_languages_changed);
144
145        if (languages.model.iter_n_children (null) > 0) {
146            languages.remove_all ();
147        }
148
149        var languages_names = get_audio_track_names ();
150        uint track = 1;
151        playback.get_audio_streams ().foreach ((lang) => {
152            var audio_stream_lang = languages_names.nth_data (track - 1);
153            if (audio_stream_lang != null) {
154                languages.append (lang, audio_stream_lang);
155            } else {
156                languages.append (lang, _("Track %u").printf (track));
157            }
158            track++;
159        });
160
161        int count = languages.model.iter_n_children (null);
162        languages.sensitive = count > 1;
163        if (languages.sensitive) {
164            languages.active = playback.audio_stream;
165        } else {
166            if (count != 0) {
167                languages.remove_all ();
168            }
169            languages.append ("def", _("Default"));
170            languages.active = 0;
171        }
172
173        languages.changed.connect (on_languages_changed);
174    }
175
176    public void next_audio () {
177        setup ();
178        int count = languages.model.iter_n_children (null);
179        if (count > 0) {
180            languages.active = (languages.active + 1) % count;
181        }
182    }
183
184    public void next_text () {
185        setup ();
186        int count = subtitles.model.iter_n_children (null);
187        if (count > 0) {
188            subtitles.active = (subtitles.active + 1) % count;
189        }
190    }
191
192    private GLib.List<string?> get_audio_track_names () {
193        GLib.List<string?> audio_languages = null;
194
195        var discoverer_info = Audience.get_discoverer_info (playback.uri);
196        if (discoverer_info != null) {
197            var audio_streams = discoverer_info.get_audio_streams ();
198
199            foreach (var audio_stream in audio_streams) {
200                unowned string language_code = ((Gst.PbUtils.DiscovererAudioInfo)(audio_stream)).get_language ();
201                if (language_code != null) {
202                    var language_name = Gst.Tag.get_language_name (language_code);
203                    audio_languages.append (language_name);
204                } else {
205                    audio_languages.append (null);
206                }
207            }
208
209            // Both ClutterGst and DiscovererAudioInfo return tracks in opposite order.
210            audio_languages.reverse ();
211        }
212
213        return audio_languages;
214    }
215
216
217}
218