Add empty playlist list and configuration option.
[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 "lib/config.h"
39 #include "lib/ratio.h"
40 #include "lib/filter.h"
41 #include "lib/dcp_content_type.h"
42 #include "lib/log.h"
43 #include "lib/util.h"
44 #include "lib/cross.h"
45 #include "lib/exceptions.h"
46 #include <dcp/locale_convert.h>
47 #include <dcp/exceptions.h>
48 #include <dcp/certificate_chain.h>
49 #include <wx/stdpaths.h>
50 #include <wx/preferences.h>
51 #include <wx/spinctrl.h>
52 #include <wx/filepicker.h>
53 #include <RtAudio.h>
54 #include <boost/filesystem.hpp>
55 #include <boost/foreach.hpp>
56 #include <iostream>
57
58 using std::vector;
59 using std::string;
60 using std::list;
61 using std::cout;
62 using std::pair;
63 using std::make_pair;
64 using std::map;
65 using boost::bind;
66 using boost::shared_ptr;
67 using boost::function;
68 using boost::optional;
69 using dcp::locale_convert;
70
71 class PlayerGeneralPage : public GeneralPage
72 {
73 public:
74         PlayerGeneralPage (wxSize panel_size, int border)
75                 : GeneralPage (panel_size, border)
76         {}
77
78 private:
79         void setup ()
80         {
81                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
82                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
83
84                 int r = 0;
85                 add_language_controls (table, r);
86                 add_play_sound_controls (table, r);
87                 add_update_controls (table, r);
88
89                 add_label_to_sizer (table, _panel, _("Start player as"), true, wxGBPosition(r, 0));
90                 _player_mode = new wxChoice (_panel, wxID_ANY);
91                 _player_mode->Append (_("window"));
92                 _player_mode->Append (_("full screen"));
93                 _player_mode->Append (_("full screen with controls on other monitor"));
94                 table->Add (_player_mode, wxGBPosition(r, 1));
95                 ++r;
96
97                 add_label_to_sizer (table, _panel, _("Dual-screen displays"), true, wxGBPosition(r, 0));
98                 _image_display = new wxChoice (_panel, wxID_ANY);
99                 _image_display->Append (_("Image on primary, controls on secondary"));
100                 _image_display->Append (_("Image on secondary, controls on primary"));
101                 table->Add (_image_display, wxGBPosition(r, 1));
102                 ++r;
103
104                 _respect_kdm = new wxCheckBox (_panel, wxID_ANY, _("Respect KDM validity periods"));
105                 table->Add (_respect_kdm, wxGBPosition(r, 0), wxGBSpan(1, 2));
106                 ++r;
107
108                 add_label_to_sizer (table, _panel, _("Log file"), true, wxGBPosition (r, 0));
109                 _log_file = new FilePickerCtrl (_panel, _("Select log file"), "*", true);
110                 table->Add (_log_file, wxGBPosition (r, 1));
111                 ++r;
112
113 #ifdef DCPOMATIC_VARIANT_SWAROOP
114                 add_label_to_sizer (table, _panel, _("KDM server URL"), true, wxGBPosition(r, 0));
115                 _kdm_server_url = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
116                 table->Add (_kdm_server_url, wxGBPosition (r, 1));
117                 ++r;
118 #endif
119
120                 _player_mode->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::player_mode_changed, this));
121                 _image_display->Bind (wxEVT_CHOICE, bind(&PlayerGeneralPage::image_display_changed, this));
122                 _respect_kdm->Bind (wxEVT_CHECKBOX, bind(&PlayerGeneralPage::respect_kdm_changed, this));
123                 _log_file->Bind (wxEVT_FILEPICKER_CHANGED, bind(&PlayerGeneralPage::log_file_changed, this));
124 #ifdef DCPOMATIC_VARIANT_SWAROOP
125                 _kdm_server_url->Bind (wxEVT_TEXT, bind(&PlayerGeneralPage::kdm_server_url_changed, this));
126 #endif
127         }
128
129         void config_changed ()
130         {
131                 GeneralPage::config_changed ();
132
133                 Config* config = Config::instance ();
134
135                 switch (config->player_mode()) {
136                 case Config::PLAYER_MODE_WINDOW:
137                         checked_set (_player_mode, 0);
138                         break;
139                 case Config::PLAYER_MODE_FULL:
140                         checked_set (_player_mode, 1);
141                         break;
142                 case Config::PLAYER_MODE_DUAL:
143                         checked_set (_player_mode, 2);
144                         break;
145                 }
146
147                 checked_set (_image_display, config->image_display());
148                 checked_set (_respect_kdm, config->respect_kdm_validity_periods());
149                 if (config->player_log_file()) {
150                         checked_set (_log_file, *config->player_log_file());
151                 }
152 #ifdef DCPOMATIC_VARIANT_SWAROOP
153                 checked_set (_kdm_server_url, config->kdm_server_url());
154 #endif
155         }
156
157 private:
158         void player_mode_changed ()
159         {
160                 switch (_player_mode->GetSelection()) {
161                 case 0:
162                         Config::instance()->set_player_mode(Config::PLAYER_MODE_WINDOW);
163                         break;
164                 case 1:
165                         Config::instance()->set_player_mode(Config::PLAYER_MODE_FULL);
166                         break;
167                 case 2:
168                         Config::instance()->set_player_mode(Config::PLAYER_MODE_DUAL);
169                         break;
170                 }
171         }
172
173         void image_display_changed ()
174         {
175                 Config::instance()->set_image_display(_image_display->GetSelection());
176         }
177
178         void respect_kdm_changed ()
179         {
180                 Config::instance()->set_respect_kdm_validity_periods(_respect_kdm->GetValue());
181         }
182
183         void log_file_changed ()
184         {
185                 Config::instance()->set_player_log_file(wx_to_std(_log_file->GetPath()));
186         }
187
188 #ifdef DCPOMATIC_VARIANT_SWAROOP
189         void kdm_server_url_changed ()
190         {
191                 Config::instance()->set_kdm_server_url(wx_to_std(_kdm_server_url->GetValue()));
192         }
193 #endif
194
195         wxChoice* _player_mode;
196         wxChoice* _image_display;
197         wxCheckBox* _respect_kdm;
198         FilePickerCtrl* _log_file;
199 #ifdef DCPOMATIC_VARIANT_SWAROOP
200         wxTextCtrl* _kdm_server_url;
201 #endif
202 };
203
204 class LocationsPage : public StandardPage
205 {
206 public:
207         LocationsPage (wxSize panel_size, int border)
208                 : StandardPage (panel_size, border)
209         {}
210
211         wxString GetName () const
212         {
213                 return _("Locations");
214         }
215
216 #ifdef DCPOMATIC_OSX
217         wxBitmap GetLargeIcon () const
218         {
219                 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
220         }
221 #endif
222
223 private:
224         void setup ()
225         {
226
227                 int r = 0;
228
229                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
230                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
231
232                 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
233                 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
234                 table->Add (_content_directory, wxGBPosition (r, 1));
235                 ++r;
236
237                 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
238                 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
239                 table->Add (_playlist_directory, wxGBPosition (r, 1));
240                 ++r;
241
242                 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
243                 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
244                 table->Add (_kdm_directory, wxGBPosition (r, 1));
245                 ++r;
246
247 #ifdef DCPOMATIC_VARIANT_SWAROOP
248                 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
249                 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true);
250                 table->Add (_background_image, wxGBPosition (r, 1));
251                 ++r;
252 #endif
253
254                 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
255                 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
256                 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
257 #ifdef DCPOMATIC_VARIANT_SWAROOP
258                 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
259 #endif
260         }
261
262         void config_changed ()
263         {
264                 Config* config = Config::instance ();
265
266                 if (config->player_content_directory()) {
267                         checked_set (_content_directory, *config->player_content_directory());
268                 }
269                 if (config->player_playlist_directory()) {
270                         checked_set (_playlist_directory, *config->player_playlist_directory());
271                 }
272                 if (config->player_kdm_directory()) {
273                         checked_set (_kdm_directory, *config->player_kdm_directory());
274                 }
275 #ifdef DCPOMATIC_VARIANT_SWAROOP
276                 if (config->player_background_image()) {
277                         checked_set (_background_image, *config->player_background_image());
278                 }
279 #endif
280         }
281
282         void content_directory_changed ()
283         {
284                 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
285         }
286
287         void playlist_directory_changed ()
288         {
289                 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
290         }
291
292         void kdm_directory_changed ()
293         {
294                 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
295         }
296
297 #ifdef DCPOMATIC_VARIANT_SWAROOP
298         void background_image_changed ()
299         {
300                 Config::instance()->set_player_background_image(wx_to_std(_background_image->GetPath()));
301         }
302 #endif
303
304         wxDirPickerCtrl* _content_directory;
305         wxDirPickerCtrl* _playlist_directory;
306         wxDirPickerCtrl* _kdm_directory;
307 #ifdef DCPOMATIC_VARIANT_SWAROOP
308         FilePickerCtrl* _background_image;
309 #endif
310 };
311
312 #ifdef DCPOMATIC_VARIANT_SWAROOP
313 class WatermarkPage : public StandardPage
314 {
315 public:
316         WatermarkPage (wxSize panel_size, int border)
317                 : StandardPage (panel_size, border)
318         {}
319
320         wxString GetName () const
321         {
322                 return _("Watermark");
323         }
324
325 private:
326         void setup ()
327         {
328                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
329                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
330
331                 int r = 0;
332
333                 add_label_to_sizer (table, _panel, _("Theatre name"), true, wxGBPosition(r, 0));
334                 _theatre = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, -1));
335                 table->Add (_theatre, wxGBPosition(r, 1), wxGBSpan(1, 2));
336                 ++r;
337
338                 add_label_to_sizer (table, _panel, _("Period"), true, wxGBPosition(r, 0));
339                 _period = new wxSpinCtrl (_panel, wxID_ANY);
340                 _period->SetRange (1, 60);
341                 table->Add (_period, wxGBPosition(r, 1));
342                 add_label_to_sizer (table, _panel, _("minutes"), false, wxGBPosition(r, 2));
343                 ++r;
344
345                 add_label_to_sizer (table, _panel, _("Duration"), true, wxGBPosition(r, 0));
346                 _duration = new wxSpinCtrl (_panel, wxID_ANY);
347                 _duration->SetRange (100, 5000);
348                 table->Add (_duration, wxGBPosition(r, 1));
349                 add_label_to_sizer (table, _panel, _("milliseconds"), false, wxGBPosition(r, 2));
350                 ++r;
351
352                 _theatre->Bind (wxEVT_TEXT, bind(&WatermarkPage::theatre_changed, this));
353                 _duration->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::duration_changed, this));
354                 _period->Bind (wxEVT_SPINCTRL, bind(&WatermarkPage::period_changed, this));
355         }
356
357         void config_changed ()
358         {
359                 Config* config = Config::instance ();
360                 checked_set (_theatre, config->player_watermark_theatre());
361                 checked_set (_duration, config->player_watermark_duration());
362                 checked_set (_period, config->player_watermark_period());
363         }
364
365         void theatre_changed ()
366         {
367                 Config::instance()->set_player_watermark_theatre(wx_to_std(_theatre->GetValue()));
368         }
369
370         void period_changed ()
371         {
372                 Config::instance()->set_player_watermark_period(_period->GetValue());
373         }
374
375         void duration_changed ()
376         {
377                 Config::instance()->set_player_watermark_duration(_duration->GetValue());
378         }
379
380         wxTextCtrl* _theatre;
381         wxSpinCtrl* _period;
382         wxSpinCtrl* _duration;
383 };
384
385 class DevicesPage : public StandardPage
386 {
387 public:
388         DevicesPage (wxSize panel_size, int border)
389                 : StandardPage (panel_size, border)
390         {}
391
392         wxString GetName () const
393         {
394                 return _("Devices");
395         }
396
397 private:
398         void setup ()
399         {
400                 vector<string> columns;
401                 columns.push_back(wx_to_std(_("Manufacturer ID")));
402                 columns.push_back(wx_to_std(_("Product code")));
403                 columns.push_back(wx_to_std(_("Serial")));
404                 columns.push_back(wx_to_std(_("Manufacture week")));
405                 columns.push_back(wx_to_std(_("Manufacture year")));
406                 _monitor_list = new EditableList<Monitor, MonitorDialog> (
407                         _panel,
408                         columns,
409                         boost::bind (&Config::required_monitors, Config::instance()),
410                         boost::bind (&Config::set_required_monitors, Config::instance(), _1),
411                         boost::bind (&DevicesPage::monitor_column, this, _1, _2),
412                         true,
413                         true,
414                         100
415                         );
416                 _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
417
418                 wxButton* get = new wxButton(_panel, wxID_ANY, _("Read current devices"));
419                 _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
420                 get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
421         }
422
423         void get_clicked ()
424         {
425                 Config::instance()->set_required_monitors(get_monitors());
426                 _monitor_list->refresh ();
427         }
428
429         string monitor_column (Monitor m, int c)
430         {
431                 switch (c) {
432                 case 0:
433                         return m.manufacturer_id;
434                 case 1:
435                         return locale_convert<string>(m.manufacturer_product_code);
436                 case 2:
437                         return locale_convert<string>(m.serial_number);
438                 case 3:
439                         return locale_convert<string>(m.week_of_manufacture);
440                 case 4:
441                         return locale_convert<string>(m.year_of_manufacture);
442                 default:
443                         DCPOMATIC_ASSERT(false);
444                 }
445
446                 return "";
447         }
448
449         void config_changed ()
450         {
451                 _monitor_list->refresh ();
452         }
453
454 private:
455         EditableList<Monitor, MonitorDialog>* _monitor_list;
456 };
457
458 #endif
459
460 wxPreferencesEditor*
461 create_player_config_dialog ()
462 {
463         wxPreferencesEditor* e = new wxPreferencesEditor ();
464
465 #ifdef DCPOMATIC_OSX
466         /* Width that we force some of the config panels to be on OSX so that
467            the containing window doesn't shrink too much when we select those panels.
468            This is obviously an unpleasant hack.
469         */
470         wxSize ps = wxSize (520, -1);
471         int const border = 16;
472 #else
473         wxSize ps = wxSize (-1, -1);
474         int const border = 8;
475 #endif
476
477         e->AddPage (new PlayerGeneralPage(wxSize(-1, 500), border));
478         e->AddPage (new LocationsPage(ps, border));
479         e->AddPage (new KeysPage(ps, border));
480 #ifdef DCPOMATIC_VARIANT_SWAROOP
481         e->AddPage (new WatermarkPage(ps, border));
482         e->AddPage (new DevicesPage(ps, border));
483 #endif
484         return e;
485 }