Give player its own configuration dialogue.
[dcpomatic.git] / src / wx / config_dialog.h
1 /*
2     Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic 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     DCP-o-matic 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
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #ifndef DCPOMATIC_CONFIG_DIALOG_H
22 #define DCPOMATIC_CONFIG_DIALOG_H
23
24 #include "wx_util.h"
25 #include "editable_list.h"
26 #include "lib/config.h"
27 #include "lib/ratio.h"
28 #include "lib/filter.h"
29 #include "lib/dcp_content_type.h"
30 #include "lib/log.h"
31 #include "lib/util.h"
32 #include "lib/cross.h"
33 #include "lib/exceptions.h"
34 #include <dcp/locale_convert.h>
35 #include <dcp/exceptions.h>
36 #include <dcp/certificate_chain.h>
37 #include <wx/stdpaths.h>
38 #include <wx/preferences.h>
39 #include <wx/spinctrl.h>
40 #include <wx/filepicker.h>
41 #include <RtAudio.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/foreach.hpp>
44 #include <iostream>
45
46 class Page
47 {
48 public:
49         Page (wxSize panel_size, int border)
50                 : _border (border)
51                 , _panel (0)
52                 , _panel_size (panel_size)
53                 , _window_exists (false)
54         {
55                 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
56         }
57
58         virtual ~Page () {}
59
60 protected:
61         wxWindow* create_window (wxWindow* parent)
62         {
63                 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
64                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
65                 _panel->SetSizer (s);
66
67                 setup ();
68                 _window_exists = true;
69                 config_changed ();
70
71                 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
72
73                 return _panel;
74         }
75
76         int _border;
77         wxPanel* _panel;
78
79 private:
80         virtual void config_changed () = 0;
81         virtual void setup () = 0;
82
83         void config_changed_wrapper ()
84         {
85                 if (_window_exists) {
86                         config_changed ();
87                 }
88         }
89
90         void window_destroyed ()
91         {
92                 _window_exists = false;
93         }
94
95         wxSize _panel_size;
96         boost::signals2::scoped_connection _config_connection;
97         bool _window_exists;
98 };
99
100 class StockPage : public wxStockPreferencesPage, public Page
101 {
102 public:
103         StockPage (Kind kind, wxSize panel_size, int border)
104                 : wxStockPreferencesPage (kind)
105                 , Page (panel_size, border)
106         {}
107
108         wxWindow* CreateWindow (wxWindow* parent)
109         {
110                 return create_window (parent);
111         }
112 };
113
114 class StandardPage : public wxPreferencesPage, public Page
115 {
116 public:
117         StandardPage (wxSize panel_size, int border)
118                 : Page (panel_size, border)
119         {}
120
121         wxWindow* CreateWindow (wxWindow* parent)
122         {
123                 return create_window (parent);
124         }
125 };
126
127 class GeneralPage : public StockPage
128 {
129 public:
130         GeneralPage (wxSize panel_size, int border)
131                 : StockPage (Kind_General, panel_size, border)
132         {}
133
134 protected:
135         void add_language_controls (wxGridBagSizer* table, int& r)
136         {
137                 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
138                 table->Add (_set_language, wxGBPosition (r, 0));
139                 _language = new wxChoice (_panel, wxID_ANY);
140                 std::vector<std::pair<std::string, std::string> > languages;
141                 languages.push_back (std::make_pair ("Čeština", "cs_CZ"));
142                 languages.push_back (std::make_pair ("汉语/漢語", "zh_CN"));
143                 languages.push_back (std::make_pair ("Dansk", "da_DK"));
144                 languages.push_back (std::make_pair ("Deutsch", "de_DE"));
145                 languages.push_back (std::make_pair ("English", "en_GB"));
146                 languages.push_back (std::make_pair ("Español", "es_ES"));
147                 languages.push_back (std::make_pair ("Français", "fr_FR"));
148                 languages.push_back (std::make_pair ("Italiano", "it_IT"));
149                 languages.push_back (std::make_pair ("Nederlands", "nl_NL"));
150                 languages.push_back (std::make_pair ("Русский", "ru_RU"));
151                 languages.push_back (std::make_pair ("Polski", "pl_PL"));
152                 languages.push_back (std::make_pair ("Português europeu", "pt_PT"));
153                 languages.push_back (std::make_pair ("Português do Brasil", "pt_BR"));
154                 languages.push_back (std::make_pair ("Svenska", "sv_SE"));
155                 languages.push_back (std::make_pair ("Slovenský jazyk", "sk_SK"));
156                 languages.push_back (std::make_pair ("українська мова", "uk_UA"));
157                 checked_set (_language, languages);
158                 table->Add (_language, wxGBPosition (r, 1));
159                 ++r;
160
161                 wxStaticText* restart = add_label_to_sizer (
162                         table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
163                         );
164                 wxFont font = restart->GetFont();
165                 font.SetStyle (wxFONTSTYLE_ITALIC);
166                 font.SetPointSize (font.GetPointSize() - 1);
167                 restart->SetFont (font);
168                 ++r;
169
170                 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
171                 _language->Bind     (wxEVT_CHOICE,   boost::bind (&GeneralPage::language_changed,     this));
172         }
173
174         void add_play_sound_controls (wxGridBagSizer* table, int& r)
175         {
176                 _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via"));
177                 table->Add (_sound, wxGBPosition (r, 0));
178                 _sound_output = new wxChoice (_panel, wxID_ANY);
179                 table->Add (_sound_output, wxGBPosition (r, 1));
180                 ++r;
181
182                 RtAudio audio (DCPOMATIC_RTAUDIO_API);
183                 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
184                         RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
185                         if (dev.probed && dev.outputChannels > 0) {
186                                 _sound_output->Append (std_to_wx (dev.name));
187                         }
188                 }
189
190                 _sound->Bind        (wxEVT_CHECKBOX, boost::bind (&GeneralPage::sound_changed, this));
191                 _sound_output->Bind (wxEVT_CHOICE,   boost::bind (&GeneralPage::sound_output_changed, this));
192         }
193
194         void add_update_controls (wxGridBagSizer* table, int& r)
195         {
196                 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
197                 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
198                 ++r;
199
200                 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
201                 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
202                 ++r;
203
204                 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
205                 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
206         }
207
208         virtual void config_changed ()
209         {
210                 Config* config = Config::instance ();
211
212                 checked_set (_set_language, static_cast<bool>(config->language()));
213
214                 /* Backwards compatibility of config file */
215
216                 std::map<std::string, std::string> compat_map;
217                 compat_map["fr"] = "fr_FR";
218                 compat_map["it"] = "it_IT";
219                 compat_map["es"] = "es_ES";
220                 compat_map["sv"] = "sv_SE";
221                 compat_map["de"] = "de_DE";
222                 compat_map["nl"] = "nl_NL";
223                 compat_map["ru"] = "ru_RU";
224                 compat_map["pl"] = "pl_PL";
225                 compat_map["da"] = "da_DK";
226                 compat_map["pt"] = "pt_PT";
227                 compat_map["sk"] = "sk_SK";
228                 compat_map["cs"] = "cs_CZ";
229                 compat_map["uk"] = "uk_UA";
230
231                 std::string lang = config->language().get_value_or ("en_GB");
232                 if (compat_map.find (lang) != compat_map.end ()) {
233                         lang = compat_map[lang];
234                 }
235
236                 checked_set (_language, lang);
237
238                 checked_set (_check_for_updates, config->check_for_updates ());
239                 checked_set (_check_for_test_updates, config->check_for_test_updates ());
240
241                 checked_set (_sound, config->sound ());
242
243                 boost::optional<std::string> const current_so = get_sound_output ();
244                 boost::optional<std::string> configured_so;
245
246                 if (config->sound_output()) {
247                         configured_so = config->sound_output().get();
248                 } else {
249                         /* No configured output means we should use the default */
250                         RtAudio audio (DCPOMATIC_RTAUDIO_API);
251                         try {
252                                 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
253                         } catch (RtAudioError& e) {
254                                 /* Probably no audio devices at all */
255                         }
256                 }
257
258                 if (configured_so && current_so != configured_so) {
259                         /* Update _sound_output with the configured value */
260                         unsigned int i = 0;
261                         while (i < _sound_output->GetCount()) {
262                                 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
263                                         _sound_output->SetSelection (i);
264                                         break;
265                                 }
266                                 ++i;
267                         }
268                 }
269
270                 setup_sensitivity ();
271         }
272
273 private:
274         void setup_sensitivity ()
275         {
276                 _language->Enable (_set_language->GetValue ());
277                 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
278                 _sound_output->Enable (_sound->GetValue ());
279         }
280
281         /** @return Currently-selected preview sound output in the dialogue */
282         boost::optional<std::string> get_sound_output ()
283         {
284                 int const sel = _sound_output->GetSelection ();
285                 if (sel == wxNOT_FOUND) {
286                         return boost::optional<std::string> ();
287                 }
288
289                 return wx_to_std (_sound_output->GetString (sel));
290         }
291
292         void set_language_changed ()
293         {
294                 setup_sensitivity ();
295                 if (_set_language->GetValue ()) {
296                         language_changed ();
297                 } else {
298                         Config::instance()->unset_language ();
299                 }
300         }
301
302         void language_changed ()
303         {
304                 int const sel = _language->GetSelection ();
305                 if (sel != -1) {
306                         Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
307                 } else {
308                         Config::instance()->unset_language ();
309                 }
310         }
311
312         void check_for_updates_changed ()
313         {
314                 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
315         }
316
317         void check_for_test_updates_changed ()
318         {
319                 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
320         }
321
322         void sound_changed ()
323         {
324                 Config::instance()->set_sound (_sound->GetValue ());
325         }
326
327         void sound_output_changed ()
328         {
329                 RtAudio audio (DCPOMATIC_RTAUDIO_API);
330                 boost::optional<std::string> const so = get_sound_output();
331                 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
332                         Config::instance()->unset_sound_output ();
333                 } else {
334                         Config::instance()->set_sound_output (*so);
335                 }
336         }
337
338         wxCheckBox* _set_language;
339         wxChoice* _language;
340         wxCheckBox* _sound;
341         wxChoice* _sound_output;
342         wxCheckBox* _check_for_updates;
343         wxCheckBox* _check_for_test_updates;
344 };
345
346 #endif