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