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