Allow configuration of relevant log levels in the player (#1549).
[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_play_sound_controls (table, r);
89                 add_update_controls (table, r);
90
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));
97                 ++r;
98
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));
104                 ++r;
105
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));
111                 ++r;
112
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);
118                 ++r;
119
120                 _respect_kdm = new CheckBox (_panel, _("Respect KDM validity periods"));
121                 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
122                 ++r;
123
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));
127                 ++r;
128
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));
132                 ++r;
133
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));
138                 ++r;
139
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));
143                 ++r;
144 #endif
145
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));
155 #endif
156         }
157
158         void config_changed ()
159         {
160                 GeneralPage::config_changed ();
161
162                 Config* config = Config::instance ();
163
164                 switch (config->player_mode()) {
165                 case Config::PLAYER_MODE_WINDOW:
166                         checked_set (_player_mode, 0);
167                         break;
168                 case Config::PLAYER_MODE_FULL:
169                         checked_set (_player_mode, 1);
170                         break;
171                 case Config::PLAYER_MODE_DUAL:
172                         checked_set (_player_mode, 2);
173                         break;
174                 }
175
176                 switch (config->video_view_type()) {
177                 case Config::VIDEO_VIEW_SIMPLE:
178                         checked_set (_video_display_mode, 0);
179                         break;
180                 case Config::VIDEO_VIEW_OPENGL:
181                         checked_set (_video_display_mode, 1);
182                         break;
183                 }
184
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());
189                 }
190                 if (config->player_debug_log_file()) {
191                         checked_set (_debug_log_file, *config->player_debug_log_file());
192                 }
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());
197                 }
198 #endif
199         }
200
201 private:
202         void player_mode_changed ()
203         {
204                 switch (_player_mode->GetSelection()) {
205                 case 0:
206                         Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
207                         break;
208                 case 1:
209                         Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
210                         break;
211                 case 2:
212                         Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
213                         break;
214                 }
215         }
216
217         void image_display_changed ()
218         {
219                 Config::instance()->set_image_display(_image_display->GetSelection());
220         }
221
222         void video_display_mode_changed ()
223         {
224                 if (_video_display_mode->GetSelection() == 0) {
225                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
226                 } else {
227                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
228                 }
229         }
230
231         void respect_kdm_changed ()
232         {
233                 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
234         }
235
236         void activity_log_file_changed ()
237         {
238                 Config::instance()->set_player_activity_log_file(wx_to_std(_activity_log_file->GetPath()));
239         }
240
241         void debug_log_file_changed ()
242         {
243                 Config::instance()->set_player_debug_log_file(wx_to_std(_debug_log_file->GetPath()));
244         }
245
246 #ifdef DCPOMATIC_VARIANT_SWAROOP
247         void kdm_server_url_changed ()
248         {
249                 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
250         }
251
252         void lock_file_changed ()
253         {
254                 Config::instance()->set_player_lock_file(wx_to_std(_lock_file->GetPath()));
255         }
256 #endif
257
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;
267 #endif
268 };
269
270 class LocationsPage : public StandardPage
271 {
272 public:
273         LocationsPage (wxSize panel_size, int border)
274                 : StandardPage (panel_size, border)
275         {}
276
277         wxString GetName () const
278         {
279                 return _("Locations");
280         }
281
282 #ifdef DCPOMATIC_OSX
283         wxBitmap GetLargeIcon () const
284         {
285                 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
286         }
287 #endif
288
289 private:
290         void setup ()
291         {
292
293                 int r = 0;
294
295                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
296                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
297
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));
301                 ++r;
302
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));
306                 ++r;
307
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));
311                 ++r;
312
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));
317                 ++r;
318 #endif
319
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));
325 #endif
326         }
327
328         void config_changed ()
329         {
330                 Config* config = Config::instance ();
331
332                 if (config->player_content_directory()) {
333                         checked_set (_content_directory, *config->player_content_directory());
334                 }
335                 if (config->player_playlist_directory()) {
336                         checked_set (_playlist_directory, *config->player_playlist_directory());
337                 }
338                 if (config->player_kdm_directory()) {
339                         checked_set (_kdm_directory, *config->player_kdm_directory());
340                 }
341 #ifdef DCPOMATIC_VARIANT_SWAROOP
342                 if (config->player_background_image()) {
343                         checked_set (_background_image, *config->player_background_image());
344                 }
345 #endif
346         }
347
348         void content_directory_changed ()
349         {
350                 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
351         }
352
353         void playlist_directory_changed ()
354         {
355                 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
356         }
357
358         void kdm_directory_changed ()
359         {
360                 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
361         }
362
363 #ifdef DCPOMATIC_VARIANT_SWAROOP
364         void background_image_changed ()
365         {
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());
371                         }
372                         return;
373                 }
374
375                 Config::instance()->set_player_background_image(f);
376         }
377 #endif
378
379         wxDirPickerCtrl* _content_directory;
380         wxDirPickerCtrl* _playlist_directory;
381         wxDirPickerCtrl* _kdm_directory;
382 #ifdef DCPOMATIC_VARIANT_SWAROOP
383         FilePickerCtrl* _background_image;
384 #endif
385 };
386
387 /** @class PlayerAdvancedPage
388  *  @brief Advanced page of the preferences dialog for the player.
389  */
390 class PlayerAdvancedPage : public StockPage
391 {
392 public:
393         PlayerAdvancedPage (wxSize panel_size, int border)
394                 : StockPage (Kind_Advanced, panel_size, border)
395                 , _log_general (0)
396                 , _log_warning (0)
397                 , _log_error (0)
398                 , _log_timing (0)
399                 , _log_debug_decode (0)
400         {}
401
402 private:
403         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
404         {
405                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
406 #ifdef __WXOSX__
407                 flags |= wxALIGN_RIGHT;
408                 text += wxT (":");
409 #endif
410                 wxStaticText* m = new StaticText (parent, text);
411                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
412         }
413
414         void setup ()
415         {
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);
419
420                 {
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);
435                 }
436
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);
441 #endif
442
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));
450 #endif
451         }
452
453         void config_changed ()
454         {
455                 Config* config = Config::instance ();
456
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());
464 #endif
465         }
466
467         void log_changed ()
468         {
469                 int types = 0;
470                 if (_log_general->GetValue ()) {
471                         types |= LogEntry::TYPE_GENERAL;
472                 }
473                 if (_log_warning->GetValue ()) {
474                         types |= LogEntry::TYPE_WARNING;
475                 }
476                 if (_log_error->GetValue ())  {
477                         types |= LogEntry::TYPE_ERROR;
478                 }
479                 if (_log_timing->GetValue ()) {
480                         types |= LogEntry::TYPE_TIMING;
481                 }
482                 if (_log_debug_decode->GetValue ()) {
483                         types |= LogEntry::TYPE_DEBUG_DECODE;
484                 }
485                 Config::instance()->set_log_types (types);
486         }
487
488 #ifdef DCPOMATIC_WINDOWS
489         void win32_console_changed ()
490         {
491                 Config::instance()->set_win32_console (_win32_console->GetValue ());
492         }
493 #endif
494
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;
502 #endif
503 };
504
505
506 #ifdef DCPOMATIC_VARIANT_SWAROOP
507 class WatermarkPage : public StandardPage
508 {
509 public:
510         WatermarkPage (wxSize panel_size, int border)
511                 : StandardPage (panel_size, border)
512         {}
513
514         wxString GetName () const
515         {
516                 return _("Watermark");
517         }
518
519 #ifdef DCPOMATIC_OSX
520         wxBitmap GetLargeIcon () const
521         {
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);
524         }
525 #endif
526
527 private:
528         void setup ()
529         {
530                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
531                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
532
533                 int r = 0;
534
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));
538                 ++r;
539
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));
545                 ++r;
546
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));
552                 ++r;
553
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));
557         }
558
559         void config_changed ()
560         {
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());
565         }
566
567         void theatre_changed ()
568         {
569                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
570         }
571
572         void period_changed ()
573         {
574                 Config::instance()->set_player_watermark_period(_period->GetValue());
575         }
576
577         void duration_changed ()
578         {
579                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
580         }
581
582         wxTextCtrl* _theatre;
583         wxSpinCtrl* _period;
584         wxSpinCtrl* _duration;
585 };
586
587 class DevicesPage : public StandardPage
588 {
589 public:
590         DevicesPage (wxSize panel_size, int border)
591                 : StandardPage (panel_size, border)
592         {}
593
594         wxString GetName () const
595         {
596                 return _("Devices");
597         }
598
599 #ifdef DCPOMATIC_OSX
600         wxBitmap GetLargeIcon () const
601         {
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);
604         }
605 #endif
606
607 private:
608         void setup ()
609         {
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> (
617                         _panel,
618                         columns,
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),
622                         true,
623                         true
624                         );
625                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
626
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));
630         }
631
632         void get_clicked ()
633         {
634                 Config::instance()->set_required_monitors(get_monitors());
635                 _monitor_list->refresh ();
636         }
637
638         string monitor_column (Monitor m, int c)
639         {
640                 switch (c) {
641                 case 0:
642                         return m.manufacturer_id;
643                 case 1:
644                         return locale_convert<string>(m.manufacturer_product_code);
645                 case 2:
646                         return locale_convert<string>(m.serial_number);
647                 case 3:
648                         return locale_convert<string>(m.week_of_manufacture);
649                 case 4:
650                         return locale_convert<string>(m.year_of_manufacture);
651                 default:
652                         DCPOMATIC_ASSERT(false);
653                 }
654
655                 return "";
656         }
657
658         void config_changed ()
659         {
660                 _monitor_list->refresh ();
661         }
662
663 private:
664         EditableList<Monitor, MonitorDialog>* _monitor_list;
665 };
666
667 #endif
668
669 wxPreferencesEditor*
670 create_player_config_dialog ()
671 {
672         wxPreferencesEditor* e = new wxPreferencesEditor ();
673
674 #ifdef DCPOMATIC_OSX
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.
678         */
679         wxSize ps = wxSize (520, -1);
680         int const border = 16;
681 #else
682         wxSize ps = wxSize (-1, -1);
683         int const border = 8;
684 #endif
685
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));
692 #endif
693         e->AddPage (new PlayerAdvancedPage(ps, border));
694         return e;
695 }