2 Copyright (C) 2012-2018 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 "lib/config.h"
40 #include "lib/ratio.h"
41 #include "lib/filter.h"
42 #include "lib/dcp_content_type.h"
45 #include "lib/cross.h"
46 #include "lib/exceptions.h"
47 #include <dcp/locale_convert.h>
48 #include <dcp/exceptions.h>
49 #include <dcp/certificate_chain.h>
50 #include <wx/stdpaths.h>
51 #include <wx/preferences.h>
52 #include <wx/spinctrl.h>
53 #include <wx/filepicker.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/foreach.hpp>
67 using boost::shared_ptr;
68 using boost::function;
69 using boost::optional;
70 using dcp::locale_convert;
72 class PlayerGeneralPage : public GeneralPage
75 PlayerGeneralPage (wxSize panel_size, int border)
76 : GeneralPage (panel_size, border)
82 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
86 add_language_controls (table, r);
87 add_play_sound_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 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
106 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
109 add_label_to_sizer (table, _panel, _("Log file"), true, wxGBPosition (r, 0));
110 _log_file = new FilePickerCtrl (_panel, _("Select log file"), "*", true);
111 table->Add (_log_file, wxGBPosition (r, 1));
114 #ifdef DCPOMATIC_VARIANT_SWAROOP
115 add_label_to_sizer (table, _panel, _("KDM server URL"), true, wxGBPosition(r, 0));
116 _kdm_server_url = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
117 table->Add (_kdm_server_url, wxGBPosition (r, 1));
120 add_label_to_sizer (table, _panel, _("Lock file"), true, wxGBPosition(r, 0));
121 _lock_file = new FilePickerCtrl (_panel, _("Select lock file"), "*", true);
122 table->Add (_lock_file, wxGBPosition (r, 1));
126 _player_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::player_mode_changed, this));
127 _image_display->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::image_display_changed, this));
128 _respect_kdm->Bind (wxEVT_CHECKBOX, bind(&PlayerGeneralPage::respect_kdm_changed, this));
129 _log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::log_file_changed, this));
130 #ifdef DCPOMATIC_VARIANT_SWAROOP
131 _kdm_server_url->Bind (wxEVT_TEXT, bind(&PlayerGeneralPage::kdm_server_url_changed, this));
132 _lock_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::lock_file_changed, this));
136 void config_changed ()
138 GeneralPage::config_changed ();
140 Config* config = Config::instance ();
142 switch (config->player_mode()) {
143 case Config::PLAYER_MODE_WINDOW:
144 checked_set (_player_mode, 0);
146 case Config::PLAYER_MODE_FULL:
147 checked_set (_player_mode, 1);
149 case Config::PLAYER_MODE_DUAL:
150 checked_set (_player_mode, 2);
154 checked_set (_image_display, config->image_display());
155 checked_set (_respect_kdm, config->respect_kdm_validity_periods());
156 if (config->player_log_file()) {
157 checked_set (_log_file, *config->player_log_file());
159 #ifdef DCPOMATIC_VARIANT_SWAROOP
160 checked_set (_kdm_server_url, config->kdm_server_url());
161 if (config->player_lock_file()) {
162 checked_set (_lock_file, config->player_lock_file().get());
168 void player_mode_changed ()
170 switch (_player_mode->GetSelection()) {
172 Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
175 Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
178 Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
183 void image_display_changed ()
185 Config::instance()->set_image_display(_image_display->GetSelection());
188 void respect_kdm_changed ()
190 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
193 void log_file_changed ()
195 Config::instance()->set_player_log_file(wx_to_std(_log_file->GetPath()));
198 #ifdef DCPOMATIC_VARIANT_SWAROOP
199 void kdm_server_url_changed ()
201 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
204 void lock_file_changed ()
206 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
210 wxChoice* _player_mode;
211 wxChoice* _image_display;
212 wxCheckBox* _respect_kdm;
213 FilePickerCtrl* _log_file;
214 #ifdef DCPOMATIC_VARIANT_SWAROOP
215 wxTextCtrl* _kdm_server_url;
216 FilePickerCtrl* _lock_file;
220 class LocationsPage : public StandardPage
223 LocationsPage (wxSize panel_size, int border)
224 : StandardPage (panel_size, border)
227 wxString GetName () const
229 return _("Locations");
233 wxBitmap GetLargeIcon () const
235 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
245 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
246 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
248 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
249 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
250 table->Add (_content_directory, wxGBPosition (r, 1));
253 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
254 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
255 table->Add (_playlist_directory, wxGBPosition (r, 1));
258 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
259 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
260 table->Add (_kdm_directory, wxGBPosition (r, 1));
263 #ifdef DCPOMATIC_VARIANT_SWAROOP
264 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
265 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true);
266 table->Add (_background_image, wxGBPosition (r, 1));
270 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
271 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
272 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
273 #ifdef DCPOMATIC_VARIANT_SWAROOP
274 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
278 void config_changed ()
280 Config* config = Config::instance ();
282 if (config->player_content_directory()) {
283 checked_set (_content_directory, *config->player_content_directory());
285 if (config->player_playlist_directory()) {
286 checked_set (_playlist_directory, *config->player_playlist_directory());
288 if (config->player_kdm_directory()) {
289 checked_set (_kdm_directory, *config->player_kdm_directory());
291 #ifdef DCPOMATIC_VARIANT_SWAROOP
292 if (config->player_background_image()) {
293 checked_set (_background_image, *config->player_background_image());
298 void content_directory_changed ()
300 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
303 void playlist_directory_changed ()
305 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
308 void kdm_directory_changed ()
310 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
313 #ifdef DCPOMATIC_VARIANT_SWAROOP
314 void background_image_changed ()
316 boost::filesystem::path const f = wx_to_std(_background_image->GetPath());
317 if (!boost::filesystem::is_regular_file(f) || !wxImage::CanRead(std_to_wx(f.string()))) {
318 error_dialog (0, _("Could not load image file."));
319 if (Config::instance()->player_background_image()) {
320 checked_set (_background_image, *Config::instance()->player_background_image());
325 Config::instance()->set_player_background_image(f);
329 wxDirPickerCtrl* _content_directory;
330 wxDirPickerCtrl* _playlist_directory;
331 wxDirPickerCtrl* _kdm_directory;
332 #ifdef DCPOMATIC_VARIANT_SWAROOP
333 FilePickerCtrl* _background_image;
337 #ifdef DCPOMATIC_VARIANT_SWAROOP
338 class WatermarkPage : public StandardPage
341 WatermarkPage (wxSize panel_size, int border)
342 : StandardPage (panel_size, border)
345 wxString GetName () const
347 return _("Watermark");
353 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
354 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
358 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
359 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
360 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
363 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
364 _period = new wxSpinCtrl (_panel, wxID_ANY);
365 _period->SetRange (1, 60);
366 table->Add (_period, wxGBPosition(r, 1));
367 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
370 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
371 _duration = new wxSpinCtrl (_panel, wxID_ANY);
372 _duration->SetRange (100, 5000);
373 table->Add (_duration, wxGBPosition(r, 1));
374 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
377 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
378 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
379 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
382 void config_changed ()
384 Config* config = Config::instance ();
385 checked_set (_theatre, config->player_watermark_theatre());
386 checked_set (_duration, config->player_watermark_duration());
387 checked_set (_period, config->player_watermark_period());
390 void theatre_changed ()
392 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
395 void period_changed ()
397 Config::instance()->set_player_watermark_period(_period->GetValue());
400 void duration_changed ()
402 Config::instance()->set_player_watermark_duration(_duration->GetValue());
405 wxTextCtrl* _theatre;
407 wxSpinCtrl* _duration;
410 class DevicesPage : public StandardPage
413 DevicesPage (wxSize panel_size, int border)
414 : StandardPage (panel_size, border)
417 wxString GetName () const
425 vector<string> columns;
426 columns.push_back(wx_to_std(_("Manufacturer ID")));
427 columns.push_back(wx_to_std(_("Product code")));
428 columns.push_back(wx_to_std(_("Serial")));
429 columns.push_back(wx_to_std(_("Manufacture week")));
430 columns.push_back(wx_to_std(_("Manufacture year")));
431 _monitor_list = new EditableList<Monitor, MonitorDialog> (
434 boost::bind (&Config::required_monitors, Config::instance()),
435 boost::bind (&Config::set_required_monitors, Config::instance(), _1),
436 boost::bind (&DevicesPage::monitor_column, this, _1, _2),
441 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
443 wxButton* get = new Button(_panel, _("Read current devices"));
444 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
445 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
450 Config::instance()->set_required_monitors(get_monitors());
451 _monitor_list->refresh ();
454 string monitor_column (Monitor m, int c)
458 return m.manufacturer_id;
460 return locale_convert<string>(m.manufacturer_product_code);
462 return locale_convert<string>(m.serial_number);
464 return locale_convert<string>(m.week_of_manufacture);
466 return locale_convert<string>(m.year_of_manufacture);
468 DCPOMATIC_ASSERT(false);
474 void config_changed ()
476 _monitor_list->refresh ();
480 EditableList<Monitor, MonitorDialog>* _monitor_list;
486 create_player_config_dialog ()
488 wxPreferencesEditor* e = new wxPreferencesEditor ();
491 /* Width that we force some of the config panels to be on OSX so that
492 the containing window doesn't shrink too much when we select those panels.
493 This is obviously an unpleasant hack.
495 wxSize ps = wxSize (520, -1);
496 int const border = 16;
498 wxSize ps = wxSize (-1, -1);
499 int const border = 8;
502 e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
503 e->AddPage (new LocationsPage(ps, border));
504 e->AddPage (new KeysPage(ps, border));
505 #ifdef DCPOMATIC_VARIANT_SWAROOP
506 e->AddPage (new WatermarkPage(ps, border));
507 e->AddPage (new DevicesPage(ps, border));