Move _state_timer into VideoView.
[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 class LocationsPage : public StandardPage
270 {
271 public:
272         LocationsPage (wxSize panel_size, int border)
273                 : StandardPage (panel_size, border)
274         {}
275
276         wxString GetName () const
277         {
278                 return _("Locations");
279         }
280
281 #ifdef DCPOMATIC_OSX
282         wxBitmap GetLargeIcon () const
283         {
284                 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
285         }
286 #endif
287
288 private:
289         void setup ()
290         {
291
292                 int r = 0;
293
294                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
295                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
296
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));
300                 ++r;
301
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));
305                 ++r;
306
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));
310                 ++r;
311
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);
315                 table->Add (_background_image, wxGBPosition (r, 1));
316                 ++r;
317 #endif
318
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));
324 #endif
325         }
326
327         void config_changed ()
328         {
329                 Config* config = Config::instance ();
330
331                 if (config->player_content_directory()) {
332                         checked_set (_content_directory, *config->player_content_directory());
333                 }
334                 if (config->player_playlist_directory()) {
335                         checked_set (_playlist_directory, *config->player_playlist_directory());
336                 }
337                 if (config->player_kdm_directory()) {
338                         checked_set (_kdm_directory, *config->player_kdm_directory());
339                 }
340 #ifdef DCPOMATIC_VARIANT_SWAROOP
341                 if (config->player_background_image()) {
342                         checked_set (_background_image, *config->player_background_image());
343                 }
344 #endif
345         }
346
347         void content_directory_changed ()
348         {
349                 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
350         }
351
352         void playlist_directory_changed ()
353         {
354                 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
355         }
356
357         void kdm_directory_changed ()
358         {
359                 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
360         }
361
362 #ifdef DCPOMATIC_VARIANT_SWAROOP
363         void background_image_changed ()
364         {
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());
370                         }
371                         return;
372                 }
373
374                 Config::instance()->set_player_background_image(f);
375         }
376 #endif
377
378         wxDirPickerCtrl* _content_directory;
379         wxDirPickerCtrl* _playlist_directory;
380         wxDirPickerCtrl* _kdm_directory;
381 #ifdef DCPOMATIC_VARIANT_SWAROOP
382         FilePickerCtrl* _background_image;
383 #endif
384 };
385
386 /** @class PlayerAdvancedPage
387  *  @brief Advanced page of the preferences dialog for the player.
388  */
389 class PlayerAdvancedPage : public StockPage
390 {
391 public:
392         PlayerAdvancedPage (wxSize panel_size, int border)
393                 : StockPage (Kind_Advanced, panel_size, border)
394                 , _log_general (0)
395                 , _log_warning (0)
396                 , _log_error (0)
397                 , _log_timing (0)
398                 , _log_debug_decode (0)
399         {}
400
401 private:
402         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
403         {
404                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
405 #ifdef __WXOSX__
406                 flags |= wxALIGN_RIGHT;
407                 text += wxT (":");
408 #endif
409                 wxStaticText* m = new StaticText (parent, text);
410                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
411         }
412
413         void setup ()
414         {
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);
418
419                 {
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);
434                 }
435
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);
440 #endif
441
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));
449 #endif
450         }
451
452         void config_changed ()
453         {
454                 Config* config = Config::instance ();
455
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());
463 #endif
464         }
465
466         void log_changed ()
467         {
468                 int types = 0;
469                 if (_log_general->GetValue ()) {
470                         types |= LogEntry::TYPE_GENERAL;
471                 }
472                 if (_log_warning->GetValue ()) {
473                         types |= LogEntry::TYPE_WARNING;
474                 }
475                 if (_log_error->GetValue ())  {
476                         types |= LogEntry::TYPE_ERROR;
477                 }
478                 if (_log_timing->GetValue ()) {
479                         types |= LogEntry::TYPE_TIMING;
480                 }
481                 if (_log_debug_decode->GetValue ()) {
482                         types |= LogEntry::TYPE_DEBUG_DECODE;
483                 }
484                 Config::instance()->set_log_types (types);
485         }
486
487 #ifdef DCPOMATIC_WINDOWS
488         void win32_console_changed ()
489         {
490                 Config::instance()->set_win32_console (_win32_console->GetValue ());
491         }
492 #endif
493
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;
501 #endif
502 };
503
504
505 #ifdef DCPOMATIC_VARIANT_SWAROOP
506 class WatermarkPage : public StandardPage
507 {
508 public:
509         WatermarkPage (wxSize panel_size, int border)
510                 : StandardPage (panel_size, border)
511         {}
512
513         wxString GetName () const
514         {
515                 return _("Watermark");
516         }
517
518 #ifdef DCPOMATIC_OSX
519         wxBitmap GetLargeIcon () const
520         {
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);
523         }
524 #endif
525
526 private:
527         void setup ()
528         {
529                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
530                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
531
532                 int r = 0;
533
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));
537                 ++r;
538
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));
544                 ++r;
545
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));
551                 ++r;
552
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));
556         }
557
558         void config_changed ()
559         {
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());
564         }
565
566         void theatre_changed ()
567         {
568                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
569         }
570
571         void period_changed ()
572         {
573                 Config::instance()->set_player_watermark_period(_period->GetValue());
574         }
575
576         void duration_changed ()
577         {
578                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
579         }
580
581         wxTextCtrl* _theatre;
582         wxSpinCtrl* _period;
583         wxSpinCtrl* _duration;
584 };
585
586 class DevicesPage : public StandardPage
587 {
588 public:
589         DevicesPage (wxSize panel_size, int border)
590                 : StandardPage (panel_size, border)
591         {}
592
593         wxString GetName () const
594         {
595                 return _("Devices");
596         }
597
598 #ifdef DCPOMATIC_OSX
599         wxBitmap GetLargeIcon () const
600         {
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);
603         }
604 #endif
605
606 private:
607         void setup ()
608         {
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> (
616                         _panel,
617                         columns,
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),
621                         true,
622                         true
623                         );
624                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
625
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));
629         }
630
631         void get_clicked ()
632         {
633                 Config::instance()->set_required_monitors(get_monitors());
634                 _monitor_list->refresh ();
635         }
636
637         string monitor_column (Monitor m, int c)
638         {
639                 switch (c) {
640                 case 0:
641                         return m.manufacturer_id;
642                 case 1:
643                         return locale_convert<string>(m.manufacturer_product_code);
644                 case 2:
645                         return locale_convert<string>(m.serial_number);
646                 case 3:
647                         return locale_convert<string>(m.week_of_manufacture);
648                 case 4:
649                         return locale_convert<string>(m.year_of_manufacture);
650                 default:
651                         DCPOMATIC_ASSERT(false);
652                 }
653
654                 return "";
655         }
656
657         void config_changed ()
658         {
659                 _monitor_list->refresh ();
660         }
661
662 private:
663         EditableList<Monitor, MonitorDialog>* _monitor_list;
664 };
665
666 #endif
667
668 wxPreferencesEditor*
669 create_player_config_dialog ()
670 {
671         wxPreferencesEditor* e = new wxPreferencesEditor ();
672
673 #ifdef DCPOMATIC_OSX
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.
677         */
678         wxSize ps = wxSize (520, -1);
679         int const border = 16;
680 #else
681         wxSize ps = wxSize (-1, -1);
682         int const border = 8;
683 #endif
684
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));
692 #endif
693         e->AddPage (new PlayerAdvancedPage(ps, border));
694         return e;
695 }