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