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