Add KDM decryption keys prefs to player.
[dcpomatic.git] / src / wx / full_config_dialog.cc
1 /*
2     Copyright (C) 2012-2017 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 "lib/config.h"
40 #include "lib/ratio.h"
41 #include "lib/filter.h"
42 #include "lib/dcp_content_type.h"
43 #include "lib/log.h"
44 #include "lib/util.h"
45 #include "lib/cross.h"
46 #include "lib/exceptions.h"
47 #include <dcp/locale_convert.h>
48 #include <dcp/exceptions.h>
49 #include <dcp/certificate_chain.h>
50 #include <wx/stdpaths.h>
51 #include <wx/preferences.h>
52 #include <wx/spinctrl.h>
53 #include <wx/filepicker.h>
54 #include <RtAudio.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/foreach.hpp>
57 #include <iostream>
58
59 using std::vector;
60 using std::string;
61 using std::list;
62 using std::cout;
63 using std::pair;
64 using std::make_pair;
65 using std::map;
66 using boost::bind;
67 using boost::shared_ptr;
68 using boost::function;
69 using boost::optional;
70 using dcp::locale_convert;
71
72 class FullGeneralPage : public GeneralPage
73 {
74 public:
75         FullGeneralPage (wxSize panel_size, int border)
76                 : GeneralPage (panel_size, border)
77         {}
78
79 private:
80         void setup ()
81         {
82                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
84
85                 int r = 0;
86                 add_language_controls (table, r);
87
88                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
89                 _master_encoding_threads = new wxSpinCtrl (_panel);
90                 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
91                 ++r;
92
93                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
94                 _server_encoding_threads = new wxSpinCtrl (_panel);
95                 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
96                 ++r;
97
98                 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
99                 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true);
100                 table->Add (_config_file, wxGBPosition (r, 1));
101                 ++r;
102
103                 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
104                 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
105                 table->Add (_cinemas_file, wxGBPosition (r, 1));
106                 ++r;
107
108                 add_play_sound_controls (table, r);
109
110 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
111                 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
112                 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
113                 ++r;
114 #endif
115
116                 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
117                 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
118                 ++r;
119
120                 add_update_controls (table, r);
121
122                 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
123                 bottom_table->AddGrowableCol (1, 1);
124
125                 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
126                 _issuer = new wxTextCtrl (_panel, wxID_ANY);
127                 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
128
129                 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
130                 _creator = new wxTextCtrl (_panel, wxID_ANY);
131                 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
132
133                 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
134                 ++r;
135
136                 _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::config_file_changed,   this));
137                 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::cinemas_file_changed,  this));
138
139                 _master_encoding_threads->SetRange (1, 128);
140                 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
141                 _server_encoding_threads->SetRange (1, 128);
142                 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
143
144 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
145                 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
146 #endif
147                 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
148
149                 _issuer->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::issuer_changed, this));
150                 _creator->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::creator_changed, this));
151         }
152
153         void config_changed ()
154         {
155                 Config* config = Config::instance ();
156
157                 checked_set (_master_encoding_threads, config->master_encoding_threads ());
158                 checked_set (_server_encoding_threads, config->server_encoding_threads ());
159 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
160                 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
161 #endif
162                 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
163                 checked_set (_issuer, config->dcp_issuer ());
164                 checked_set (_creator, config->dcp_creator ());
165                 checked_set (_config_file, config->config_file());
166                 checked_set (_cinemas_file, config->cinemas_file());
167
168                 GeneralPage::config_changed ();
169         }
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 issuer_changed ()
195         {
196                 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
197         }
198
199         void creator_changed ()
200         {
201                 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
202         }
203
204         void config_file_changed ()
205         {
206                 Config* config = Config::instance();
207                 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
208                 if (new_file == config->config_file()) {
209                         return;
210                 }
211                 bool copy_and_link = true;
212                 if (boost::filesystem::exists(new_file)) {
213                         ConfigMoveDialog* d = new ConfigMoveDialog (_panel, new_file);
214                         if (d->ShowModal() == wxID_OK) {
215                                 copy_and_link = false;
216                         }
217                         d->Destroy ();
218                 }
219
220                 if (copy_and_link) {
221                         config->write ();
222                         if (new_file != config->config_file()) {
223                                 config->copy_and_link (new_file);
224                         }
225                 } else {
226                         config->link (new_file);
227                 }
228         }
229
230         void cinemas_file_changed ()
231         {
232                 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
233         }
234
235         wxSpinCtrl* _master_encoding_threads;
236         wxSpinCtrl* _server_encoding_threads;
237         FilePickerCtrl* _config_file;
238         FilePickerCtrl* _cinemas_file;
239 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
240         wxCheckBox* _analyse_ebur128;
241 #endif
242         wxCheckBox* _automatic_audio_analysis;
243         wxTextCtrl* _issuer;
244         wxTextCtrl* _creator;
245 };
246
247 class DefaultsPage : public StandardPage
248 {
249 public:
250         DefaultsPage (wxSize panel_size, int border)
251                 : StandardPage (panel_size, border)
252         {}
253
254         wxString GetName () const
255         {
256                 return _("Defaults");
257         }
258
259 #ifdef DCPOMATIC_OSX
260         wxBitmap GetLargeIcon () const
261         {
262                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
263         }
264 #endif
265
266 private:
267         void setup ()
268         {
269                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
270                 table->AddGrowableCol (1, 1);
271                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
272
273                 {
274                         add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
275                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
276                         _still_length = new wxSpinCtrl (_panel);
277                         s->Add (_still_length);
278                         add_label_to_sizer (s, _panel, _("s"), false);
279                         table->Add (s, 1);
280                 }
281
282                 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
283 #ifdef DCPOMATIC_USE_OWN_PICKER
284                 _directory = new DirPickerCtrl (_panel);
285 #else
286                 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
287 #endif
288                 table->Add (_directory, 1, wxEXPAND);
289
290                 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
291                 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
292                 table->Add (_isdcf_metadata_button);
293
294                 add_label_to_sizer (table, _panel, _("Default container"), true);
295                 _container = new wxChoice (_panel, wxID_ANY);
296                 table->Add (_container);
297
298                 add_label_to_sizer (table, _panel, _("Default scale-to"), true);
299                 _scale_to = new wxChoice (_panel, wxID_ANY);
300                 table->Add (_scale_to);
301
302                 add_label_to_sizer (table, _panel, _("Default content type"), true);
303                 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
304                 table->Add (_dcp_content_type);
305
306                 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
307                 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
308                 table->Add (_dcp_audio_channels);
309
310                 {
311                         add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
312                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
313                         _j2k_bandwidth = new wxSpinCtrl (_panel);
314                         s->Add (_j2k_bandwidth);
315                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
316                         table->Add (s, 1);
317                 }
318
319                 {
320                         add_label_to_sizer (table, _panel, _("Default audio delay"), true);
321                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
322                         _audio_delay = new wxSpinCtrl (_panel);
323                         s->Add (_audio_delay);
324                         add_label_to_sizer (s, _panel, _("ms"), false);
325                         table->Add (s, 1);
326                 }
327
328                 add_label_to_sizer (table, _panel, _("Default standard"), true);
329                 _standard = new wxChoice (_panel, wxID_ANY);
330                 table->Add (_standard);
331
332                 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
333 #ifdef DCPOMATIC_USE_OWN_PICKER
334                 _kdm_directory = new DirPickerCtrl (_panel);
335 #else
336                 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
337 #endif
338                 table->Add (_kdm_directory, 1, wxEXPAND);
339
340                 _still_length->SetRange (1, 3600);
341                 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
342
343                 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
344                 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
345
346                 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
347
348                 BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
349                         _container->Append (std_to_wx(i->container_nickname()));
350                 }
351
352                 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
353
354                 _scale_to->Append (_("Guess from content"));
355
356                 BOOST_FOREACH (Ratio const * i, Ratio::all()) {
357                         _scale_to->Append (std_to_wx(i->image_nickname()));
358                 }
359
360                 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
361
362                 BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
363                         _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
364                 }
365
366                 setup_audio_channels_choice (_dcp_audio_channels, 2);
367
368                 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
369                 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
370
371                 _j2k_bandwidth->SetRange (50, 250);
372                 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
373
374                 _audio_delay->SetRange (-1000, 1000);
375                 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
376
377                 _standard->Append (_("SMPTE"));
378                 _standard->Append (_("Interop"));
379                 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
380         }
381
382         void config_changed ()
383         {
384                 Config* config = Config::instance ();
385
386                 vector<Ratio const *> containers = Ratio::containers ();
387                 for (size_t i = 0; i < containers.size(); ++i) {
388                         if (containers[i] == config->default_container ()) {
389                                 _container->SetSelection (i);
390                         }
391                 }
392
393                 vector<Ratio const *> ratios = Ratio::all ();
394                 for (size_t i = 0; i < ratios.size(); ++i) {
395                         if (ratios[i] == config->default_scale_to ()) {
396                                 _scale_to->SetSelection (i + 1);
397                         }
398                 }
399
400                 if (!config->default_scale_to()) {
401                         _scale_to->SetSelection (0);
402                 }
403
404                 vector<DCPContentType const *> const ct = DCPContentType::all ();
405                 for (size_t i = 0; i < ct.size(); ++i) {
406                         if (ct[i] == config->default_dcp_content_type ()) {
407                                 _dcp_content_type->SetSelection (i);
408                         }
409                 }
410
411                 checked_set (_still_length, config->default_still_length ());
412                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
413                 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
414                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
415                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
416                 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
417                 checked_set (_audio_delay, config->default_audio_delay ());
418                 checked_set (_standard, config->default_interop() ? 1 : 0);
419         }
420
421         void j2k_bandwidth_changed ()
422         {
423                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
424         }
425
426         void audio_delay_changed ()
427         {
428                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
429         }
430
431         void dcp_audio_channels_changed ()
432         {
433                 int const s = _dcp_audio_channels->GetSelection ();
434                 if (s != wxNOT_FOUND) {
435                         Config::instance()->set_default_dcp_audio_channels (
436                                 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
437                                 );
438                 }
439         }
440
441         void directory_changed ()
442         {
443                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
444         }
445
446         void kdm_directory_changed ()
447         {
448                 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
449         }
450
451         void edit_isdcf_metadata_clicked ()
452         {
453                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
454                 d->ShowModal ();
455                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
456                 d->Destroy ();
457         }
458
459         void still_length_changed ()
460         {
461                 Config::instance()->set_default_still_length (_still_length->GetValue ());
462         }
463
464         void container_changed ()
465         {
466                 vector<Ratio const *> ratio = Ratio::containers ();
467                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
468         }
469
470         void scale_to_changed ()
471         {
472                 int const s = _scale_to->GetSelection ();
473                 if (s == 0) {
474                         Config::instance()->set_default_scale_to (0);
475                 } else {
476                         vector<Ratio const *> ratio = Ratio::all ();
477                         Config::instance()->set_default_scale_to (ratio[s - 1]);
478                 }
479         }
480
481         void dcp_content_type_changed ()
482         {
483                 vector<DCPContentType const *> ct = DCPContentType::all ();
484                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
485         }
486
487         void standard_changed ()
488         {
489                 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
490         }
491
492         wxSpinCtrl* _j2k_bandwidth;
493         wxSpinCtrl* _audio_delay;
494         wxButton* _isdcf_metadata_button;
495         wxSpinCtrl* _still_length;
496 #ifdef DCPOMATIC_USE_OWN_PICKER
497         DirPickerCtrl* _directory;
498         DirPickerCtrl* _kdm_directory;
499 #else
500         wxDirPickerCtrl* _directory;
501         wxDirPickerCtrl* _kdm_directory;
502 #endif
503         wxChoice* _container;
504         wxChoice* _scale_to;
505         wxChoice* _dcp_content_type;
506         wxChoice* _dcp_audio_channels;
507         wxChoice* _standard;
508 };
509
510 class EncodingServersPage : public StandardPage
511 {
512 public:
513         EncodingServersPage (wxSize panel_size, int border)
514                 : StandardPage (panel_size, border)
515         {}
516
517         wxString GetName () const
518         {
519                 return _("Servers");
520         }
521
522 #ifdef DCPOMATIC_OSX
523         wxBitmap GetLargeIcon () const
524         {
525                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
526         }
527 #endif
528
529 private:
530         void setup ()
531         {
532                 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
533                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
534
535                 vector<string> columns;
536                 columns.push_back (wx_to_std (_("IP address / host name")));
537                 _servers_list = new EditableList<string, ServerDialog> (
538                         _panel,
539                         columns,
540                         boost::bind (&Config::servers, Config::instance()),
541                         boost::bind (&Config::set_servers, Config::instance(), _1),
542                         boost::bind (&EncodingServersPage::server_column, this, _1)
543                         );
544
545                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
546
547                 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
548         }
549
550         void config_changed ()
551         {
552                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
553                 _servers_list->refresh ();
554         }
555
556         void use_any_servers_changed ()
557         {
558                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
559         }
560
561         string server_column (string s)
562         {
563                 return s;
564         }
565
566         wxCheckBox* _use_any_servers;
567         EditableList<string, ServerDialog>* _servers_list;
568 };
569
570 class TMSPage : public StandardPage
571 {
572 public:
573         TMSPage (wxSize panel_size, int border)
574                 : StandardPage (panel_size, border)
575         {}
576
577         wxString GetName () const
578         {
579                 return _("TMS");
580         }
581
582 #ifdef DCPOMATIC_OSX
583         wxBitmap GetLargeIcon () const
584         {
585                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
586         }
587 #endif
588
589 private:
590         void setup ()
591         {
592                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
593                 table->AddGrowableCol (1, 1);
594                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
595
596                 add_label_to_sizer (table, _panel, _("Protocol"), true);
597                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
598                 table->Add (_tms_protocol, 1, wxEXPAND);
599
600                 add_label_to_sizer (table, _panel, _("IP address"), true);
601                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
602                 table->Add (_tms_ip, 1, wxEXPAND);
603
604                 add_label_to_sizer (table, _panel, _("Target path"), true);
605                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
606                 table->Add (_tms_path, 1, wxEXPAND);
607
608                 add_label_to_sizer (table, _panel, _("User name"), true);
609                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
610                 table->Add (_tms_user, 1, wxEXPAND);
611
612                 add_label_to_sizer (table, _panel, _("Password"), true);
613                 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
614                 table->Add (_tms_password, 1, wxEXPAND);
615
616                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
617                 _tms_protocol->Append (_("FTP (for Dolby)"));
618
619                 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
620                 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
621                 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
622                 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
623                 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
624         }
625
626         void config_changed ()
627         {
628                 Config* config = Config::instance ();
629
630                 checked_set (_tms_protocol, config->tms_protocol ());
631                 checked_set (_tms_ip, config->tms_ip ());
632                 checked_set (_tms_path, config->tms_path ());
633                 checked_set (_tms_user, config->tms_user ());
634                 checked_set (_tms_password, config->tms_password ());
635         }
636
637         void tms_protocol_changed ()
638         {
639                 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
640         }
641
642         void tms_ip_changed ()
643         {
644                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
645         }
646
647         void tms_path_changed ()
648         {
649                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
650         }
651
652         void tms_user_changed ()
653         {
654                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
655         }
656
657         void tms_password_changed ()
658         {
659                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
660         }
661
662         wxChoice* _tms_protocol;
663         wxTextCtrl* _tms_ip;
664         wxTextCtrl* _tms_path;
665         wxTextCtrl* _tms_user;
666         wxTextCtrl* _tms_password;
667 };
668
669 static string
670 column (string s)
671 {
672         return s;
673 }
674
675 class KDMEmailPage : public StandardPage
676 {
677 public:
678
679         KDMEmailPage (wxSize panel_size, int border)
680 #ifdef DCPOMATIC_OSX
681                 /* We have to force both width and height of this one */
682                 : StandardPage (wxSize (480, 128), border)
683 #else
684                 : StandardPage (panel_size, border)
685 #endif
686         {}
687
688         wxString GetName () const
689         {
690                 return _("KDM Email");
691         }
692
693 #ifdef DCPOMATIC_OSX
694         wxBitmap GetLargeIcon () const
695         {
696                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
697         }
698 #endif
699
700 private:
701         void setup ()
702         {
703                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
704                 table->AddGrowableCol (1, 1);
705                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
706
707                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
708                 {
709                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
710                         _mail_server = new wxTextCtrl (_panel, wxID_ANY);
711                         s->Add (_mail_server, 1, wxEXPAND | wxALL);
712                         add_label_to_sizer (s, _panel, _("port"), false);
713                         _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
714                         _mail_port->SetRange (0, 65535);
715                         s->Add (_mail_port);
716                         table->Add (s, 1, wxEXPAND | wxALL);
717                 }
718
719                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
720                 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
721                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
722
723                 add_label_to_sizer (table, _panel, _("Mail password"), true);
724                 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
725                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
726
727                 add_label_to_sizer (table, _panel, _("Subject"), true);
728                 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
729                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
730
731                 add_label_to_sizer (table, _panel, _("From address"), true);
732                 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
733                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
734
735                 vector<string> columns;
736                 columns.push_back (wx_to_std (_("Address")));
737                 add_label_to_sizer (table, _panel, _("CC addresses"), true);
738                 _kdm_cc = new EditableList<string, EmailDialog> (
739                         _panel,
740                         columns,
741                         bind (&Config::kdm_cc, Config::instance()),
742                         bind (&Config::set_kdm_cc, Config::instance(), _1),
743                         bind (&column, _1)
744                         );
745                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
746
747                 add_label_to_sizer (table, _panel, _("BCC address"), true);
748                 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
749                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
750
751                 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
752                 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
753
754                 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
755                 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
756
757                 _kdm_cc->layout ();
758
759                 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
760                 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
761                 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
762                 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
763                 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
764                 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
765                 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
766                 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
767                 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
768         }
769
770         void config_changed ()
771         {
772                 Config* config = Config::instance ();
773
774                 checked_set (_mail_server, config->mail_server ());
775                 checked_set (_mail_port, config->mail_port ());
776                 checked_set (_mail_user, config->mail_user ());
777                 checked_set (_mail_password, config->mail_password ());
778                 checked_set (_kdm_subject, config->kdm_subject ());
779                 checked_set (_kdm_from, config->kdm_from ());
780                 checked_set (_kdm_bcc, config->kdm_bcc ());
781                 checked_set (_kdm_email, Config::instance()->kdm_email ());
782         }
783
784         void mail_server_changed ()
785         {
786                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
787         }
788
789         void mail_port_changed ()
790         {
791                 Config::instance()->set_mail_port (_mail_port->GetValue ());
792         }
793
794         void mail_user_changed ()
795         {
796                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
797         }
798
799         void mail_password_changed ()
800         {
801                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
802         }
803
804         void kdm_subject_changed ()
805         {
806                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
807         }
808
809         void kdm_from_changed ()
810         {
811                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
812         }
813
814         void kdm_bcc_changed ()
815         {
816                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
817         }
818
819         void kdm_email_changed ()
820         {
821                 if (_kdm_email->GetValue().IsEmpty ()) {
822                         /* Sometimes we get sent an erroneous notification that the email
823                            is empty; I don't know why.
824                         */
825                         return;
826                 }
827                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
828         }
829
830         void reset_kdm_email ()
831         {
832                 Config::instance()->reset_kdm_email ();
833                 checked_set (_kdm_email, Config::instance()->kdm_email ());
834         }
835
836         wxTextCtrl* _mail_server;
837         wxSpinCtrl* _mail_port;
838         wxTextCtrl* _mail_user;
839         wxTextCtrl* _mail_password;
840         wxTextCtrl* _kdm_subject;
841         wxTextCtrl* _kdm_from;
842         EditableList<string, EmailDialog>* _kdm_cc;
843         wxTextCtrl* _kdm_bcc;
844         wxTextCtrl* _kdm_email;
845         wxButton* _reset_kdm_email;
846 };
847
848 class CoverSheetPage : public StandardPage
849 {
850 public:
851
852         CoverSheetPage (wxSize panel_size, int border)
853 #ifdef DCPOMATIC_OSX
854                 /* We have to force both width and height of this one */
855                 : StandardPage (wxSize (480, 128), border)
856 #else
857                 : StandardPage (panel_size, border)
858 #endif
859         {}
860
861         wxString GetName () const
862         {
863                 return _("Cover Sheet");
864         }
865
866 #ifdef DCPOMATIC_OSX
867         wxBitmap GetLargeIcon () const
868         {
869                 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
870         }
871 #endif
872
873 private:
874         void setup ()
875         {
876                 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
877                 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
878
879                 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
880                 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
881
882                 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
883                 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
884         }
885
886         void config_changed ()
887         {
888                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
889         }
890
891         void cover_sheet_changed ()
892         {
893                 if (_cover_sheet->GetValue().IsEmpty ()) {
894                         /* Sometimes we get sent an erroneous notification that the cover sheet
895                            is empty; I don't know why.
896                         */
897                         return;
898                 }
899                 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
900         }
901
902         void reset_cover_sheet ()
903         {
904                 Config::instance()->reset_cover_sheet ();
905                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
906         }
907
908         wxTextCtrl* _cover_sheet;
909         wxButton* _reset_cover_sheet;
910 };
911
912
913 /** @class AdvancedPage
914  *  @brief Advanced page of the preferences dialog.
915  */
916 class AdvancedPage : public StockPage
917 {
918 public:
919         AdvancedPage (wxSize panel_size, int border)
920                 : StockPage (Kind_Advanced, panel_size, border)
921                 , _maximum_j2k_bandwidth (0)
922                 , _allow_any_dcp_frame_rate (0)
923                 , _only_servers_encode (0)
924                 , _log_general (0)
925                 , _log_warning (0)
926                 , _log_error (0)
927                 , _log_timing (0)
928                 , _log_debug_decode (0)
929                 , _log_debug_encode (0)
930                 , _log_debug_email (0)
931         {}
932
933 private:
934         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
935         {
936                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
937 #ifdef __WXOSX__
938                 flags |= wxALIGN_RIGHT;
939                 text += wxT (":");
940 #endif
941                 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
942                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
943         }
944
945         void setup ()
946         {
947                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
948                 table->AddGrowableCol (1, 1);
949                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
950
951                 {
952                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
953                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
954                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
955                         s->Add (_maximum_j2k_bandwidth, 1);
956                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
957                         table->Add (s, 1);
958                 }
959
960                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
961                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
962                 table->AddSpacer (0);
963
964                 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
965                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
966                 table->AddSpacer (0);
967
968                 {
969                         add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
970                         dcp::NameFormat::Map titles;
971                         titles['t'] = "type (cpl/pkl)";
972                         dcp::NameFormat::Map examples;
973                         examples['t'] = "cpl";
974                         _dcp_metadata_filename_format = new NameFormatEditor (
975                                 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
976                                 );
977                         table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
978                 }
979
980                 {
981                         add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
982                         dcp::NameFormat::Map titles;
983                         titles['t'] = "type (j2c/pcm/sub)";
984                         titles['r'] = "reel number";
985                         titles['n'] = "number of reels";
986                         titles['c'] = "content filename";
987                         dcp::NameFormat::Map examples;
988                         examples['t'] = "j2c";
989                         examples['r'] = "1";
990                         examples['n'] = "4";
991                         examples['c'] = "myfile.mp4";
992                         _dcp_asset_filename_format = new NameFormatEditor (
993                                 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
994                                 );
995                         table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
996                 }
997
998                 {
999                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1000                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1001                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1002                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1003                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1004                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1005                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1006                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1007                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1008                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1009                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1010                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1011                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1012                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1013                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1014                         _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1015                         t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1016                         table->Add (t, 0, wxALL, 6);
1017                 }
1018
1019 #ifdef DCPOMATIC_WINDOWS
1020                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1021                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1022                 table->AddSpacer (0);
1023 #endif
1024
1025                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1026                 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1027                 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1028                 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1029                 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1030                 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1031                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1032                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1033                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1034                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1035                 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1036                 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1037                 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1038 #ifdef DCPOMATIC_WINDOWS
1039                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1040 #endif
1041         }
1042
1043         void config_changed ()
1044         {
1045                 Config* config = Config::instance ();
1046
1047                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1048                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1049                 checked_set (_only_servers_encode, config->only_servers_encode ());
1050                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1051                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1052                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1053                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1054                 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1055                 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1056                 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1057 #ifdef DCPOMATIC_WINDOWS
1058                 checked_set (_win32_console, config->win32_console());
1059 #endif
1060         }
1061
1062         void maximum_j2k_bandwidth_changed ()
1063         {
1064                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1065         }
1066
1067         void allow_any_dcp_frame_rate_changed ()
1068         {
1069                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1070         }
1071
1072         void only_servers_encode_changed ()
1073         {
1074                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1075         }
1076
1077         void dcp_metadata_filename_format_changed ()
1078         {
1079                 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1080         }
1081
1082         void dcp_asset_filename_format_changed ()
1083         {
1084                 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1085         }
1086
1087         void log_changed ()
1088         {
1089                 int types = 0;
1090                 if (_log_general->GetValue ()) {
1091                         types |= LogEntry::TYPE_GENERAL;
1092                 }
1093                 if (_log_warning->GetValue ()) {
1094                         types |= LogEntry::TYPE_WARNING;
1095                 }
1096                 if (_log_error->GetValue ())  {
1097                         types |= LogEntry::TYPE_ERROR;
1098                 }
1099                 if (_log_timing->GetValue ()) {
1100                         types |= LogEntry::TYPE_TIMING;
1101                 }
1102                 if (_log_debug_decode->GetValue ()) {
1103                         types |= LogEntry::TYPE_DEBUG_DECODE;
1104                 }
1105                 if (_log_debug_encode->GetValue ()) {
1106                         types |= LogEntry::TYPE_DEBUG_ENCODE;
1107                 }
1108                 if (_log_debug_email->GetValue ()) {
1109                         types |= LogEntry::TYPE_DEBUG_EMAIL;
1110                 }
1111                 Config::instance()->set_log_types (types);
1112         }
1113
1114 #ifdef DCPOMATIC_WINDOWS
1115         void win32_console_changed ()
1116         {
1117                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1118         }
1119 #endif
1120
1121         wxSpinCtrl* _maximum_j2k_bandwidth;
1122         wxCheckBox* _allow_any_dcp_frame_rate;
1123         wxCheckBox* _only_servers_encode;
1124         NameFormatEditor* _dcp_metadata_filename_format;
1125         NameFormatEditor* _dcp_asset_filename_format;
1126         wxCheckBox* _log_general;
1127         wxCheckBox* _log_warning;
1128         wxCheckBox* _log_error;
1129         wxCheckBox* _log_timing;
1130         wxCheckBox* _log_debug_decode;
1131         wxCheckBox* _log_debug_encode;
1132         wxCheckBox* _log_debug_email;
1133 #ifdef DCPOMATIC_WINDOWS
1134         wxCheckBox* _win32_console;
1135 #endif
1136 };
1137
1138 wxPreferencesEditor*
1139 create_full_config_dialog ()
1140 {
1141         wxPreferencesEditor* e = new wxPreferencesEditor ();
1142
1143 #ifdef DCPOMATIC_OSX
1144         /* Width that we force some of the config panels to be on OSX so that
1145            the containing window doesn't shrink too much when we select those panels.
1146            This is obviously an unpleasant hack.
1147         */
1148         wxSize ps = wxSize (520, -1);
1149         int const border = 16;
1150 #else
1151         wxSize ps = wxSize (-1, -1);
1152         int const border = 8;
1153 #endif
1154
1155         e->AddPage (new FullGeneralPage (ps, border));
1156         e->AddPage (new DefaultsPage (ps, border));
1157         e->AddPage (new EncodingServersPage (ps, border));
1158         e->AddPage (new KeysPage (ps, border, true));
1159         e->AddPage (new TMSPage (ps, border));
1160         e->AddPage (new KDMEmailPage (ps, border));
1161         e->AddPage (new CoverSheetPage (ps, border));
1162         e->AddPage (new AdvancedPage (ps, border));
1163         return e;
1164 }