Use libdcp's warnings.h
[dcpomatic.git] / src / wx / full_config_dialog.cc
1 /*
2     Copyright (C) 2012-2021 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
22 /** @file src/full_config_dialog.cc
23  *  @brief A dialogue to edit all DCP-o-matic configuration.
24  */
25
26
27 #include "check_box.h"
28 #include "config_dialog.h"
29 #include "config_move_dialog.h"
30 #include "dcpomatic_button.h"
31 #include "dir_picker_ctrl.h"
32 #include "editable_list.h"
33 #include "email_dialog.h"
34 #include "file_picker_ctrl.h"
35 #include "filter_dialog.h"
36 #include "full_config_dialog.h"
37 #include "kdm_choice.h"
38 #include "make_chain_dialog.h"
39 #include "nag_dialog.h"
40 #include "name_format_editor.h"
41 #include "password_entry.h"
42 #include "send_test_email_dialog.h"
43 #include "server_dialog.h"
44 #include "static_text.h"
45 #include "wx_util.h"
46 #include "lib/config.h"
47 #include "lib/cross.h"
48 #include "lib/dcp_content_type.h"
49 #include "lib/emailer.h"
50 #include "lib/exceptions.h"
51 #include "lib/filter.h"
52 #include "lib/log.h"
53 #include "lib/ratio.h"
54 #include "lib/util.h"
55 #include <dcp/certificate_chain.h>
56 #include <dcp/exceptions.h>
57 #include <dcp/locale_convert.h>
58 #include <dcp/warnings.h>
59 LIBDCP_DISABLE_WARNINGS
60 #include <wx/filepicker.h>
61 #include <wx/preferences.h>
62 #include <wx/spinctrl.h>
63 #include <wx/stdpaths.h>
64 LIBDCP_ENABLE_WARNINGS
65 #include <RtAudio.h>
66 #include <boost/filesystem.hpp>
67 #include <iostream>
68
69
70 using std::cout;
71 using std::function;
72 using std::list;
73 using std::make_pair;
74 using std::map;
75 using std::pair;
76 using std::shared_ptr;
77 using std::string;
78 using std::vector;
79 using boost::bind;
80 using boost::optional;
81 #if BOOST_VERSION >= 106100
82 using namespace boost::placeholders;
83 #endif
84 using dcp::locale_convert;
85
86
87 class FullGeneralPage : public GeneralPage
88 {
89 public:
90         FullGeneralPage (wxSize panel_size, int border)
91                 : GeneralPage (panel_size, border)
92         {}
93
94 private:
95         void setup () override
96         {
97                 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
98                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
99
100                 int r = 0;
101                 add_language_controls (table, r);
102
103                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
104                 _master_encoding_threads = new wxSpinCtrl (_panel);
105                 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
106                 ++r;
107
108                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
109                 _server_encoding_threads = new wxSpinCtrl (_panel);
110                 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
111                 ++r;
112
113                 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
114                 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
115                 table->Add (_config_file, wxGBPosition (r, 1));
116                 ++r;
117
118                 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
119                 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
120                 table->Add (_cinemas_file, wxGBPosition (r, 1));
121                 Button* export_cinemas = new Button (_panel, _("Export..."));
122                 table->Add (export_cinemas, wxGBPosition (r, 2));
123                 ++r;
124
125 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
126                 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
127                 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
128                 ++r;
129 #endif
130
131                 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
132                 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
133                 ++r;
134
135                 add_update_controls (table, r);
136
137                 _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed,  this));
138                 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
139
140                 _master_encoding_threads->SetRange (1, 128);
141                 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
142                 _server_encoding_threads->SetRange (1, 128);
143                 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
144                 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
145
146 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
147                 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
148 #endif
149                 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
150         }
151
152         void config_changed () override
153         {
154                 auto config = Config::instance ();
155
156                 checked_set (_master_encoding_threads, config->master_encoding_threads ());
157                 checked_set (_server_encoding_threads, config->server_encoding_threads ());
158 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
159                 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
160 #endif
161                 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
162                 checked_set (_config_file, config->config_read_file());
163                 checked_set (_cinemas_file, config->cinemas_file());
164
165                 GeneralPage::config_changed ();
166         }
167
168         void export_cinemas_file ()
169         {
170                 auto d = new wxFileDialog (
171                         _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
172                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
173                 );
174
175                 if (d->ShowModal () == wxID_OK) {
176                         boost::filesystem::copy_file (Config::instance()->cinemas_file(), wx_to_std(d->GetPath()));
177                 }
178                 d->Destroy ();
179         }
180
181 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
182         void analyse_ebur128_changed ()
183         {
184                 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
185         }
186 #endif
187
188         void automatic_audio_analysis_changed ()
189         {
190                 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
191         }
192
193         void master_encoding_threads_changed ()
194         {
195                 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
196         }
197
198         void server_encoding_threads_changed ()
199         {
200                 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
201         }
202
203         void config_file_changed ()
204         {
205                 auto config = Config::instance();
206                 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
207                 if (new_file == config->config_read_file()) {
208                         return;
209                 }
210                 bool copy_and_link = true;
211                 if (boost::filesystem::exists(new_file)) {
212                         auto d = new ConfigMoveDialog (_panel, new_file);
213                         if (d->ShowModal() == wxID_OK) {
214                                 copy_and_link = false;
215                         }
216                         d->Destroy ();
217                 }
218
219                 if (copy_and_link) {
220                         config->write ();
221                         if (new_file != config->config_read_file()) {
222                                 config->copy_and_link (new_file);
223                         }
224                 } else {
225                         config->link (new_file);
226                 }
227         }
228
229         void cinemas_file_changed ()
230         {
231                 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
232         }
233
234         wxSpinCtrl* _master_encoding_threads;
235         wxSpinCtrl* _server_encoding_threads;
236         FilePickerCtrl* _config_file;
237         FilePickerCtrl* _cinemas_file;
238 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
239         wxCheckBox* _analyse_ebur128;
240 #endif
241         wxCheckBox* _automatic_audio_analysis;
242 };
243
244
245 class DefaultsPage : public Page
246 {
247 public:
248         DefaultsPage (wxSize panel_size, int border)
249                 : Page (panel_size, border)
250         {}
251
252         wxString GetName () const override
253         {
254                 return _("Defaults");
255         }
256
257 #ifdef DCPOMATIC_OSX
258         wxBitmap GetLargeIcon () const override
259         {
260                 return wxBitmap(bitmap_path("defaults"), wxBITMAP_TYPE_PNG);
261         }
262 #endif
263
264 private:
265         void setup () override
266         {
267                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
268                 table->AddGrowableCol (1, 1);
269                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
270
271                 {
272                         add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
273                         auto s = new wxBoxSizer (wxHORIZONTAL);
274                         _still_length = new wxSpinCtrl (_panel);
275                         s->Add (_still_length);
276                         add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
277                         table->Add (s, 1);
278                 }
279
280                 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
281 #ifdef DCPOMATIC_USE_OWN_PICKER
282                 _directory = new DirPickerCtrl (_panel);
283 #else
284                 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
285 #endif
286                 table->Add (_directory, 1, wxEXPAND);
287
288                 add_label_to_sizer (table, _panel, _("Default container"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
289                 _container = new wxChoice (_panel, wxID_ANY);
290                 table->Add (_container);
291
292                 add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
293                 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
294                 table->Add (_dcp_content_type);
295
296                 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
297                 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
298                 table->Add (_dcp_audio_channels);
299
300                 {
301                         add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
302                         auto s = new wxBoxSizer (wxHORIZONTAL);
303                         _j2k_bandwidth = new wxSpinCtrl (_panel);
304                         s->Add (_j2k_bandwidth);
305                         add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
306                         table->Add (s, 1);
307                 }
308
309                 {
310                         add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
311                         auto s = new wxBoxSizer (wxHORIZONTAL);
312                         _audio_delay = new wxSpinCtrl (_panel);
313                         s->Add (_audio_delay);
314                         add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
315                         table->Add (s, 1);
316                 }
317
318                 add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
319                 _standard = new wxChoice (_panel, wxID_ANY);
320                 table->Add (_standard);
321
322                 table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
323                 table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
324
325                 table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
326                 table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
327
328                 table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
329                 table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
330
331                 table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
332                 table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
333
334                 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
335 #ifdef DCPOMATIC_USE_OWN_PICKER
336                 _kdm_directory = new DirPickerCtrl (_panel);
337 #else
338                 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
339 #endif
340                 table->Add (_kdm_directory, 1, wxEXPAND);
341
342                 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
343                 _kdm_type = new KDMChoice (_panel);
344                 table->Add (_kdm_type, 1, wxEXPAND);
345
346                 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
347
348                 _still_length->SetRange (1, 3600);
349                 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
350
351                 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
352                 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
353                 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
354
355                 _use_isdcf_name_by_default->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::use_isdcf_name_by_default_changed, this));
356
357                 for (auto i: Ratio::containers()) {
358                         _container->Append (std_to_wx(i->container_nickname()));
359                 }
360
361                 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
362
363                 for (auto i: DCPContentType::all()) {
364                         _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
365                 }
366
367                 setup_audio_channels_choice (_dcp_audio_channels, 2);
368
369                 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
370                 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
371
372                 _j2k_bandwidth->SetRange (50, 250);
373                 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
374
375                 _audio_delay->SetRange (-1000, 1000);
376                 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
377
378                 _standard->Append (_("SMPTE"));
379                 _standard->Append (_("Interop"));
380                 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
381
382                 for (auto const& i: _enable_metadata) {
383                         i.second->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::metadata_changed, this));
384                 }
385
386                 for (auto const& i: _metadata) {
387                         i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
388                 }
389         }
390
391         void config_changed () override
392         {
393                 auto config = Config::instance ();
394
395                 auto containers = Ratio::containers ();
396                 for (size_t i = 0; i < containers.size(); ++i) {
397                         if (containers[i] == config->default_container()) {
398                                 _container->SetSelection (i);
399                         }
400                 }
401
402                 auto const ct = DCPContentType::all ();
403                 for (size_t i = 0; i < ct.size(); ++i) {
404                         if (ct[i] == config->default_dcp_content_type()) {
405                                 _dcp_content_type->SetSelection (i);
406                         }
407                 }
408
409                 checked_set (_still_length, config->default_still_length ());
410                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
411                 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
412                 _kdm_type->set (config->default_kdm_type());
413                 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
414                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
415                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
416                 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
417                 checked_set (_audio_delay, config->default_audio_delay ());
418                 checked_set (_standard, config->default_interop() ? 1 : 0);
419
420                 auto metadata = config->default_metadata();
421
422                 for (auto const& i: metadata) {
423                         _enable_metadata[i.first]->SetValue(true);
424                         checked_set (_metadata[i.first], i.second);
425                 }
426
427                 for (auto const& i: _enable_metadata) {
428                         if (metadata.find(i.first) == metadata.end()) {
429                                 checked_set (i.second, false);
430                         }
431                 }
432
433                 for (auto const& i: _metadata) {
434                         if (metadata.find(i.first) == metadata.end()) {
435                                 checked_set (i.second, wxT(""));
436                         }
437                 }
438
439                 setup_sensitivity ();
440         }
441
442         void j2k_bandwidth_changed ()
443         {
444                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
445         }
446
447         void audio_delay_changed ()
448         {
449                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
450         }
451
452         void dcp_audio_channels_changed ()
453         {
454                 int const s = _dcp_audio_channels->GetSelection ();
455                 if (s != wxNOT_FOUND) {
456                         Config::instance()->set_default_dcp_audio_channels (
457                                 locale_convert<int>(string_client_data(_dcp_audio_channels->GetClientObject(s)))
458                                 );
459                 }
460         }
461
462         void directory_changed ()
463         {
464                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
465         }
466
467         void kdm_directory_changed ()
468         {
469                 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
470         }
471
472         void kdm_type_changed ()
473         {
474                 Config::instance()->set_default_kdm_type(_kdm_type->get());
475         }
476
477         void use_isdcf_name_by_default_changed ()
478         {
479                 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
480         }
481
482         void still_length_changed ()
483         {
484                 Config::instance()->set_default_still_length (_still_length->GetValue ());
485         }
486
487         void container_changed ()
488         {
489                 auto ratio = Ratio::containers ();
490                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
491         }
492
493         void dcp_content_type_changed ()
494         {
495                 auto ct = DCPContentType::all ();
496                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
497         }
498
499         void standard_changed ()
500         {
501                 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
502         }
503
504         void metadata_changed ()
505         {
506                 map<string, string> metadata;
507                 for (auto const& i: _enable_metadata) {
508                         if (i.second->GetValue()) {
509                                 metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
510                         }
511                 }
512                 Config::instance()->set_default_metadata (metadata);
513                 setup_sensitivity ();
514         }
515
516         void setup_sensitivity ()
517         {
518                 for (auto const& i: _enable_metadata) {
519                         _metadata[i.first]->Enable(i.second->GetValue());
520                 }
521         }
522
523         wxSpinCtrl* _j2k_bandwidth;
524         wxSpinCtrl* _audio_delay;
525         wxSpinCtrl* _still_length;
526 #ifdef DCPOMATIC_USE_OWN_PICKER
527         DirPickerCtrl* _directory;
528         DirPickerCtrl* _kdm_directory;
529 #else
530         wxDirPickerCtrl* _directory;
531         wxDirPickerCtrl* _kdm_directory;
532 #endif
533         KDMChoice* _kdm_type;
534         wxCheckBox* _use_isdcf_name_by_default;
535         wxChoice* _container;
536         wxChoice* _dcp_content_type;
537         wxChoice* _dcp_audio_channels;
538         wxChoice* _standard;
539         map<string, CheckBox*> _enable_metadata;
540         map<string, wxTextCtrl*> _metadata;
541 };
542
543
544 class EncodingServersPage : public Page
545 {
546 public:
547         EncodingServersPage (wxSize panel_size, int border)
548                 : Page (panel_size, border)
549         {}
550
551         wxString GetName () const override
552         {
553                 return _("Servers");
554         }
555
556 #ifdef DCPOMATIC_OSX
557         wxBitmap GetLargeIcon () const override
558         {
559                 return wxBitmap(bitmap_path("servers"), wxBITMAP_TYPE_PNG);
560         }
561 #endif
562
563 private:
564         void setup () override
565         {
566                 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
567                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
568
569                 vector<EditableListColumn> columns;
570                 columns.push_back (EditableListColumn(_("IP address / host name")));
571                 _servers_list = new EditableList<string, ServerDialog> (
572                         _panel,
573                         columns,
574                         boost::bind (&Config::servers, Config::instance()),
575                         boost::bind (&Config::set_servers, Config::instance(), _1),
576                         boost::bind (&EncodingServersPage::server_column, this, _1)
577                         );
578
579                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
580
581                 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind(&EncodingServersPage::use_any_servers_changed, this));
582         }
583
584         void config_changed () override
585         {
586                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
587                 _servers_list->refresh ();
588         }
589
590         void use_any_servers_changed ()
591         {
592                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
593         }
594
595         string server_column (string s)
596         {
597                 return s;
598         }
599
600         wxCheckBox* _use_any_servers;
601         EditableList<string, ServerDialog>* _servers_list;
602 };
603
604
605 class TMSPage : public Page
606 {
607 public:
608         TMSPage (wxSize panel_size, int border)
609                 : Page (panel_size, border)
610         {}
611
612         wxString GetName () const override
613         {
614                 return _("TMS");
615         }
616
617 #ifdef DCPOMATIC_OSX
618         wxBitmap GetLargeIcon () const override
619         {
620                 return wxBitmap(bitmap_path("tms"), wxBITMAP_TYPE_PNG);
621         }
622 #endif
623
624 private:
625         void setup () override
626         {
627                 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
628                 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
629
630                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
631                 table->AddGrowableCol (1, 1);
632                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
633
634                 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
635                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
636                 table->Add (_tms_protocol, 1, wxEXPAND);
637
638                 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
639                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
640                 table->Add (_tms_ip, 1, wxEXPAND);
641
642                 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
643                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
644                 table->Add (_tms_path, 1, wxEXPAND);
645
646                 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
647                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
648                 table->Add (_tms_user, 1, wxEXPAND);
649
650                 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
651                 _tms_password = new PasswordEntry (_panel);
652                 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
653
654                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
655                 _tms_protocol->Append (_("FTP (for Dolby)"));
656
657                 _upload->Bind (wxEVT_CHECKBOX, boost::bind(&TMSPage::upload_changed, this));
658                 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
659                 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
660                 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
661                 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
662                 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
663         }
664
665         void config_changed () override
666         {
667                 auto config = Config::instance ();
668
669                 checked_set (_upload, config->upload_after_make_dcp());
670                 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
671                 checked_set (_tms_ip, config->tms_ip ());
672                 checked_set (_tms_path, config->tms_path ());
673                 checked_set (_tms_user, config->tms_user ());
674                 checked_set (_tms_password, config->tms_password ());
675         }
676
677         void upload_changed ()
678         {
679                 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
680         }
681
682         void tms_protocol_changed ()
683         {
684                 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
685         }
686
687         void tms_ip_changed ()
688         {
689                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
690         }
691
692         void tms_path_changed ()
693         {
694                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
695         }
696
697         void tms_user_changed ()
698         {
699                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
700         }
701
702         void tms_password_changed ()
703         {
704                 Config::instance()->set_tms_password (_tms_password->get());
705         }
706
707         CheckBox* _upload;
708         wxChoice* _tms_protocol;
709         wxTextCtrl* _tms_ip;
710         wxTextCtrl* _tms_path;
711         wxTextCtrl* _tms_user;
712         PasswordEntry* _tms_password;
713 };
714
715
716 class EmailPage : public Page
717 {
718 public:
719         EmailPage (wxSize panel_size, int border)
720                 : Page (panel_size, border)
721         {}
722
723         wxString GetName () const override
724         {
725                 return _("Email");
726         }
727
728 #ifdef DCPOMATIC_OSX
729         wxBitmap GetLargeIcon () const override
730         {
731                 return wxBitmap(bitmap_path("email"), wxBITMAP_TYPE_PNG);
732         }
733 #endif
734
735 private:
736         void setup () override
737         {
738                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
739                 table->AddGrowableCol (1, 1);
740                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
741
742                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
743                 {
744                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
745                         _server = new wxTextCtrl (_panel, wxID_ANY);
746                         s->Add (_server, 1, wxEXPAND | wxALL);
747                         add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
748                         _port = new wxSpinCtrl (_panel, wxID_ANY);
749                         _port->SetRange (0, 65535);
750                         s->Add (_port);
751                         add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
752                         _protocol = new wxChoice (_panel, wxID_ANY);
753                         /* Make sure this matches the switches in config_changed and port_changed below */
754                         _protocol->Append (_("Auto"));
755                         _protocol->Append (_("Plain"));
756                         _protocol->Append (_("STARTTLS"));
757                         _protocol->Append (_("SSL"));
758                         s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
759                         table->Add (s, 1, wxEXPAND | wxALL);
760                 }
761
762                 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
763                 _user = new wxTextCtrl (_panel, wxID_ANY);
764                 table->Add (_user, 1, wxEXPAND | wxALL);
765
766                 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
767                 _password = new PasswordEntry (_panel);
768                 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
769
770                 table->AddSpacer (0);
771                 _send_test_email = new Button (_panel, _("Send test email..."));
772                 table->Add (_send_test_email);
773
774                 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
775                 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
776                 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
777                 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
778                 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
779                 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
780         }
781
782         void config_changed () override
783         {
784                 auto config = Config::instance ();
785
786                 checked_set (_server, config->mail_server());
787                 checked_set (_port, config->mail_port());
788                 switch (config->mail_protocol()) {
789                 case EmailProtocol::AUTO:
790                         checked_set (_protocol, 0);
791                         break;
792                 case EmailProtocol::PLAIN:
793                         checked_set (_protocol, 1);
794                         break;
795                 case EmailProtocol::STARTTLS:
796                         checked_set (_protocol, 2);
797                         break;
798                 case EmailProtocol::SSL:
799                         checked_set (_protocol, 3);
800                         break;
801                 }
802                 checked_set (_user, config->mail_user());
803                 checked_set (_password, config->mail_password());
804         }
805
806         void server_changed ()
807         {
808                 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
809         }
810
811         void port_changed ()
812         {
813                 Config::instance()->set_mail_port(_port->GetValue());
814         }
815
816         void protocol_changed ()
817         {
818                 switch (_protocol->GetSelection()) {
819                 case 0:
820                         Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
821                         break;
822                 case 1:
823                         Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
824                         break;
825                 case 2:
826                         Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
827                         break;
828                 case 3:
829                         Config::instance()->set_mail_protocol(EmailProtocol::SSL);
830                         break;
831                 }
832         }
833
834         void user_changed ()
835         {
836                 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
837         }
838
839         void password_changed ()
840         {
841                 Config::instance()->set_mail_password(_password->get());
842         }
843
844         void send_test_email_clicked ()
845         {
846                 auto dialog = new SendTestEmailDialog(_panel);
847                 auto result = dialog->ShowModal();
848                 dialog->Destroy();
849                 if (result == wxID_OK) {
850                         Emailer emailer(
851                                 wx_to_std(dialog->from()),
852                                 { wx_to_std(dialog->to()) },
853                                 wx_to_std(_("DCP-o-matic test email")),
854                                 wx_to_std(_("This is a test email from DCP-o-matic."))
855                                 );
856                         auto config = Config::instance();
857                         try {
858                                 emailer.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
859                         } catch (NetworkError& e) {
860                                 error_dialog (_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
861                                 return;
862                         } catch (std::exception& e) {
863                                 error_dialog (_panel, _("Test email sending failed."), std_to_wx(e.what()));
864                                 return;
865                         } catch (...) {
866                                 error_dialog (_panel, _("Test email sending failed."));
867                                 return;
868                         }
869                         message_dialog (_panel, _("Test email sent."));
870                 }
871         }
872
873         wxTextCtrl* _server;
874         wxSpinCtrl* _port;
875         wxChoice* _protocol;
876         wxTextCtrl* _user;
877         PasswordEntry* _password;
878         Button* _send_test_email;
879 };
880
881
882 class KDMEmailPage : public Page
883 {
884 public:
885
886         KDMEmailPage (wxSize panel_size, int border)
887 #ifdef DCPOMATIC_OSX
888                 /* We have to force both width and height of this one */
889                 : Page (wxSize (panel_size.GetWidth(), 128), border)
890 #else
891                 : Page (panel_size, border)
892 #endif
893         {}
894
895         wxString GetName () const override
896         {
897                 return _("KDM Email");
898         }
899
900 #ifdef DCPOMATIC_OSX
901         wxBitmap GetLargeIcon () const override
902         {
903                 return wxBitmap(bitmap_path("kdm_email"), wxBITMAP_TYPE_PNG);
904         }
905 #endif
906
907 private:
908         void setup () override
909         {
910                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
911                 table->AddGrowableCol (1, 1);
912                 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
913
914                 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
915                 _subject = new wxTextCtrl (_panel, wxID_ANY);
916                 table->Add (_subject, 1, wxEXPAND | wxALL);
917
918                 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
919                 _from = new wxTextCtrl (_panel, wxID_ANY);
920                 table->Add (_from, 1, wxEXPAND | wxALL);
921
922                 vector<EditableListColumn> columns;
923                 columns.push_back (EditableListColumn(_("Address")));
924                 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
925                 _cc = new EditableList<string, EmailDialog> (
926                         _panel,
927                         columns,
928                         bind (&Config::kdm_cc, Config::instance()),
929                         bind (&Config::set_kdm_cc, Config::instance(), _1),
930                         [] (string s, int) {
931                                 return s;
932                         });
933                 table->Add (_cc, 1, wxEXPAND | wxALL);
934
935                 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
936                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
937                 table->Add (_bcc, 1, wxEXPAND | wxALL);
938
939                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
940                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
941
942                 _reset_email = new Button (_panel, _("Reset to default subject and text"));
943                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
944
945                 _cc->layout ();
946
947                 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
948                 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
949                 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
950                 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
951                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
952         }
953
954         void config_changed () override
955         {
956                 auto config = Config::instance ();
957
958                 checked_set (_subject, config->kdm_subject ());
959                 checked_set (_from, config->kdm_from ());
960                 checked_set (_bcc, config->kdm_bcc ());
961                 checked_set (_email, Config::instance()->kdm_email ());
962         }
963
964         void kdm_subject_changed ()
965         {
966                 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
967         }
968
969         void kdm_from_changed ()
970         {
971                 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
972         }
973
974         void kdm_bcc_changed ()
975         {
976                 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
977         }
978
979         void kdm_email_changed ()
980         {
981                 if (_email->GetValue().IsEmpty ()) {
982                         /* Sometimes we get sent an erroneous notification that the email
983                            is empty; I don't know why.
984                         */
985                         return;
986                 }
987                 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
988         }
989
990         void reset_email ()
991         {
992                 Config::instance()->reset_kdm_email ();
993                 checked_set (_email, Config::instance()->kdm_email ());
994         }
995
996         wxTextCtrl* _subject;
997         wxTextCtrl* _from;
998         EditableList<string, EmailDialog>* _cc;
999         wxTextCtrl* _bcc;
1000         wxTextCtrl* _email;
1001         wxButton* _reset_email;
1002 };
1003
1004
1005 class NotificationsPage : public Page
1006 {
1007 public:
1008         NotificationsPage (wxSize panel_size, int border)
1009 #ifdef DCPOMATIC_OSX
1010                 /* We have to force both width and height of this one */
1011                 : Page (wxSize (panel_size.GetWidth(), 128), border)
1012 #else
1013                 : Page (panel_size, border)
1014 #endif
1015         {}
1016
1017         wxString GetName () const override
1018         {
1019                 return _("Notifications");
1020         }
1021
1022 #ifdef DCPOMATIC_OSX
1023         wxBitmap GetLargeIcon () const override
1024         {
1025                 return wxBitmap(bitmap_path("notifications"), wxBITMAP_TYPE_PNG);
1026         }
1027 #endif
1028
1029 private:
1030         void setup () override
1031         {
1032                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1033                 table->AddGrowableCol (1, 1);
1034                 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1035
1036                 _enable_message_box = new CheckBox (_panel, _("Message box"));
1037                 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1038                 table->AddSpacer (0);
1039
1040                 _enable_email = new CheckBox (_panel, _("Email"));
1041                 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1042                 table->AddSpacer (0);
1043
1044                 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1045                 _subject = new wxTextCtrl (_panel, wxID_ANY);
1046                 table->Add (_subject, 1, wxEXPAND | wxALL);
1047
1048                 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1049                 _from = new wxTextCtrl (_panel, wxID_ANY);
1050                 table->Add (_from, 1, wxEXPAND | wxALL);
1051
1052                 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1053                 _to = new wxTextCtrl (_panel, wxID_ANY);
1054                 table->Add (_to, 1, wxEXPAND | wxALL);
1055
1056                 vector<EditableListColumn> columns;
1057                 columns.push_back (EditableListColumn(_("Address")));
1058                 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1059                 _cc = new EditableList<string, EmailDialog> (
1060                         _panel,
1061                         columns,
1062                         bind (&Config::notification_cc, Config::instance()),
1063                         bind (&Config::set_notification_cc, Config::instance(), _1),
1064                         [] (string s, int) {
1065                                 return s;
1066                         });
1067                 table->Add (_cc, 1, wxEXPAND | wxALL);
1068
1069                 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1070                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1071                 table->Add (_bcc, 1, wxEXPAND | wxALL);
1072
1073                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1074                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1075
1076                 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1077                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1078
1079                 _cc->layout ();
1080
1081                 _enable_message_box->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX));
1082                 _enable_email->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL));
1083
1084                 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1085                 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1086                 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1087                 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1088                 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1089                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1090
1091                 setup_sensitivity ();
1092         }
1093
1094         void setup_sensitivity ()
1095         {
1096                 bool const s = _enable_email->GetValue();
1097                 _subject->Enable(s);
1098                 _from->Enable(s);
1099                 _to->Enable(s);
1100                 _cc->Enable(s);
1101                 _bcc->Enable(s);
1102                 _email->Enable(s);
1103                 _reset_email->Enable(s);
1104         }
1105
1106         void config_changed () override
1107         {
1108                 auto config = Config::instance ();
1109
1110                 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1111                 checked_set (_enable_email, config->notification(Config::EMAIL));
1112                 checked_set (_subject, config->notification_subject ());
1113                 checked_set (_from, config->notification_from ());
1114                 checked_set (_to, config->notification_to ());
1115                 checked_set (_bcc, config->notification_bcc ());
1116                 checked_set (_email, Config::instance()->notification_email ());
1117
1118                 setup_sensitivity ();
1119         }
1120
1121         void notification_subject_changed ()
1122         {
1123                 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1124         }
1125
1126         void notification_from_changed ()
1127         {
1128                 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1129         }
1130
1131         void notification_to_changed ()
1132         {
1133                 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1134         }
1135
1136         void notification_bcc_changed ()
1137         {
1138                 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1139         }
1140
1141         void notification_email_changed ()
1142         {
1143                 if (_email->GetValue().IsEmpty()) {
1144                         /* Sometimes we get sent an erroneous notification that the email
1145                            is empty; I don't know why.
1146                         */
1147                         return;
1148                 }
1149                 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1150         }
1151
1152         void reset_email ()
1153         {
1154                 Config::instance()->reset_notification_email();
1155                 checked_set (_email, Config::instance()->notification_email());
1156         }
1157
1158         void type_changed (wxCheckBox* b, Config::Notification n)
1159         {
1160                 Config::instance()->set_notification(n, b->GetValue());
1161                 setup_sensitivity ();
1162         }
1163
1164         wxCheckBox* _enable_message_box;
1165         wxCheckBox* _enable_email;
1166
1167         wxTextCtrl* _subject;
1168         wxTextCtrl* _from;
1169         wxTextCtrl* _to;
1170         EditableList<string, EmailDialog>* _cc;
1171         wxTextCtrl* _bcc;
1172         wxTextCtrl* _email;
1173         wxButton* _reset_email;
1174 };
1175
1176
1177 class CoverSheetPage : public Page
1178 {
1179 public:
1180
1181         CoverSheetPage (wxSize panel_size, int border)
1182 #ifdef DCPOMATIC_OSX
1183                 /* We have to force both width and height of this one */
1184                 : Page (wxSize (panel_size.GetWidth(), 128), border)
1185 #else
1186                 : Page (panel_size, border)
1187 #endif
1188         {}
1189
1190         wxString GetName () const override
1191         {
1192                 return _("Cover Sheet");
1193         }
1194
1195 #ifdef DCPOMATIC_OSX
1196         wxBitmap GetLargeIcon () const override
1197         {
1198                 return wxBitmap(bitmap_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1199         }
1200 #endif
1201
1202 private:
1203         void setup () override
1204         {
1205                 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1206                 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1207
1208                 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1209                 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1210
1211                 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1212                 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1213         }
1214
1215         void config_changed () override
1216         {
1217                 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1218         }
1219
1220         void cover_sheet_changed ()
1221         {
1222                 if (_cover_sheet->GetValue().IsEmpty ()) {
1223                         /* Sometimes we get sent an erroneous notification that the cover sheet
1224                            is empty; I don't know why.
1225                         */
1226                         return;
1227                 }
1228                 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1229         }
1230
1231         void reset_cover_sheet ()
1232         {
1233                 Config::instance()->reset_cover_sheet();
1234                 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1235         }
1236
1237         wxTextCtrl* _cover_sheet;
1238         wxButton* _reset_cover_sheet;
1239 };
1240
1241
1242 class IdentifiersPage : public Page
1243 {
1244 public:
1245         IdentifiersPage (wxSize panel_size, int border)
1246                 : Page (panel_size, border)
1247         {}
1248
1249         wxString GetName () const override
1250         {
1251                 return _("Identifiers");
1252         }
1253
1254 #ifdef DCPOMATIC_OSX
1255         wxBitmap GetLargeIcon () const override
1256         {
1257                 return wxBitmap(bitmap_path("identifiers"), wxBITMAP_TYPE_PNG);
1258         }
1259 #endif
1260
1261 private:
1262         void setup () override
1263         {
1264                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1265                 table->AddGrowableCol (1, 1);
1266
1267                 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1268                 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1269                 _issuer->SetToolTip (_("This will be written to the DCP's XML files as the <Issuer>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
1270                 table->Add (_issuer, 1, wxALL | wxEXPAND);
1271
1272                 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1273                 _creator = new wxTextCtrl (_panel, wxID_ANY);
1274                 _creator->SetToolTip (_("This will be written to the DCP's XML files as the <Creator>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
1275                 table->Add (_creator, 1, wxALL | wxEXPAND);
1276
1277                 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1278                 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1279                 _company_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'company name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1280                 table->Add (_company_name, 1, wxALL | wxEXPAND);
1281
1282                 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1283                 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1284                 _product_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'product name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1285                 table->Add (_product_name, 1, wxALL | wxEXPAND);
1286
1287                 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1288                 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1289                 _product_version->SetToolTip (_("This will be written to the DCP's MXF files as the 'product version'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1290                 table->Add (_product_version, 1, wxALL | wxEXPAND);
1291
1292                 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1293                 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1294                 _j2k_comment->SetToolTip (_("This will be written to the DCP's JPEG2000 data as a comment.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1295                 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1296
1297                 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1298
1299                 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1300                 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1301                 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1302                 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1303                 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1304                 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1305         }
1306
1307         void config_changed () override
1308         {
1309                 auto config = Config::instance ();
1310                 checked_set (_issuer, config->dcp_issuer ());
1311                 checked_set (_creator, config->dcp_creator ());
1312                 checked_set (_company_name, config->dcp_company_name ());
1313                 checked_set (_product_name, config->dcp_product_name ());
1314                 checked_set (_product_version, config->dcp_product_version ());
1315                 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1316         }
1317
1318         void issuer_changed ()
1319         {
1320                 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1321         }
1322
1323         void creator_changed ()
1324         {
1325                 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1326         }
1327
1328         void company_name_changed ()
1329         {
1330                 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1331         }
1332
1333         void product_name_changed ()
1334         {
1335                 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1336         }
1337
1338         void product_version_changed ()
1339         {
1340                 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1341         }
1342
1343         void j2k_comment_changed ()
1344         {
1345                 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1346         }
1347
1348         wxTextCtrl* _issuer;
1349         wxTextCtrl* _creator;
1350         wxTextCtrl* _company_name;
1351         wxTextCtrl* _product_name;
1352         wxTextCtrl* _product_version;
1353         wxTextCtrl* _j2k_comment;
1354 };
1355
1356
1357 /** @class AdvancedPage
1358  *  @brief Advanced page of the preferences dialog.
1359  */
1360 class AdvancedPage : public Page
1361 {
1362 public:
1363         AdvancedPage (wxSize panel_size, int border)
1364                 : Page (panel_size, border)
1365         {}
1366
1367         wxString GetName () const override
1368         {
1369                 return _("Advanced");
1370         }
1371
1372 #ifdef DCPOMATIC_OSX
1373         wxBitmap GetLargeIcon () const override
1374         {
1375                 return wxBitmap(bitmap_path("advanced"), wxBITMAP_TYPE_PNG);
1376         }
1377 #endif
1378
1379 private:
1380         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1381         {
1382                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1383 #ifdef __WXOSX__
1384                 flags |= wxALIGN_RIGHT;
1385                 text += wxT (":");
1386 #endif
1387                 wxStaticText* m = new StaticText (parent, text);
1388                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1389         }
1390
1391         void setup () override
1392         {
1393                 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1394                 table->AddGrowableCol (1, 1);
1395                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1396
1397                 {
1398                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1399                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1400                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1401                         s->Add (_maximum_j2k_bandwidth, 1);
1402                         add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1403                         table->Add (s, 1);
1404                 }
1405
1406                 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1407                 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1408                 table->Add (_video_display_mode);
1409
1410                 auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1411                 auto font = restart->GetFont();
1412                 font.SetStyle (wxFONTSTYLE_ITALIC);
1413                 font.SetPointSize (font.GetPointSize() - 1);
1414                 restart->SetFont (font);
1415                 table->AddSpacer (0);
1416
1417                 _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
1418                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1419                 table->AddSpacer (0);
1420
1421                 _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
1422                 table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
1423                 table->AddSpacer (0);
1424
1425                 restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
1426                 restart->SetFont (font);
1427                 table->AddSpacer (0);
1428
1429                 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1430                 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
1431                 table->AddSpacer (0);
1432
1433                 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1434                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1435                 table->AddSpacer (0);
1436
1437                 {
1438                         add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1439                         auto s = new wxBoxSizer (wxHORIZONTAL);
1440                         _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1441                         s->Add (_frames_in_memory_multiplier, 1);
1442                         table->Add (s, 1);
1443                 }
1444
1445                 {
1446                         auto format = create_label (_panel, _("DCP metadata filename format"), true);
1447 #ifdef DCPOMATIC_OSX
1448                         auto align = new wxBoxSizer (wxHORIZONTAL);
1449                         align->Add (format, 0, wxTOP, 2);
1450                         table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1451 #else
1452                         table->Add (format, 0, wxTOP | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1453 #endif
1454                         dcp::NameFormat::Map titles;
1455                         titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1456                         dcp::NameFormat::Map examples;
1457                         examples['t'] = "cpl";
1458                         _dcp_metadata_filename_format = new NameFormatEditor (
1459                                 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1460                                 );
1461                         table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1462                 }
1463
1464                 {
1465                         auto format = create_label (_panel, _("DCP asset filename format"), true);
1466 #ifdef DCPOMATIC_OSX
1467                         auto align = new wxBoxSizer (wxHORIZONTAL);
1468                         align->Add (format, 0, wxTOP, 2);
1469                         table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1470 #else
1471                         table->Add (format, 0, wxTOP | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1472 #endif
1473                         dcp::NameFormat::Map titles;
1474                         titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1475                         titles['r'] = wx_to_std (_("reel number"));
1476                         titles['n'] = wx_to_std (_("number of reels"));
1477                         titles['c'] = wx_to_std (_("content filename"));
1478                         dcp::NameFormat::Map examples;
1479                         examples['t'] = "j2c";
1480                         examples['r'] = "1";
1481                         examples['n'] = "4";
1482                         examples['c'] = "myfile.mp4";
1483                         _dcp_asset_filename_format = new NameFormatEditor (
1484                                 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1485                                 );
1486                         table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1487                 }
1488
1489                 {
1490                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1491                         auto t = new wxFlexGridSizer (2);
1492                         _log_general = new CheckBox (_panel, _("General"));
1493                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1494                         _log_warning = new CheckBox (_panel, _("Warnings"));
1495                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1496                         _log_error = new CheckBox (_panel, _("Errors"));
1497                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1498                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1499                         _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1500                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1501                         _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1502                         t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1503                         _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1504                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1505                         _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1506                         t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1507                         _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1508                         t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1509                         _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1510                         t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1511                         _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1512                         t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1513                         table->Add (t, 0, wxALL, 6);
1514                 }
1515
1516 #ifdef DCPOMATIC_WINDOWS
1517                 _win32_console = new CheckBox (_panel, _("Open console window"));
1518                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1519                 table->AddSpacer (0);
1520 #endif
1521
1522                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1523                 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1524                 _video_display_mode->Append (_("Simple (safer)"));
1525 #if wxCHECK_VERSION(3, 1, 0)
1526                 _video_display_mode->Append (_("OpenGL (faster)"));
1527 #endif
1528                 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1529                 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1530                 _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
1531                 _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
1532                 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1533                 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1534                 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1535                 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1536                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1537                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1538                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1539                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1540                 _log_debug_threed->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1541                 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1542                 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1543                 _log_debug_video_view->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1544                 _log_debug_player->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1545                 _log_debug_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1546 #ifdef DCPOMATIC_WINDOWS
1547                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1548 #endif
1549         }
1550
1551         void config_changed () override
1552         {
1553                 auto config = Config::instance ();
1554
1555                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1556                 switch (config->video_view_type()) {
1557                 case Config::VIDEO_VIEW_SIMPLE:
1558                         checked_set (_video_display_mode, 0);
1559                         break;
1560                 case Config::VIDEO_VIEW_OPENGL:
1561                         checked_set (_video_display_mode, 1);
1562                         break;
1563                 }
1564                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1565                 checked_set (_allow_any_container, config->allow_any_container ());
1566                 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1567                 checked_set (_only_servers_encode, config->only_servers_encode ());
1568                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1569                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1570                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1571                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1572                 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1573                 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1574                 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1575                 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1576                 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1577                 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1578                 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1579 #ifdef DCPOMATIC_WINDOWS
1580                 checked_set (_win32_console, config->win32_console());
1581 #endif
1582         }
1583
1584         void maximum_j2k_bandwidth_changed ()
1585         {
1586                 Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
1587         }
1588
1589         void video_display_mode_changed ()
1590         {
1591                 if (_video_display_mode->GetSelection() == 0) {
1592                         Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1593                 } else {
1594                         Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1595                 }
1596         }
1597
1598         void frames_in_memory_multiplier_changed ()
1599         {
1600                 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1601         }
1602
1603         void allow_any_dcp_frame_rate_changed ()
1604         {
1605                 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1606         }
1607
1608         void allow_any_container_changed ()
1609         {
1610                 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1611         }
1612
1613         void show_experimental_audio_processors_changed ()
1614         {
1615                 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1616         }
1617
1618         void only_servers_encode_changed ()
1619         {
1620                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1621         }
1622
1623         void dcp_metadata_filename_format_changed ()
1624         {
1625                 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1626         }
1627
1628         void dcp_asset_filename_format_changed ()
1629         {
1630                 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1631         }
1632
1633         void log_changed ()
1634         {
1635                 int types = 0;
1636                 if (_log_general->GetValue ()) {
1637                         types |= LogEntry::TYPE_GENERAL;
1638                 }
1639                 if (_log_warning->GetValue ()) {
1640                         types |= LogEntry::TYPE_WARNING;
1641                 }
1642                 if (_log_error->GetValue ())  {
1643                         types |= LogEntry::TYPE_ERROR;
1644                 }
1645                 if (_log_timing->GetValue ()) {
1646                         types |= LogEntry::TYPE_TIMING;
1647                 }
1648                 if (_log_debug_threed->GetValue ()) {
1649                         types |= LogEntry::TYPE_DEBUG_THREE_D;
1650                 }
1651                 if (_log_debug_encode->GetValue ()) {
1652                         types |= LogEntry::TYPE_DEBUG_ENCODE;
1653                 }
1654                 if (_log_debug_email->GetValue ()) {
1655                         types |= LogEntry::TYPE_DEBUG_EMAIL;
1656                 }
1657                 if (_log_debug_video_view->GetValue()) {
1658                         types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1659                 }
1660                 if (_log_debug_player->GetValue()) {
1661                         types |= LogEntry::TYPE_DEBUG_PLAYER;
1662                 }
1663                 if (_log_debug_audio_analysis->GetValue()) {
1664                         types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1665                 }
1666                 Config::instance()->set_log_types (types);
1667         }
1668
1669 #ifdef DCPOMATIC_WINDOWS
1670         void win32_console_changed ()
1671         {
1672                 Config::instance()->set_win32_console(_win32_console->GetValue());
1673         }
1674 #endif
1675
1676         wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
1677         wxChoice* _video_display_mode = nullptr;
1678         wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1679         wxCheckBox* _allow_any_dcp_frame_rate = nullptr;
1680         wxCheckBox* _allow_any_container = nullptr;
1681         wxCheckBox* _show_experimental_audio_processors = nullptr;
1682         wxCheckBox* _only_servers_encode = nullptr;
1683         NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1684         NameFormatEditor* _dcp_asset_filename_format = nullptr;
1685         wxCheckBox* _log_general = nullptr;
1686         wxCheckBox* _log_warning = nullptr;
1687         wxCheckBox* _log_error = nullptr;
1688         wxCheckBox* _log_timing = nullptr;
1689         wxCheckBox* _log_debug_threed = nullptr;
1690         wxCheckBox* _log_debug_encode = nullptr;
1691         wxCheckBox* _log_debug_email = nullptr;
1692         wxCheckBox* _log_debug_video_view = nullptr;
1693         wxCheckBox* _log_debug_player = nullptr;
1694         wxCheckBox* _log_debug_audio_analysis = nullptr;
1695 #ifdef DCPOMATIC_WINDOWS
1696         wxCheckBox* _win32_console = nullptr;
1697 #endif
1698 };
1699
1700
1701 wxPreferencesEditor*
1702 create_full_config_dialog ()
1703 {
1704         auto e = new wxPreferencesEditor ();
1705
1706 #ifdef DCPOMATIC_OSX
1707         /* Width that we force some of the config panels to be on OSX so that
1708            the containing window doesn't shrink too much when we select those panels.
1709            This is obviously an unpleasant hack.
1710         */
1711         wxSize ps = wxSize (750, -1);
1712         int const border = 16;
1713 #else
1714         wxSize ps = wxSize (-1, -1);
1715         int const border = 8;
1716 #endif
1717
1718         e->AddPage (new FullGeneralPage    (ps, border));
1719         e->AddPage (new SoundPage          (ps, border));
1720         e->AddPage (new DefaultsPage       (ps, border));
1721         e->AddPage (new EncodingServersPage(ps, border));
1722         e->AddPage (new KeysPage           (ps, border));
1723         e->AddPage (new TMSPage            (ps, border));
1724         e->AddPage (new EmailPage          (ps, border));
1725         e->AddPage (new KDMEmailPage       (ps, border));
1726         e->AddPage (new NotificationsPage  (ps, border));
1727         e->AddPage (new CoverSheetPage     (ps, border));
1728         e->AddPage (new IdentifiersPage    (ps, border));
1729         e->AddPage (new AdvancedPage       (ps, border));
1730         return e;
1731 }