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_play_sound_controls (table, r);
89 add_update_controls (table, r);
91 add_label_to_sizer (table, _panel, _("Start player as"), true, wxGBPosition(r, 0));
92 _player_mode = new wxChoice (_panel, wxID_ANY);
93 _player_mode->Append (_("window"));
94 _player_mode->Append (_("full screen"));
95 _player_mode->Append (_("full screen with controls on other monitor"));
96 table->Add (_player_mode, wxGBPosition(r, 1));
99 add_label_to_sizer (table, _panel, _("Dual-screen displays"), true, wxGBPosition(r, 0));
100 _image_display = new wxChoice (_panel, wxID_ANY);
101 _image_display->Append (_("Image on primary, controls on secondary"));
102 _image_display->Append (_("Image on secondary, controls on primary"));
103 table->Add (_image_display, wxGBPosition(r, 1));
106 add_label_to_sizer (table, _panel, _("Video display mode"), true, wxGBPosition(r, 0));
107 _video_display_mode = new wxChoice (_panel, wxID_ANY);
108 _video_display_mode->Append (_("Simple (safer)"));
109 _video_display_mode->Append (_("OpenGL (faster)"));
110 table->Add (_video_display_mode, wxGBPosition(r, 1));
113 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false, wxGBPosition(r, 0));
114 wxFont font = restart->GetFont();
115 font.SetStyle (wxFONTSTYLE_ITALIC);
116 font.SetPointSize (font.GetPointSize() - 1);
117 restart->SetFont (font);
120 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
121 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
124 add_label_to_sizer (table, _panel, _("Activity log file"), true, wxGBPosition (r, 0));
125 _activity_log_file = new FilePickerCtrl (_panel, _("Select activity log file"), "*", true, true);
126 table->Add (_activity_log_file, wxGBPosition(r, 1));
129 add_label_to_sizer (table, _panel, _("Debug log file"), true, wxGBPosition (r, 0));
130 _debug_log_file = new FilePickerCtrl (_panel, _("Select debug log file"), "*", true, true);
131 table->Add (_debug_log_file, wxGBPosition(r, 1));
134 #ifdef DCPOMATIC_VARIANT_SWAROOP
135 add_label_to_sizer (table, _panel, _("KDM server URL"), true, wxGBPosition(r, 0));
136 _kdm_server_url = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
137 table->Add (_kdm_server_url, wxGBPosition (r, 1));
140 add_label_to_sizer (table, _panel, _("Lock file"), true, wxGBPosition(r, 0));
141 _lock_file = new FilePickerCtrl (_panel, _("Select lock file"), "*", true, true);
142 table->Add (_lock_file, wxGBPosition (r, 1));
146 _player_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::player_mode_changed, this));
147 _image_display->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::image_display_changed, this));
148 _video_display_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::video_display_mode_changed, this));
149 _respect_kdm->Bind (wxEVT_CHECKBOX, bind(&PlayerGeneralPage::respect_kdm_changed, this));
150 _activity_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::activity_log_file_changed, this));
151 _debug_log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::debug_log_file_changed, this));
152 #ifdef DCPOMATIC_VARIANT_SWAROOP
153 _kdm_server_url->Bind (wxEVT_TEXT, bind(&PlayerGeneralPage::kdm_server_url_changed, this));
154 _lock_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::lock_file_changed, this));
158 void config_changed ()
160 GeneralPage::config_changed ();
162 Config* config = Config::instance ();
164 switch (config->player_mode()) {
165 case Config::PLAYER_MODE_WINDOW:
166 checked_set (_player_mode, 0);
168 case Config::PLAYER_MODE_FULL:
169 checked_set (_player_mode, 1);
171 case Config::PLAYER_MODE_DUAL:
172 checked_set (_player_mode, 2);
176 switch (config->video_view_type()) {
177 case Config::VIDEO_VIEW_SIMPLE:
178 checked_set (_video_display_mode, 0);
180 case Config::VIDEO_VIEW_OPENGL:
181 checked_set (_video_display_mode, 1);
185 checked_set (_image_display, config->image_display());
186 checked_set (_respect_kdm, config->respect_kdm_validity_periods());
187 if (config->player_activity_log_file()) {
188 checked_set (_activity_log_file, *config->player_activity_log_file());
190 if (config->player_debug_log_file()) {
191 checked_set (_debug_log_file, *config->player_debug_log_file());
193 #ifdef DCPOMATIC_VARIANT_SWAROOP
194 checked_set (_kdm_server_url, config->kdm_server_url());
195 if (config->player_lock_file()) {
196 checked_set (_lock_file, config->player_lock_file().get());
202 void player_mode_changed ()
204 switch (_player_mode->GetSelection()) {
206 Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
209 Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
212 Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
217 void image_display_changed ()
219 Config::instance()->set_image_display(_image_display->GetSelection());
222 void video_display_mode_changed ()
224 if (_video_display_mode->GetSelection() == 0) {
225 Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
227 Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
231 void respect_kdm_changed ()
233 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
236 void activity_log_file_changed ()
238 Config::instance()->set_player_activity_log_file(wx_to_std(_activity_log_file->GetPath()));
241 void debug_log_file_changed ()
243 Config::instance()->set_player_debug_log_file(wx_to_std(_debug_log_file->GetPath()));
246 #ifdef DCPOMATIC_VARIANT_SWAROOP
247 void kdm_server_url_changed ()
249 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
252 void lock_file_changed ()
254 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
258 wxChoice* _player_mode;
259 wxChoice* _image_display;
260 wxChoice* _video_display_mode;
261 wxCheckBox* _respect_kdm;
262 FilePickerCtrl* _activity_log_file;
263 FilePickerCtrl* _debug_log_file;
264 #ifdef DCPOMATIC_VARIANT_SWAROOP
265 wxTextCtrl* _kdm_server_url;
266 FilePickerCtrl* _lock_file;
270 class LocationsPage : public StandardPage
273 LocationsPage (wxSize panel_size, int border)
274 : StandardPage (panel_size, border)
277 wxString GetName () const
279 return _("Locations");
283 wxBitmap GetLargeIcon () const
285 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
295 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
296 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
298 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
299 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
300 table->Add (_content_directory, wxGBPosition (r, 1));
303 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
304 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
305 table->Add (_playlist_directory, wxGBPosition (r, 1));
308 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
309 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
310 table->Add (_kdm_directory, wxGBPosition (r, 1));
313 #ifdef DCPOMATIC_VARIANT_SWAROOP
314 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
315 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true);
316 table->Add (_background_image, wxGBPosition (r, 1));
320 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
321 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
322 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
323 #ifdef DCPOMATIC_VARIANT_SWAROOP
324 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
328 void config_changed ()
330 Config* config = Config::instance ();
332 if (config->player_content_directory()) {
333 checked_set (_content_directory, *config->player_content_directory());
335 if (config->player_playlist_directory()) {
336 checked_set (_playlist_directory, *config->player_playlist_directory());
338 if (config->player_kdm_directory()) {
339 checked_set (_kdm_directory, *config->player_kdm_directory());
341 #ifdef DCPOMATIC_VARIANT_SWAROOP
342 if (config->player_background_image()) {
343 checked_set (_background_image, *config->player_background_image());
348 void content_directory_changed ()
350 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
353 void playlist_directory_changed ()
355 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
358 void kdm_directory_changed ()
360 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
363 #ifdef DCPOMATIC_VARIANT_SWAROOP
364 void background_image_changed ()
366 boost::filesystem::path const f = wx_to_std(_background_image->GetPath());
367 if (!boost::filesystem::is_regular_file(f) || !wxImage::CanRead(std_to_wx(f.string()))) {
368 error_dialog (0, _("Could not load image file."));
369 if (Config::instance()->player_background_image()) {
370 checked_set (_background_image, *Config::instance()->player_background_image());
375 Config::instance()->set_player_background_image(f);
379 wxDirPickerCtrl* _content_directory;
380 wxDirPickerCtrl* _playlist_directory;
381 wxDirPickerCtrl* _kdm_directory;
382 #ifdef DCPOMATIC_VARIANT_SWAROOP
383 FilePickerCtrl* _background_image;
387 /** @class PlayerAdvancedPage
388 * @brief Advanced page of the preferences dialog for the player.
390 class PlayerAdvancedPage : public StockPage
393 PlayerAdvancedPage (wxSize panel_size, int border)
394 : StockPage (Kind_Advanced, panel_size, border)
399 , _log_debug_decode (0)
403 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
405 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
407 flags |= wxALIGN_RIGHT;
410 wxStaticText* m = new StaticText (parent, text);
411 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
416 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
417 table->AddGrowableCol (1, 1);
418 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
421 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
422 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
423 _log_general = new CheckBox (_panel, _("General"));
424 t->Add (_log_general, 1, wxEXPAND | wxALL);
425 _log_warning = new CheckBox (_panel, _("Warnings"));
426 t->Add (_log_warning, 1, wxEXPAND | wxALL);
427 _log_error = new CheckBox (_panel, _("Errors"));
428 t->Add (_log_error, 1, wxEXPAND | wxALL);
429 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
430 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
431 t->Add (_log_timing, 1, wxEXPAND | wxALL);
432 _log_debug_decode = new CheckBox (_panel, _("Debug"));
433 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
434 table->Add (t, 0, wxALL, 6);
437 #ifdef DCPOMATIC_WINDOWS
438 _win32_console = new CheckBox (_panel, _("Open console window"));
439 table->Add (_win32_console, 1, wxEXPAND | wxALL);
440 table->AddSpacer (0);
443 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
444 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
445 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
446 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
447 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::log_changed, this));
448 #ifdef DCPOMATIC_WINDOWS
449 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&PlayerAdvancedPage::win32_console_changed, this));
453 void config_changed ()
455 Config* config = Config::instance ();
457 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
458 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
459 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
460 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
461 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
462 #ifdef DCPOMATIC_WINDOWS
463 checked_set (_win32_console, config->win32_console());
470 if (_log_general->GetValue ()) {
471 types |= LogEntry::TYPE_GENERAL;
473 if (_log_warning->GetValue ()) {
474 types |= LogEntry::TYPE_WARNING;
476 if (_log_error->GetValue ()) {
477 types |= LogEntry::TYPE_ERROR;
479 if (_log_timing->GetValue ()) {
480 types |= LogEntry::TYPE_TIMING;
482 if (_log_debug_decode->GetValue ()) {
483 types |= LogEntry::TYPE_DEBUG_DECODE;
485 Config::instance()->set_log_types (types);
488 #ifdef DCPOMATIC_WINDOWS
489 void win32_console_changed ()
491 Config::instance()->set_win32_console (_win32_console->GetValue ());
495 wxCheckBox* _log_general;
496 wxCheckBox* _log_warning;
497 wxCheckBox* _log_error;
498 wxCheckBox* _log_timing;
499 wxCheckBox* _log_debug_decode;
500 #ifdef DCPOMATIC_WINDOWS
501 wxCheckBox* _win32_console;
506 #ifdef DCPOMATIC_VARIANT_SWAROOP
507 class WatermarkPage : public StandardPage
510 WatermarkPage (wxSize panel_size, int border)
511 : StandardPage (panel_size, border)
514 wxString GetName () const
516 return _("Watermark");
520 wxBitmap GetLargeIcon () const
522 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
523 return wxBitmap ("watermark", wxBITMAP_TYPE_PNG_RESOURCE);
530 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
531 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
535 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
536 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
537 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
540 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
541 _period = new wxSpinCtrl (_panel, wxID_ANY);
542 _period->SetRange (1, 60);
543 table->Add (_period, wxGBPosition(r, 1));
544 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
547 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
548 _duration = new wxSpinCtrl (_panel, wxID_ANY);
549 _duration->SetRange (100, 5000);
550 table->Add (_duration, wxGBPosition(r, 1));
551 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
554 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
555 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
556 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
559 void config_changed ()
561 Config* config = Config::instance ();
562 checked_set (_theatre, config->player_watermark_theatre());
563 checked_set (_duration, config->player_watermark_duration());
564 checked_set (_period, config->player_watermark_period());
567 void theatre_changed ()
569 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
572 void period_changed ()
574 Config::instance()->set_player_watermark_period(_period->GetValue());
577 void duration_changed ()
579 Config::instance()->set_player_watermark_duration(_duration->GetValue());
582 wxTextCtrl* _theatre;
584 wxSpinCtrl* _duration;
587 class DevicesPage : public StandardPage
590 DevicesPage (wxSize panel_size, int border)
591 : StandardPage (panel_size, border)
594 wxString GetName () const
600 wxBitmap GetLargeIcon () const
602 /* XXX: this icon doesn't exist; this is just to make the swaroop variant build on OS X */
603 return wxBitmap ("devices", wxBITMAP_TYPE_PNG_RESOURCE);
610 vector<EditableListColumn> columns;
611 columns.push_back(EditableListColumn(wx_to_std(_("Manufacturer ID"))));
612 columns.push_back(EditableListColumn(wx_to_std(_("Product code"))));
613 columns.push_back(EditableListColumn(wx_to_std(_("Serial"))));
614 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture week"))));
615 columns.push_back(EditableListColumn(wx_to_std(_("Manufacture year"))));
616 _monitor_list = new EditableList<Monitor, MonitorDialog> (
619 boost::bind (&Config::required_monitors, Config::instance()),
620 boost::bind (&Config::set_required_monitors, Config::instance(), _1),
621 boost::bind (&DevicesPage::monitor_column, this, _1, _2),
625 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
627 wxButton* get = new Button(_panel, _("Read current devices"));
628 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
629 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
634 Config::instance()->set_required_monitors(get_monitors());
635 _monitor_list->refresh ();
638 string monitor_column (Monitor m, int c)
642 return m.manufacturer_id;
644 return locale_convert<string>(m.manufacturer_product_code);
646 return locale_convert<string>(m.serial_number);
648 return locale_convert<string>(m.week_of_manufacture);
650 return locale_convert<string>(m.year_of_manufacture);
652 DCPOMATIC_ASSERT(false);
658 void config_changed ()
660 _monitor_list->refresh ();
664 EditableList<Monitor, MonitorDialog>* _monitor_list;
670 create_player_config_dialog ()
672 wxPreferencesEditor* e = new wxPreferencesEditor ();
675 /* Width that we force some of the config panels to be on OSX so that
676 the containing window doesn't shrink too much when we select those panels.
677 This is obviously an unpleasant hack.
679 wxSize ps = wxSize (520, -1);
680 int const border = 16;
682 wxSize ps = wxSize (-1, -1);
683 int const border = 8;
686 e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), 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));