Extract common code out into kdm_for_screen()
[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 StockPage
274 {
275 public:
276         PlayerAdvancedPage (wxSize panel_size, int border)
277                 : StockPage (Kind_Advanced, panel_size, border)
278                 , _log_general (0)
279                 , _log_warning (0)
280                 , _log_error (0)
281                 , _log_timing (0)
282         {}
283
284 private:
285         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
286         {
287                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
288 #ifdef __WXOSX__
289                 flags |= wxALIGN_RIGHT;
290                 text += wxT (":");
291 #endif
292                 wxStaticText* m = new StaticText (parent, text);
293                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
294         }
295
296         void setup ()
297         {
298                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
299                 table->AddGrowableCol (1, 1);
300                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
301
302                 {
303                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
304                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
305                         _log_general = new CheckBox (_panel, _("General"));
306                         t->Add (_log_general, 1, wxEXPAND | wxALL);
307                         _log_warning = new CheckBox (_panel, _("Warnings"));
308                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
309                         _log_error = new CheckBox (_panel, _("Errors"));
310                         t->Add (_log_error, 1, wxEXPAND | wxALL);
311                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
312                         _log_timing = new CheckBox (_panel, S_("Config|Timing"));
313                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
314                         table->Add (t, 0, wxALL, 6);
315                 }
316
317 #ifdef DCPOMATIC_WINDOWS
318                 _win32_console = new CheckBox (_panel, _("Open console window"));
319                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
320                 table->AddSpacer (0);
321 #endif
322
323                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
324                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
325                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
326                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
327 #ifdef DCPOMATIC_WINDOWS
328                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::win32_console_changed, this));
329 #endif
330         }
331
332         void config_changed ()
333         {
334                 Config* config = Config::instance ();
335
336                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
337                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
338                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
339                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
340 #ifdef DCPOMATIC_WINDOWS
341                 checked_set (_win32_console, config->win32_console());
342 #endif
343         }
344
345         void log_changed ()
346         {
347                 int types = 0;
348                 if (_log_general->GetValue ()) {
349                         types |= LogEntry::TYPE_GENERAL;
350                 }
351                 if (_log_warning->GetValue ()) {
352                         types |= LogEntry::TYPE_WARNING;
353                 }
354                 if (_log_error->GetValue ())  {
355                         types |= LogEntry::TYPE_ERROR;
356                 }
357                 if (_log_timing->GetValue ()) {
358                         types |= LogEntry::TYPE_TIMING;
359                 }
360                 Config::instance()->set_log_types (types);
361         }
362
363 #ifdef DCPOMATIC_WINDOWS
364         void win32_console_changed ()
365         {
366                 Config::instance()->set_win32_console (_win32_console->GetValue ());
367         }
368 #endif
369
370         wxCheckBox* _log_general;
371         wxCheckBox* _log_warning;
372         wxCheckBox* _log_error;
373         wxCheckBox* _log_timing;
374 #ifdef DCPOMATIC_WINDOWS
375         wxCheckBox* _win32_console;
376 #endif
377 };
378
379
380 #ifdef DCPOMATIC_VARIANT_SWAROOP
381 class WatermarkPage : public StandardPage
382 {
383 public:
384         WatermarkPage (wxSize panel_size, int border)
385                 : StandardPage (panel_size, border)
386         {}
387
388         wxString GetName () const
389         {
390                 return _("Watermark");
391         }
392
393 #ifdef DCPOMATIC_OSX
394         wxBitmap GetLargeIcon () const
395         {
396                 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
397                 return wxBitmap ("watermark", wxBITMAP_TYPE_PNG_RESOURCE);
398         }
399 #endif
400
401 private:
402         void setup ()
403         {
404                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
405                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
406
407                 int r = 0;
408
409                 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
410                 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
411                 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
412                 ++r;
413
414                 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
415                 _period = new wxSpinCtrl (_panel, wxID_ANY);
416                 _period->SetRange (1, 60);
417                 table->Add (_period, wxGBPosition(r, 1));
418                 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
419                 ++r;
420
421                 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
422                 _duration = new wxSpinCtrl (_panel, wxID_ANY);
423                 _duration->SetRange (100, 5000);
424                 table->Add (_duration, wxGBPosition(r, 1));
425                 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
426                 ++r;
427
428                 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
429                 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
430                 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
431         }
432
433         void config_changed ()
434         {
435                 Config* config = Config::instance ();
436                 checked_set (_theatre, config->player_watermark_theatre());
437                 checked_set (_duration, config->player_watermark_duration());
438                 checked_set (_period, config->player_watermark_period());
439         }
440
441         void theatre_changed ()
442         {
443                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
444         }
445
446         void period_changed ()
447         {
448                 Config::instance()->set_player_watermark_period(_period->GetValue());
449         }
450
451         void duration_changed ()
452         {
453                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
454         }
455
456         wxTextCtrl* _theatre;
457         wxSpinCtrl* _period;
458         wxSpinCtrl* _duration;
459 };
460
461 class DevicesPage : public StandardPage
462 {
463 public:
464         DevicesPage (wxSize panel_size, int border)
465                 : StandardPage (panel_size, border)
466         {}
467
468         wxString GetName () const
469         {
470                 return _("Devices");
471         }
472
473 #ifdef DCPOMATIC_OSX
474         wxBitmap GetLargeIcon () const
475         {
476                 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
477                 return wxBitmap ("devices", wxBITMAP_TYPE_PNG_RESOURCE);
478         }
479 #endif
480
481 private:
482         void setup ()
483         {
484                 vector<EditableListColumn> columns;
485                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacturer ID"))));
486                 columns.push_back(EditableListColumn(wx_to_std(_("Product code"))));
487                 columns.push_back(EditableListColumn(wx_to_std(_("Serial"))));
488                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture week"))));
489                 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture year"))));
490                 _monitor_list = new EditableList<Monitor, MonitorDialog> (
491                         _panel,
492                         columns,
493                         boost::bind (&Config::required_monitors, Config::instance()),
494                         boost::bind (&Config::set_required_monitors, Config::instance(), _1),
495                         boost::bind (&DevicesPage::monitor_column, this, _1, _2),
496                         true,
497                         true
498                         );
499                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
500
501                 wxButton* get = new Button(_panel, _("Read current devices"));
502                 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
503                 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
504         }
505
506         void get_clicked ()
507         {
508                 Config::instance()->set_required_monitors(get_monitors());
509                 _monitor_list->refresh ();
510         }
511
512         string monitor_column (Monitor m, int c)
513         {
514                 switch (c) {
515                 case 0:
516                         return m.manufacturer_id;
517                 case 1:
518                         return locale_convert<string>(m.manufacturer_product_code);
519                 case 2:
520                         return locale_convert<string>(m.serial_number);
521                 case 3:
522                         return locale_convert<string>(m.week_of_manufacture);
523                 case 4:
524                         return locale_convert<string>(m.year_of_manufacture);
525                 default:
526                         DCPOMATIC_ASSERT(false);
527                 }
528
529                 return "";
530         }
531
532         void config_changed ()
533         {
534                 _monitor_list->refresh ();
535         }
536
537 private:
538         EditableList<Monitor, MonitorDialog>* _monitor_list;
539 };
540
541 #endif
542
543 wxPreferencesEditor*
544 create_player_config_dialog ()
545 {
546         wxPreferencesEditor* e = new wxPreferencesEditor (_("DCP-o-matic Player Preferences"));
547
548 #ifdef DCPOMATIC_OSX
549         /* Width that we force some of the config panels to be on OSX so that
550            the containing window doesn't shrink too much when we select those panels.
551            This is obviously an unpleasant hack.
552         */
553         wxSize ps = wxSize (520, -1);
554         int const border = 16;
555 #else
556         wxSize ps = wxSize (-1, -1);
557         int const border = 8;
558 #endif
559
560         e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
561         e->AddPage (new SoundPage(ps, border));
562         e->AddPage (new LocationsPage(ps, border));
563         e->AddPage (new KeysPage(ps, border));
564 #ifdef DCPOMATIC_VARIANT_SWAROOP
565         e->AddPage (new WatermarkPage(ps, border));
566         e->AddPage (new DevicesPage(ps, border));
567 #endif
568         e->AddPage (new PlayerAdvancedPage(ps, border));
569         return e;
570 }