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