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