swaroop: at least partially fix crashes when trying to set the background image to...
[dcpomatic.git] / src / wx / player_config_dialog.cc
1 /*
2     Copyright (C) 2012-2018 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 "lib/config.h"
40 #include "lib/ratio.h"
41 #include "lib/filter.h"
42 #include "lib/dcp_content_type.h"
43 #include "lib/log.h"
44 #include "lib/util.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>
54 #include <RtAudio.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/foreach.hpp>
57 #include <iostream>
58
59 using std::vector;
60 using std::string;
61 using std::list;
62 using std::cout;
63 using std::pair;
64 using std::make_pair;
65 using std::map;
66 using boost::bind;
67 using boost::shared_ptr;
68 using boost::function;
69 using boost::optional;
70 using dcp::locale_convert;
71
72 class PlayerGeneralPage : public GeneralPage
73 {
74 public:
75         PlayerGeneralPage (wxSize panel_size, int border)
76                 : GeneralPage (panel_size, border)
77         {}
78
79 private:
80         void setup ()
81         {
82                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
84
85                 int r = 0;
86                 add_language_controls (table, r);
87                 add_play_sound_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                 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
106                 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
107                 ++r;
108
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));
112                 ++r;
113
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));
118                 ++r;
119
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));
123                 ++r;
124 #endif
125
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));
133 #endif
134         }
135
136         void config_changed ()
137         {
138                 GeneralPage::config_changed ();
139
140                 Config* config = Config::instance ();
141
142                 switch (config->player_mode()) {
143                 case Config::PLAYER_MODE_WINDOW:
144                         checked_set (_player_mode, 0);
145                         break;
146                 case Config::PLAYER_MODE_FULL:
147                         checked_set (_player_mode, 1);
148                         break;
149                 case Config::PLAYER_MODE_DUAL:
150                         checked_set (_player_mode, 2);
151                         break;
152                 }
153
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());
158                 }
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());
163                 }
164 #endif
165         }
166
167 private:
168         void player_mode_changed ()
169         {
170                 switch (_player_mode->GetSelection()) {
171                 case 0:
172                         Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
173                         break;
174                 case 1:
175                         Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
176                         break;
177                 case 2:
178                         Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
179                         break;
180                 }
181         }
182
183         void image_display_changed ()
184         {
185                 Config::instance()->set_image_display(_image_display->GetSelection());
186         }
187
188         void respect_kdm_changed ()
189         {
190                 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
191         }
192
193         void log_file_changed ()
194         {
195                 Config::instance()->set_player_log_file(wx_to_std(_log_file->GetPath()));
196         }
197
198 #ifdef DCPOMATIC_VARIANT_SWAROOP
199         void kdm_server_url_changed ()
200         {
201                 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
202         }
203
204         void lock_file_changed ()
205         {
206                 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
207         }
208 #endif
209
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;
217 #endif
218 };
219
220 class LocationsPage : public StandardPage
221 {
222 public:
223         LocationsPage (wxSize panel_size, int border)
224                 : StandardPage (panel_size, border)
225         {}
226
227         wxString GetName () const
228         {
229                 return _("Locations");
230         }
231
232 #ifdef DCPOMATIC_OSX
233         wxBitmap GetLargeIcon () const
234         {
235                 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
236         }
237 #endif
238
239 private:
240         void setup ()
241         {
242
243                 int r = 0;
244
245                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
246                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
247
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));
251                 ++r;
252
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));
256                 ++r;
257
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));
261                 ++r;
262
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));
267                 ++r;
268 #endif
269
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));
275 #endif
276         }
277
278         void config_changed ()
279         {
280                 Config* config = Config::instance ();
281
282                 if (config->player_content_directory()) {
283                         checked_set (_content_directory, *config->player_content_directory());
284                 }
285                 if (config->player_playlist_directory()) {
286                         checked_set (_playlist_directory, *config->player_playlist_directory());
287                 }
288                 if (config->player_kdm_directory()) {
289                         checked_set (_kdm_directory, *config->player_kdm_directory());
290                 }
291 #ifdef DCPOMATIC_VARIANT_SWAROOP
292                 if (config->player_background_image()) {
293                         checked_set (_background_image, *config->player_background_image());
294                 }
295 #endif
296         }
297
298         void content_directory_changed ()
299         {
300                 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
301         }
302
303         void playlist_directory_changed ()
304         {
305                 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
306         }
307
308         void kdm_directory_changed ()
309         {
310                 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
311         }
312
313 #ifdef DCPOMATIC_VARIANT_SWAROOP
314         void background_image_changed ()
315         {
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());
321                         }
322                         return;
323                 }
324
325                 Config::instance()->set_player_background_image(f);
326         }
327 #endif
328
329         wxDirPickerCtrl* _content_directory;
330         wxDirPickerCtrl* _playlist_directory;
331         wxDirPickerCtrl* _kdm_directory;
332 #ifdef DCPOMATIC_VARIANT_SWAROOP
333         FilePickerCtrl* _background_image;
334 #endif
335 };
336
337 #ifdef DCPOMATIC_VARIANT_SWAROOP
338 class WatermarkPage : public StandardPage
339 {
340 public:
341         WatermarkPage (wxSize panel_size, int border)
342                 : StandardPage (panel_size, border)
343         {}
344
345         wxString GetName () const
346         {
347                 return _("Watermark");
348         }
349
350 private:
351         void setup ()
352         {
353                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
354                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
355
356                 int r = 0;
357
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));
361                 ++r;
362
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));
368                 ++r;
369
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));
375                 ++r;
376
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));
380         }
381
382         void config_changed ()
383         {
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());
388         }
389
390         void theatre_changed ()
391         {
392                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
393         }
394
395         void period_changed ()
396         {
397                 Config::instance()->set_player_watermark_period(_period->GetValue());
398         }
399
400         void duration_changed ()
401         {
402                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
403         }
404
405         wxTextCtrl* _theatre;
406         wxSpinCtrl* _period;
407         wxSpinCtrl* _duration;
408 };
409
410 class DevicesPage : public StandardPage
411 {
412 public:
413         DevicesPage (wxSize panel_size, int border)
414                 : StandardPage (panel_size, border)
415         {}
416
417         wxString GetName () const
418         {
419                 return _("Devices");
420         }
421
422 private:
423         void setup ()
424         {
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> (
432                         _panel,
433                         columns,
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),
437                         true,
438                         true,
439                         100
440                         );
441                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
442
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));
446         }
447
448         void get_clicked ()
449         {
450                 Config::instance()->set_required_monitors(get_monitors());
451                 _monitor_list->refresh ();
452         }
453
454         string monitor_column (Monitor m, int c)
455         {
456                 switch (c) {
457                 case 0:
458                         return m.manufacturer_id;
459                 case 1:
460                         return locale_convert<string>(m.manufacturer_product_code);
461                 case 2:
462                         return locale_convert<string>(m.serial_number);
463                 case 3:
464                         return locale_convert<string>(m.week_of_manufacture);
465                 case 4:
466                         return locale_convert<string>(m.year_of_manufacture);
467                 default:
468                         DCPOMATIC_ASSERT(false);
469                 }
470
471                 return "";
472         }
473
474         void config_changed ()
475         {
476                 _monitor_list->refresh ();
477         }
478
479 private:
480         EditableList<Monitor, MonitorDialog>* _monitor_list;
481 };
482
483 #endif
484
485 wxPreferencesEditor*
486 create_player_config_dialog ()
487 {
488         wxPreferencesEditor* e = new wxPreferencesEditor ();
489
490 #ifdef DCPOMATIC_OSX
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.
494         */
495         wxSize ps = wxSize (520, -1);
496         int const border = 16;
497 #else
498         wxSize ps = wxSize (-1, -1);
499         int const border = 8;
500 #endif
501
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));
508 #endif
509         return e;
510 }