00a74c65925b67fe25f8af93bb990100c117d087
[dcpomatic.git] / src / wx / player_config_dialog.cc
1 /*
2     Copyright (C) 2012-2019 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 "static_text.h"
40 #include "lib/config.h"
41 #include "lib/ratio.h"
42 #include "lib/filter.h"
43 #include "lib/dcp_content_type.h"
44 #include "lib/log.h"
45 #include "lib/util.h"
46 #include "lib/cross.h"
47 #include "lib/exceptions.h"
48 #include <dcp/locale_convert.h>
49 #include <dcp/exceptions.h>
50 #include <dcp/certificate_chain.h>
51 #include <wx/stdpaths.h>
52 #include <wx/preferences.h>
53 #include <wx/spinctrl.h>
54 #include <wx/filepicker.h>
55 #include <RtAudio.h>
56 #include <boost/filesystem.hpp>
57 #include <boost/foreach.hpp>
58 #include <iostream>
59
60 using std::vector;
61 using std::string;
62 using std::list;
63 using std::cout;
64 using std::pair;
65 using std::make_pair;
66 using std::map;
67 using boost::bind;
68 using boost::shared_ptr;
69 using boost::function;
70 using boost::optional;
71 using dcp::locale_convert;
72
73 class PlayerGeneralPage : public GeneralPage
74 {
75 public:
76         PlayerGeneralPage (wxSize panel_size, int border)
77                 : GeneralPage (panel_size, border)
78         {}
79
80 private:
81         void setup ()
82         {
83                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
84                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
85
86                 int r = 0;
87                 add_language_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                 add_label_to_sizer (table, _panel, _("Video display mode"), true, wxGBPosition(r, 0));
106                 _video_display_mode = new wxChoice (_panel, wxID_ANY);
107                 _video_display_mode->Append (_("Simple (safer)"));
108                 _video_display_mode->Append (_("OpenGL (faster)"));
109                 table->Add (_video_display_mode, wxGBPosition(r, 1));
110                 ++r;
111
112                 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false, wxGBPosition(r, 0));
113                 wxFont font = restart->GetFont();
114                 font.SetStyle (wxFONTSTYLE_ITALIC);
115                 font.SetPointSize (font.GetPointSize() - 1);
116                 restart->SetFont (font);
117                 ++r;
118
119                 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
120                 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
121                 ++r;
122
123                 add_label_to_sizer (table, _panel, _("Activity log file"), true, wxGBPosition (r, 0));
124                 _activity_log_file = new FilePickerCtrl (_panel, _("Select activity log file"), "*", true, true);
125                 table->Add (_activity_log_file, wxGBPosition(r, 1));
126                 ++r;
127
128                 add_label_to_sizer (table, _panel, _("Debug log file"), true, wxGBPosition (r, 0));
129                 _debug_log_file = new FilePickerCtrl (_panel, _("Select debug log file"), "*", true, true);
130                 table->Add (_debug_log_file, wxGBPosition(r, 1));
131                 ++r;
132
133 #ifdef DCPOMATIC_VARIANT_SWAROOP
134                 add_label_to_sizer (table, _panel, _("KDM server URL"), true, wxGBPosition(r, 0));
135                 _kdm_server_url = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
136                 table->Add (_kdm_server_url, wxGBPosition (r, 1));
137                 ++r;
138
139                 add_label_to_sizer (table, _panel, _("Lock file"), true, wxGBPosition(r, 0));
140                 _lock_file = new FilePickerCtrl (_panel, _("Select lock file"), "*", true, true);
141                 table->Add (_lock_file, wxGBPosition (r, 1));
142                 ++r;
143 #endif
144
145                 _player_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::player_mode_changed, this));
146                 _image_display->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::image_display_changed, this));
147                 _video_display_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::video_display_mode_changed, this));
148                 _respect_kdm->Bind (wxEVT_CHECKBOX, bind(&PlayerGeneralPage::respect_kdm_changed, this));
149                 _activity_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::activity_log_file_changed, this));
150                 _debug_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::debug_log_file_changed, this));
151 #ifdef DCPOMATIC_VARIANT_SWAROOP
152                 _kdm_server_url->Bind (wxEVT_TEXT, bind(&PlayerGeneralPage::kdm_server_url_changed, this));
153                 _lock_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::lock_file_changed, this));
154 #endif
155         }
156
157         void config_changed ()
158         {
159                 GeneralPage::config_changed ();
160
161                 Config* config = Config::instance ();
162
163                 switch (config->player_mode()) {
164                 case Config::PLAYER_MODE_WINDOW:
165                         checked_set (_player_mode, 0);
166                         break;
167                 case Config::PLAYER_MODE_FULL:
168                         checked_set (_player_mode, 1);
169                         break;
170                 case Config::PLAYER_MODE_DUAL:
171                         checked_set (_player_mode, 2);
172                         break;
173                 }
174
175                 switch (config->video_view_type()) {
176                 case Config::VIDEO_VIEW_SIMPLE:
177                         checked_set (_video_display_mode, 0);
178                         break;
179                 case Config::VIDEO_VIEW_OPENGL:
180                         checked_set (_video_display_mode, 1);
181                         break;
182                 }
183
184                 checked_set (_image_display, config->image_display());
185                 checked_set (_respect_kdm, config->respect_kdm_validity_periods());
186                 if (config->player_activity_log_file()) {
187                         checked_set (_activity_log_file, *config->player_activity_log_file());
188                 }
189                 if (config->player_debug_log_file()) {
190                         checked_set (_debug_log_file, *config->player_debug_log_file());
191                 }
192 #ifdef DCPOMATIC_VARIANT_SWAROOP
193                 checked_set (_kdm_server_url, config->kdm_server_url());
194                 if (config->player_lock_file()) {
195                         checked_set (_lock_file, config->player_lock_file().get());
196                 }
197 #endif
198         }
199
200 private:
201         void player_mode_changed ()
202         {
203                 switch (_player_mode->GetSelection()) {
204                 case 0:
205                         Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
206                         break;
207                 case 1:
208                         Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
209                         break;
210                 case 2:
211                         Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
212                         break;
213                 }
214         }
215
216         void image_display_changed ()
217         {
218                 Config::instance()->set_image_display(_image_display->GetSelection());
219         }
220
221         void video_display_mode_changed ()
222         {
223                 if (_video_display_mode->GetSelection() == 0) {
224                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
225                 } else {
226                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
227                 }
228         }
229
230         void respect_kdm_changed ()
231         {
232                 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
233         }
234
235         void activity_log_file_changed ()
236         {
237                 Config::instance()->set_player_activity_log_file(wx_to_std(_activity_log_file->GetPath()));
238         }
239
240         void debug_log_file_changed ()
241         {
242                 Config::instance()->set_player_debug_log_file(wx_to_std(_debug_log_file->GetPath()));
243         }
244
245 #ifdef DCPOMATIC_VARIANT_SWAROOP
246         void kdm_server_url_changed ()
247         {
248                 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
249         }
250
251         void lock_file_changed ()
252         {
253                 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
254         }
255 #endif
256
257         wxChoice* _player_mode;
258         wxChoice* _image_display;
259         wxChoice* _video_display_mode;
260         wxCheckBox* _respect_kdm;
261         FilePickerCtrl* _activity_log_file;
262         FilePickerCtrl* _debug_log_file;
263 #ifdef DCPOMATIC_VARIANT_SWAROOP
264         wxTextCtrl* _kdm_server_url;
265         FilePickerCtrl* _lock_file;
266 #endif
267 };
268
269
270 /** @class PlayerAdvancedPage
271  *  @brief Advanced page of the preferences dialog for the player.
272  */
273 class PlayerAdvancedPage : public Page
274 {
275 public:
276         PlayerAdvancedPage (wxSize panel_size, int border)
277                 : Page (panel_size, border)
278                 , _log_general (0)
279                 , _log_warning (0)
280                 , _log_error (0)
281                 , _log_timing (0)
282         {}
283
284         wxString GetName () const
285         {
286                 return _("Advanced");
287         }
288
289 #ifdef DCPOMATIC_OSX
290         wxBitmap GetLargeIcon () const
291         {
292                 return wxBitmap ("advanced", wxBITMAP_TYPE_PNG_RESOURCE);
293         }
294 #endif
295
296 private:
297         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
298         {
299                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
300 #ifdef __WXOSX__
301                 flags |= wxALIGN_RIGHT;
302                 text += wxT (":");
303 #endif
304                 wxStaticText* m = new StaticText (parent, text);
305                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
306         }
307
308         void setup ()
309         {
310                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
311                 table->AddGrowableCol (1, 1);
312                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
313
314                 {
315                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
316                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
317                         _log_general = new CheckBox (_panel, _("General"));
318                         t->Add (_log_general, 1, wxEXPAND | wxALL);
319                         _log_warning = new CheckBox (_panel, _("Warnings"));
320                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
321                         _log_error = new CheckBox (_panel, _("Errors"));
322                         t->Add (_log_error, 1, wxEXPAND | wxALL);
323                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
324                         _log_timing = new CheckBox (_panel, S_("Config|Timing"));
325                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
326                         table->Add (t, 0, wxALL, 6);
327                 }
328
329 #ifdef DCPOMATIC_WINDOWS
330                 _win32_console = new CheckBox (_panel, _("Open console window"));
331                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
332                 table->AddSpacer (0);
333 #endif
334
335                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
336                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
337                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
338                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
339 #ifdef DCPOMATIC_WINDOWS
340                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::win32_console_changed, this));
341 #endif
342         }
343
344         void config_changed ()
345         {
346                 Config* config = Config::instance ();
347
348                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
349                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
350                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
351                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
352 #ifdef DCPOMATIC_WINDOWS
353                 checked_set (_win32_console, config->win32_console());
354 #endif
355         }
356
357         void log_changed ()
358         {
359                 int types = 0;
360                 if (_log_general->GetValue ()) {
361                         types |= LogEntry::TYPE_GENERAL;
362                 }
363                 if (_log_warning->GetValue ()) {
364                         types |= LogEntry::TYPE_WARNING;
365                 }
366                 if (_log_error->GetValue ())  {
367                         types |= LogEntry::TYPE_ERROR;
368                 }
369                 if (_log_timing->GetValue ()) {
370                         types |= LogEntry::TYPE_TIMING;
371                 }
372                 Config::instance()->set_log_types (types);
373         }
374
375 #ifdef DCPOMATIC_WINDOWS
376         void win32_console_changed ()
377         {
378                 Config::instance()->set_win32_console (_win32_console->GetValue ());
379         }
380 #endif
381
382         wxCheckBox* _log_general;
383         wxCheckBox* _log_warning;
384         wxCheckBox* _log_error;
385         wxCheckBox* _log_timing;
386 #ifdef DCPOMATIC_WINDOWS
387         wxCheckBox* _win32_console;
388 #endif
389 };
390
391
392 #ifdef DCPOMATIC_VARIANT_SWAROOP
393 class WatermarkPage : public StandardPage
394 {
395 public:
396         WatermarkPage (wxSize panel_size, int border)
397                 : StandardPage (panel_size, border)
398         {}
399
400         wxString GetName () const
401         {
402                 return _("Watermark");
403         }
404
405 #ifdef DCPOMATIC_OSX
406         wxBitmap GetLargeIcon () const
407         {
408                 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
409                 return wxBitmap ("watermark", wxBITMAP_TYPE_PNG_RESOURCE);
410         }
411 #endif
412
413 private:
414         void setup ()
415         {
416                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
417                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
418
419                 int r = 0;
420
421                 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
422                 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
423                 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
424                 ++r;
425
426                 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
427                 _period = new wxSpinCtrl (_panel, wxID_ANY);
428                 _period->SetRange (1, 60);
429                 table->Add (_period, wxGBPosition(r, 1));
430                 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
431                 ++r;
432
433                 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
434                 _duration = new wxSpinCtrl (_panel, wxID_ANY);
435                 _duration->SetRange (100, 5000);
436                 table->Add (_duration, wxGBPosition(r, 1));
437                 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
438                 ++r;
439
440                 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
441                 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
442                 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
443         }
444
445         void config_changed ()
446         {
447                 Config* config = Config::instance ();
448                 checked_set (_theatre, config->player_watermark_theatre());
449                 checked_set (_duration, config->player_watermark_duration());
450                 checked_set (_period, config->player_watermark_period());
451         }
452
453         void theatre_changed ()
454         {
455                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
456         }
457
458         void period_changed ()
459         {
460                 Config::instance()->set_player_watermark_period(_period->GetValue());
461         }
462
463         void duration_changed ()
464         {
465                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
466         }
467
468         wxTextCtrl* _theatre;
469         wxSpinCtrl* _period;
470         wxSpinCtrl* _duration;
471 };
472
473 class DevicesPage : public StandardPage
474 {
475 public:
476         DevicesPage (wxSize panel_size, int border)
477                 : StandardPage (panel_size, border)
478         {}
479
480         wxString GetName () const
481         {
482                 return _("Devices");
483         }
484
485 #ifdef DCPOMATIC_OSX
486         wxBitmap GetLargeIcon () const
487         {
488                 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
489                 return wxBitmap ("devices", wxBITMAP_TYPE_PNG_RESOURCE);
490         }
491 #endif
492
493 private:
494         void setup ()
495         {
496                 vector<EditableListColumn> columns;
497                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacturer ID"))));
498                 columns.push_back(EditableListColumn(wx_to_std(_("Product code"))));
499                 columns.push_back(EditableListColumn(wx_to_std(_("Serial"))));
500                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture week"))));
501                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture year"))));
502                 _monitor_list = new EditableList<Monitor, MonitorDialog> (
503                         _panel,
504                         columns,
505                         boost::bind (&Config::required_monitors, Config::instance()),
506                         boost::bind (&Config::set_required_monitors, Config::instance(), _1),
507                         boost::bind (&DevicesPage::monitor_column, this, _1, _2),
508                         true,
509                         true
510                         );
511                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
512
513                 wxButton* get = new Button(_panel, _("Read current devices"));
514                 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
515                 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
516         }
517
518         void get_clicked ()
519         {
520                 Config::instance()->set_required_monitors(get_monitors());
521                 _monitor_list->refresh ();
522         }
523
524         string monitor_column (Monitor m, int c)
525         {
526                 switch (c) {
527                 case 0:
528                         return m.manufacturer_id;
529                 case 1:
530                         return locale_convert<string>(m.manufacturer_product_code);
531                 case 2:
532                         return locale_convert<string>(m.serial_number);
533                 case 3:
534                         return locale_convert<string>(m.week_of_manufacture);
535                 case 4:
536                         return locale_convert<string>(m.year_of_manufacture);
537                 default:
538                         DCPOMATIC_ASSERT(false);
539                 }
540
541                 return "";
542         }
543
544         void config_changed ()
545         {
546                 _monitor_list->refresh ();
547         }
548
549 private:
550         EditableList<Monitor, MonitorDialog>* _monitor_list;
551 };
552
553 #endif
554
555 wxPreferencesEditor*
556 create_player_config_dialog ()
557 {
558         wxPreferencesEditor* e = new wxPreferencesEditor (_("DCP-o-matic Player Preferences"));
559
560 #ifdef DCPOMATIC_OSX
561         /* Width that we force some of the config panels to be on OSX so that
562            the containing window doesn't shrink too much when we select those panels.
563            This is obviously an unpleasant hack.
564         */
565         wxSize ps = wxSize (520, -1);
566         int const border = 16;
567 #else
568         wxSize ps = wxSize (-1, -1);
569         int const border = 8;
570 #endif
571
572         e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
573         e->AddPage (new SoundPage(ps, border));
574         e->AddPage (new LocationsPage(ps, border));
575         e->AddPage (new KeysPage(ps, border));
576 #ifdef DCPOMATIC_VARIANT_SWAROOP
577         e->AddPage (new WatermarkPage(ps, border));
578         e->AddPage (new DevicesPage(ps, border));
579 #endif
580         e->AddPage (new PlayerAdvancedPage(ps, border));
581         return e;
582 }