2 Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
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.
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.
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/>.
21 /** @file src/player_config_dialog.cc
22 * @brief A dialogue to edit DCP-o-matic Player configuration.
25 #include "config_dialog.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"
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>
56 #include <boost/filesystem.hpp>
57 #include <boost/foreach.hpp>
68 using boost::shared_ptr;
69 using boost::function;
70 using boost::optional;
71 using dcp::locale_convert;
73 class PlayerGeneralPage : public GeneralPage
76 PlayerGeneralPage (wxSize panel_size, int border)
77 : GeneralPage (panel_size, border)
83 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
84 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
87 add_language_controls (table, r);
88 add_update_controls (table, r);
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));
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));
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));
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);
119 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
120 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
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));
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));
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));
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));
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));
157 void config_changed ()
159 GeneralPage::config_changed ();
161 Config* config = Config::instance ();
163 switch (config->player_mode()) {
164 case Config::PLAYER_MODE_WINDOW:
165 checked_set (_player_mode, 0);
167 case Config::PLAYER_MODE_FULL:
168 checked_set (_player_mode, 1);
170 case Config::PLAYER_MODE_DUAL:
171 checked_set (_player_mode, 2);
175 switch (config->video_view_type()) {
176 case Config::VIDEO_VIEW_SIMPLE:
177 checked_set (_video_display_mode, 0);
179 case Config::VIDEO_VIEW_OPENGL:
180 checked_set (_video_display_mode, 1);
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());
189 if (config->player_debug_log_file()) {
190 checked_set (_debug_log_file, *config->player_debug_log_file());
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());
201 void player_mode_changed ()
203 switch (_player_mode->GetSelection()) {
205 Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
208 Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
211 Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
216 void image_display_changed ()
218 Config::instance()->set_image_display(_image_display->GetSelection());
221 void video_display_mode_changed ()
223 if (_video_display_mode->GetSelection() == 0) {
224 Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
226 Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
230 void respect_kdm_changed ()
232 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
235 void activity_log_file_changed ()
237 Config::instance()->set_player_activity_log_file(wx_to_std(_activity_log_file->GetPath()));
240 void debug_log_file_changed ()
242 Config::instance()->set_player_debug_log_file(wx_to_std(_debug_log_file->GetPath()));
245 #ifdef DCPOMATIC_VARIANT_SWAROOP
246 void kdm_server_url_changed ()
248 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
251 void lock_file_changed ()
253 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
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;
269 class LocationsPage : public StandardPage
272 LocationsPage (wxSize panel_size, int border)
273 : StandardPage (panel_size, border)
276 wxString GetName () const
278 return _("Locations");
282 wxBitmap GetLargeIcon () const
284 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
294 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
295 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
297 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
298 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
299 table->Add (_content_directory, wxGBPosition (r, 1));
302 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
303 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
304 table->Add (_playlist_directory, wxGBPosition (r, 1));
307 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
308 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
309 table->Add (_kdm_directory, wxGBPosition (r, 1));
312 #ifdef DCPOMATIC_VARIANT_SWAROOP
313 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
314 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true, false);
315 table->Add (_background_image, wxGBPosition (r, 1));
319 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
320 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
321 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
322 #ifdef DCPOMATIC_VARIANT_SWAROOP
323 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
327 void config_changed ()
329 Config* config = Config::instance ();
331 if (config->player_content_directory()) {
332 checked_set (_content_directory, *config->player_content_directory());
334 if (config->player_playlist_directory()) {
335 checked_set (_playlist_directory, *config->player_playlist_directory());
337 if (config->player_kdm_directory()) {
338 checked_set (_kdm_directory, *config->player_kdm_directory());
340 #ifdef DCPOMATIC_VARIANT_SWAROOP
341 if (config->player_background_image()) {
342 checked_set (_background_image, *config->player_background_image());
347 void content_directory_changed ()
349 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
352 void playlist_directory_changed ()
354 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
357 void kdm_directory_changed ()
359 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
362 #ifdef DCPOMATIC_VARIANT_SWAROOP
363 void background_image_changed ()
365 boost::filesystem::path const f = wx_to_std(_background_image->GetPath());
366 if (!boost::filesystem::is_regular_file(f) || !wxImage::CanRead(std_to_wx(f.string()))) {
367 error_dialog (0, _("Could not load image file."));
368 if (Config::instance()->player_background_image()) {
369 checked_set (_background_image, *Config::instance()->player_background_image());
374 Config::instance()->set_player_background_image(f);
378 wxDirPickerCtrl* _content_directory;
379 wxDirPickerCtrl* _playlist_directory;
380 wxDirPickerCtrl* _kdm_directory;
381 #ifdef DCPOMATIC_VARIANT_SWAROOP
382 FilePickerCtrl* _background_image;
386 /** @class PlayerAdvancedPage
387 * @brief Advanced page of the preferences dialog for the player.
389 class PlayerAdvancedPage : public StockPage
392 PlayerAdvancedPage (wxSize panel_size, int border)
393 : StockPage (Kind_Advanced, panel_size, border)
398 , _log_debug_decode (0)
402 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
404 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
406 flags |= wxALIGN_RIGHT;
409 wxStaticText* m = new StaticText (parent, text);
410 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
415 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
416 table->AddGrowableCol (1, 1);
417 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
420 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
421 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
422 _log_general = new CheckBox (_panel, _("General"));
423 t->Add (_log_general, 1, wxEXPAND | wxALL);
424 _log_warning = new CheckBox (_panel, _("Warnings"));
425 t->Add (_log_warning, 1, wxEXPAND | wxALL);
426 _log_error = new CheckBox (_panel, _("Errors"));
427 t->Add (_log_error, 1, wxEXPAND | wxALL);
428 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
429 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
430 t->Add (_log_timing, 1, wxEXPAND | wxALL);
431 _log_debug_decode = new CheckBox (_panel, _("Debug"));
432 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
433 table->Add (t, 0, wxALL, 6);
436 #ifdef DCPOMATIC_WINDOWS
437 _win32_console = new CheckBox (_panel, _("Open console window"));
438 table->Add (_win32_console, 1, wxEXPAND | wxALL);
439 table->AddSpacer (0);
442 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
443 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
444 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
445 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
446 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
447 #ifdef DCPOMATIC_WINDOWS
448 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::win32_console_changed, this));
452 void config_changed ()
454 Config* config = Config::instance ();
456 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
457 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
458 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
459 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
460 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
461 #ifdef DCPOMATIC_WINDOWS
462 checked_set (_win32_console, config->win32_console());
469 if (_log_general->GetValue ()) {
470 types |= LogEntry::TYPE_GENERAL;
472 if (_log_warning->GetValue ()) {
473 types |= LogEntry::TYPE_WARNING;
475 if (_log_error->GetValue ()) {
476 types |= LogEntry::TYPE_ERROR;
478 if (_log_timing->GetValue ()) {
479 types |= LogEntry::TYPE_TIMING;
481 if (_log_debug_decode->GetValue ()) {
482 types |= LogEntry::TYPE_DEBUG_DECODE;
484 Config::instance()->set_log_types (types);
487 #ifdef DCPOMATIC_WINDOWS
488 void win32_console_changed ()
490 Config::instance()->set_win32_console (_win32_console->GetValue ());
494 wxCheckBox* _log_general;
495 wxCheckBox* _log_warning;
496 wxCheckBox* _log_error;
497 wxCheckBox* _log_timing;
498 wxCheckBox* _log_debug_decode;
499 #ifdef DCPOMATIC_WINDOWS
500 wxCheckBox* _win32_console;
505 #ifdef DCPOMATIC_VARIANT_SWAROOP
506 class WatermarkPage : public StandardPage
509 WatermarkPage (wxSize panel_size, int border)
510 : StandardPage (panel_size, border)
513 wxString GetName () const
515 return _("Watermark");
519 wxBitmap GetLargeIcon () const
521 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
522 return wxBitmap ("watermark", wxBITMAP_TYPE_PNG_RESOURCE);
529 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
530 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
534 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
535 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
536 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
539 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
540 _period = new wxSpinCtrl (_panel, wxID_ANY);
541 _period->SetRange (1, 60);
542 table->Add (_period, wxGBPosition(r, 1));
543 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
546 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
547 _duration = new wxSpinCtrl (_panel, wxID_ANY);
548 _duration->SetRange (100, 5000);
549 table->Add (_duration, wxGBPosition(r, 1));
550 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
553 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
554 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
555 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
558 void config_changed ()
560 Config* config = Config::instance ();
561 checked_set (_theatre, config->player_watermark_theatre());
562 checked_set (_duration, config->player_watermark_duration());
563 checked_set (_period, config->player_watermark_period());
566 void theatre_changed ()
568 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
571 void period_changed ()
573 Config::instance()->set_player_watermark_period(_period->GetValue());
576 void duration_changed ()
578 Config::instance()->set_player_watermark_duration(_duration->GetValue());
581 wxTextCtrl* _theatre;
583 wxSpinCtrl* _duration;
586 class DevicesPage : public StandardPage
589 DevicesPage (wxSize panel_size, int border)
590 : StandardPage (panel_size, border)
593 wxString GetName () const
599 wxBitmap GetLargeIcon () const
601 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
602 return wxBitmap ("devices", wxBITMAP_TYPE_PNG_RESOURCE);
609 vector<EditableListColumn> columns;
610 columns.push_back(EditableListColumn(wx_to_std(_("Manufacturer ID"))));
611 columns.push_back(EditableListColumn(wx_to_std(_("Product code"))));
612 columns.push_back(EditableListColumn(wx_to_std(_("Serial"))));
613 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture week"))));
614 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture year"))));
615 _monitor_list = new EditableList<Monitor, MonitorDialog> (
618 boost::bind (&Config::required_monitors, Config::instance()),
619 boost::bind (&Config::set_required_monitors, Config::instance(), _1),
620 boost::bind (&DevicesPage::monitor_column, this, _1, _2),
624 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
626 wxButton* get = new Button(_panel, _("Read current devices"));
627 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
628 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
633 Config::instance()->set_required_monitors(get_monitors());
634 _monitor_list->refresh ();
637 string monitor_column (Monitor m, int c)
641 return m.manufacturer_id;
643 return locale_convert<string>(m.manufacturer_product_code);
645 return locale_convert<string>(m.serial_number);
647 return locale_convert<string>(m.week_of_manufacture);
649 return locale_convert<string>(m.year_of_manufacture);
651 DCPOMATIC_ASSERT(false);
657 void config_changed ()
659 _monitor_list->refresh ();
663 EditableList<Monitor, MonitorDialog>* _monitor_list;
669 create_player_config_dialog ()
671 wxPreferencesEditor* e = new wxPreferencesEditor ();
674 /* Width that we force some of the config panels to be on OSX so that
675 the containing window doesn't shrink too much when we select those panels.
676 This is obviously an unpleasant hack.
678 wxSize ps = wxSize (520, -1);
679 int const border = 16;
681 wxSize ps = wxSize (-1, -1);
682 int const border = 8;
685 e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
686 e->AddPage (new SoundPage(ps, border));
687 e->AddPage (new LocationsPage(ps, border));
688 e->AddPage (new KeysPage(ps, border));
689 #ifdef DCPOMATIC_VARIANT_SWAROOP
690 e->AddPage (new WatermarkPage(ps, border));
691 e->AddPage (new DevicesPage(ps, border));
693 e->AddPage (new PlayerAdvancedPage(ps, border));