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