ef58bab001fb01f82ae371892890b91e0f1f4636
[dcpomatic.git] / src / wx / player_config_dialog.cc
1 /*
2     Copyright (C) 2012-2018 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 /** @file src/player_config_dialog.cc
22  *  @brief A dialogue to edit DCP-o-matic Player configuration.
23  */
24
25 #include "config_dialog.h"
26 #include "wx_util.h"
27 #include "editable_list.h"
28 #include "filter_dialog.h"
29 #include "file_picker_ctrl.h"
30 #include "dir_picker_ctrl.h"
31 #include "isdcf_metadata_dialog.h"
32 #include "server_dialog.h"
33 #include "make_chain_dialog.h"
34 #include "email_dialog.h"
35 #include "name_format_editor.h"
36 #include "nag_dialog.h"
37 #include "monitor_dialog.h"
38 #include "check_box.h"
39 #include "lib/config.h"
40 #include "lib/ratio.h"
41 #include "lib/filter.h"
42 #include "lib/dcp_content_type.h"
43 #include "lib/log.h"
44 #include "lib/util.h"
45 #include "lib/cross.h"
46 #include "lib/exceptions.h"
47 #include <dcp/locale_convert.h>
48 #include <dcp/exceptions.h>
49 #include <dcp/certificate_chain.h>
50 #include <wx/stdpaths.h>
51 #include <wx/preferences.h>
52 #include <wx/spinctrl.h>
53 #include <wx/filepicker.h>
54 #include <RtAudio.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/foreach.hpp>
57 #include <iostream>
58
59 using std::vector;
60 using std::string;
61 using std::list;
62 using std::cout;
63 using std::pair;
64 using std::make_pair;
65 using std::map;
66 using boost::bind;
67 using boost::shared_ptr;
68 using boost::function;
69 using boost::optional;
70 using dcp::locale_convert;
71
72 class PlayerGeneralPage : public GeneralPage
73 {
74 public:
75         PlayerGeneralPage (wxSize panel_size, int border)
76                 : GeneralPage (panel_size, border)
77         {}
78
79 private:
80         void setup ()
81         {
82                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
84
85                 int r = 0;
86                 add_language_controls (table, r);
87                 add_play_sound_controls (table, r);
88                 add_update_controls (table, r);
89
90                 add_label_to_sizer (table, _panel, _("Start player as"), true, wxGBPosition(r, 0));
91                 _player_mode = new wxChoice (_panel, wxID_ANY);
92                 _player_mode->Append (_("window"));
93                 _player_mode->Append (_("full screen"));
94                 _player_mode->Append (_("full screen with controls on other monitor"));
95                 table->Add (_player_mode, wxGBPosition(r, 1));
96                 ++r;
97
98                 add_label_to_sizer (table, _panel, _("Dual-screen displays"), true, wxGBPosition(r, 0));
99                 _image_display = new wxChoice (_panel, wxID_ANY);
100                 _image_display->Append (_("Image on primary, controls on secondary"));
101                 _image_display->Append (_("Image on secondary, controls on primary"));
102                 table->Add (_image_display, wxGBPosition(r, 1));
103                 ++r;
104
105                 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
106                 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
107                 ++r;
108
109                 add_label_to_sizer (table, _panel, _("Activity log file"), true, wxGBPosition (r, 0));
110                 _activity_log_file = new FilePickerCtrl (_panel, _("Select activity log file"), "*", true);
111                 table->Add (_activity_log_file, wxGBPosition(r, 1));
112                 ++r;
113
114                 add_label_to_sizer (table, _panel, _("Debug log file"), true, wxGBPosition (r, 0));
115                 _debug_log_file = new FilePickerCtrl (_panel, _("Select debug log file"), "*", true);
116                 table->Add (_debug_log_file, wxGBPosition(r, 1));
117                 ++r;
118
119 #ifdef DCPOMATIC_VARIANT_SWAROOP
120                 add_label_to_sizer (table, _panel, _("KDM server URL"), true, wxGBPosition(r, 0));
121                 _kdm_server_url = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
122                 table->Add (_kdm_server_url, wxGBPosition (r, 1));
123                 ++r;
124
125                 add_label_to_sizer (table, _panel, _("Lock file"), true, wxGBPosition(r, 0));
126                 _lock_file = new FilePickerCtrl (_panel, _("Select lock file"), "*", true);
127                 table->Add (_lock_file, wxGBPosition (r, 1));
128                 ++r;
129 #endif
130
131                 _player_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::player_mode_changed, this));
132                 _image_display->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::image_display_changed, this));
133                 _respect_kdm->Bind (wxEVT_CHECKBOX, bind(&PlayerGeneralPage::respect_kdm_changed, this));
134                 _activity_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::activity_log_file_changed, this));
135                 _debug_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::debug_log_file_changed, this));
136 #ifdef DCPOMATIC_VARIANT_SWAROOP
137                 _kdm_server_url->Bind (wxEVT_TEXT, bind(&PlayerGeneralPage::kdm_server_url_changed, this));
138                 _lock_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::lock_file_changed, this));
139 #endif
140         }
141
142         void config_changed ()
143         {
144                 GeneralPage::config_changed ();
145
146                 Config* config = Config::instance ();
147
148                 switch (config->player_mode()) {
149                 case Config::PLAYER_MODE_WINDOW:
150                         checked_set (_player_mode, 0);
151                         break;
152                 case Config::PLAYER_MODE_FULL:
153                         checked_set (_player_mode, 1);
154                         break;
155                 case Config::PLAYER_MODE_DUAL:
156                         checked_set (_player_mode, 2);
157                         break;
158                 }
159
160                 checked_set (_image_display, config->image_display());
161                 checked_set (_respect_kdm, config->respect_kdm_validity_periods());
162                 if (config->player_activity_log_file()) {
163                         checked_set (_activity_log_file, *config->player_activity_log_file());
164                 }
165                 if (config->player_debug_log_file()) {
166                         checked_set (_debug_log_file, *config->player_debug_log_file());
167                 }
168 #ifdef DCPOMATIC_VARIANT_SWAROOP
169                 checked_set (_kdm_server_url, config->kdm_server_url());
170                 if (config->player_lock_file()) {
171                         checked_set (_lock_file, config->player_lock_file().get());
172                 }
173 #endif
174         }
175
176 private:
177         void player_mode_changed ()
178         {
179                 switch (_player_mode->GetSelection()) {
180                 case 0:
181                         Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
182                         break;
183                 case 1:
184                         Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
185                         break;
186                 case 2:
187                         Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
188                         break;
189                 }
190         }
191
192         void image_display_changed ()
193         {
194                 Config::instance()->set_image_display(_image_display->GetSelection());
195         }
196
197         void respect_kdm_changed ()
198         {
199                 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
200         }
201
202         void activity_log_file_changed ()
203         {
204                 Config::instance()->set_player_activity_log_file(wx_to_std(_activity_log_file->GetPath()));
205         }
206
207         void debug_log_file_changed ()
208         {
209                 Config::instance()->set_player_debug_log_file(wx_to_std(_debug_log_file->GetPath()));
210         }
211
212 #ifdef DCPOMATIC_VARIANT_SWAROOP
213         void kdm_server_url_changed ()
214         {
215                 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
216         }
217
218         void lock_file_changed ()
219         {
220                 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
221         }
222 #endif
223
224         wxChoice* _player_mode;
225         wxChoice* _image_display;
226         wxCheckBox* _respect_kdm;
227         FilePickerCtrl* _activity_log_file;
228         FilePickerCtrl* _debug_log_file;
229 #ifdef DCPOMATIC_VARIANT_SWAROOP
230         wxTextCtrl* _kdm_server_url;
231         FilePickerCtrl* _lock_file;
232 #endif
233 };
234
235 class LocationsPage : public StandardPage
236 {
237 public:
238         LocationsPage (wxSize panel_size, int border)
239                 : StandardPage (panel_size, border)
240         {}
241
242         wxString GetName () const
243         {
244                 return _("Locations");
245         }
246
247 #ifdef DCPOMATIC_OSX
248         wxBitmap GetLargeIcon () const
249         {
250                 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
251         }
252 #endif
253
254 private:
255         void setup ()
256         {
257
258                 int r = 0;
259
260                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
261                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
262
263                 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
264                 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
265                 table->Add (_content_directory, wxGBPosition (r, 1));
266                 ++r;
267
268                 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
269                 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
270                 table->Add (_playlist_directory, wxGBPosition (r, 1));
271                 ++r;
272
273                 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
274                 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
275                 table->Add (_kdm_directory, wxGBPosition (r, 1));
276                 ++r;
277
278 #ifdef DCPOMATIC_VARIANT_SWAROOP
279                 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
280                 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true);
281                 table->Add (_background_image, wxGBPosition (r, 1));
282                 ++r;
283 #endif
284
285                 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
286                 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
287                 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
288 #ifdef DCPOMATIC_VARIANT_SWAROOP
289                 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
290 #endif
291         }
292
293         void config_changed ()
294         {
295                 Config* config = Config::instance ();
296
297                 if (config->player_content_directory()) {
298                         checked_set (_content_directory, *config->player_content_directory());
299                 }
300                 if (config->player_playlist_directory()) {
301                         checked_set (_playlist_directory, *config->player_playlist_directory());
302                 }
303                 if (config->player_kdm_directory()) {
304                         checked_set (_kdm_directory, *config->player_kdm_directory());
305                 }
306 #ifdef DCPOMATIC_VARIANT_SWAROOP
307                 if (config->player_background_image()) {
308                         checked_set (_background_image, *config->player_background_image());
309                 }
310 #endif
311         }
312
313         void content_directory_changed ()
314         {
315                 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
316         }
317
318         void playlist_directory_changed ()
319         {
320                 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
321         }
322
323         void kdm_directory_changed ()
324         {
325                 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
326         }
327
328 #ifdef DCPOMATIC_VARIANT_SWAROOP
329         void background_image_changed ()
330         {
331                 boost::filesystem::path const f = wx_to_std(_background_image->GetPath());
332                 if (!boost::filesystem::is_regular_file(f) || !wxImage::CanRead(std_to_wx(f.string()))) {
333                         error_dialog (0, _("Could not load image file."));
334                         if (Config::instance()->player_background_image()) {
335                                 checked_set (_background_image, *Config::instance()->player_background_image());
336                         }
337                         return;
338                 }
339
340                 Config::instance()->set_player_background_image(f);
341         }
342 #endif
343
344         wxDirPickerCtrl* _content_directory;
345         wxDirPickerCtrl* _playlist_directory;
346         wxDirPickerCtrl* _kdm_directory;
347 #ifdef DCPOMATIC_VARIANT_SWAROOP
348         FilePickerCtrl* _background_image;
349 #endif
350 };
351
352 #ifdef DCPOMATIC_VARIANT_SWAROOP
353 class WatermarkPage : public StandardPage
354 {
355 public:
356         WatermarkPage (wxSize panel_size, int border)
357                 : StandardPage (panel_size, border)
358         {}
359
360         wxString GetName () const
361         {
362                 return _("Watermark");
363         }
364
365 private:
366         void setup ()
367         {
368                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
369                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
370
371                 int r = 0;
372
373                 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
374                 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
375                 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
376                 ++r;
377
378                 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
379                 _period = new wxSpinCtrl (_panel, wxID_ANY);
380                 _period->SetRange (1, 60);
381                 table->Add (_period, wxGBPosition(r, 1));
382                 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
383                 ++r;
384
385                 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
386                 _duration = new wxSpinCtrl (_panel, wxID_ANY);
387                 _duration->SetRange (100, 5000);
388                 table->Add (_duration, wxGBPosition(r, 1));
389                 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
390                 ++r;
391
392                 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
393                 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
394                 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
395         }
396
397         void config_changed ()
398         {
399                 Config* config = Config::instance ();
400                 checked_set (_theatre, config->player_watermark_theatre());
401                 checked_set (_duration, config->player_watermark_duration());
402                 checked_set (_period, config->player_watermark_period());
403         }
404
405         void theatre_changed ()
406         {
407                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
408         }
409
410         void period_changed ()
411         {
412                 Config::instance()->set_player_watermark_period(_period->GetValue());
413         }
414
415         void duration_changed ()
416         {
417                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
418         }
419
420         wxTextCtrl* _theatre;
421         wxSpinCtrl* _period;
422         wxSpinCtrl* _duration;
423 };
424
425 class DevicesPage : public StandardPage
426 {
427 public:
428         DevicesPage (wxSize panel_size, int border)
429                 : StandardPage (panel_size, border)
430         {}
431
432         wxString GetName () const
433         {
434                 return _("Devices");
435         }
436
437 private:
438         void setup ()
439         {
440                 vector<string> columns;
441                 columns.push_back(wx_to_std(_("Manufacturer ID")));
442                 columns.push_back(wx_to_std(_("Product code")));
443                 columns.push_back(wx_to_std(_("Serial")));
444                 columns.push_back(wx_to_std(_("Manufacture week")));
445                 columns.push_back(wx_to_std(_("Manufacture year")));
446                 _monitor_list = new EditableList<Monitor, MonitorDialog> (
447                         _panel,
448                         columns,
449                         boost::bind (&Config::required_monitors, Config::instance()),
450                         boost::bind (&Config::set_required_monitors, Config::instance(), _1),
451                         boost::bind (&DevicesPage::monitor_column, this, _1, _2),
452                         true,
453                         true,
454                         100
455                         );
456                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
457
458                 wxButton* get = new Button(_panel, _("Read current devices"));
459                 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
460                 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
461         }
462
463         void get_clicked ()
464         {
465                 Config::instance()->set_required_monitors(get_monitors());
466                 _monitor_list->refresh ();
467         }
468
469         string monitor_column (Monitor m, int c)
470         {
471                 switch (c) {
472                 case 0:
473                         return m.manufacturer_id;
474                 case 1:
475                         return locale_convert<string>(m.manufacturer_product_code);
476                 case 2:
477                         return locale_convert<string>(m.serial_number);
478                 case 3:
479                         return locale_convert<string>(m.week_of_manufacture);
480                 case 4:
481                         return locale_convert<string>(m.year_of_manufacture);
482                 default:
483                         DCPOMATIC_ASSERT(false);
484                 }
485
486                 return "";
487         }
488
489         void config_changed ()
490         {
491                 _monitor_list->refresh ();
492         }
493
494 private:
495         EditableList<Monitor, MonitorDialog>* _monitor_list;
496 };
497
498 #endif
499
500 wxPreferencesEditor*
501 create_player_config_dialog ()
502 {
503         wxPreferencesEditor* e = new wxPreferencesEditor ();
504
505 #ifdef DCPOMATIC_OSX
506         /* Width that we force some of the config panels to be on OSX so that
507            the containing window doesn't shrink too much when we select those panels.
508            This is obviously an unpleasant hack.
509         */
510         wxSize ps = wxSize (520, -1);
511         int const border = 16;
512 #else
513         wxSize ps = wxSize (-1, -1);
514         int const border = 8;
515 #endif
516
517         e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
518         e->AddPage (new LocationsPage(ps, border));
519         e->AddPage (new KeysPage(ps, border));
520 #ifdef DCPOMATIC_VARIANT_SWAROOP
521         e->AddPage (new WatermarkPage(ps, border));
522         e->AddPage (new DevicesPage(ps, border));
523 #endif
524         return e;
525 }