Add configuration option to set writer's maximum frames in memory.
[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_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true);
970                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
971                         _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
972                         s->Add (_frames_in_memory_multiplier, 1);
973                         table->Add (s, 1);
974                 }
975
976                 {
977                         add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
978                         dcp::NameFormat::Map titles;
979                         titles['t'] = "type (cpl/pkl)";
980                         dcp::NameFormat::Map examples;
981                         examples['t'] = "cpl";
982                         _dcp_metadata_filename_format = new NameFormatEditor (
983                                 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
984                                 );
985                         table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
986                 }
987
988                 {
989                         add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
990                         dcp::NameFormat::Map titles;
991                         titles['t'] = "type (j2c/pcm/sub)";
992                         titles['r'] = "reel number";
993                         titles['n'] = "number of reels";
994                         titles['c'] = "content filename";
995                         dcp::NameFormat::Map examples;
996                         examples['t'] = "j2c";
997                         examples['r'] = "1";
998                         examples['n'] = "4";
999                         examples['c'] = "myfile.mp4";
1000                         _dcp_asset_filename_format = new NameFormatEditor (
1001                                 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1002                                 );
1003                         table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1004                 }
1005
1006                 {
1007                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1008                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1009                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1010                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1011                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1012                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1013                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1014                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1015                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1016                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1017                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1018                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1019                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1020                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1021                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1022                         _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1023                         t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1024                         table->Add (t, 0, wxALL, 6);
1025                 }
1026
1027 #ifdef DCPOMATIC_WINDOWS
1028                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1029                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1030                 table->AddSpacer (0);
1031 #endif
1032
1033                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1034                 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1035                 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1036                 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1037                 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1038                 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1039                 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1040                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1041                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1042                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1043                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1044                 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1045                 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1046                 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1047 #ifdef DCPOMATIC_WINDOWS
1048                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1049 #endif
1050         }
1051
1052         void config_changed ()
1053         {
1054                 Config* config = Config::instance ();
1055
1056                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1057                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1058                 checked_set (_only_servers_encode, config->only_servers_encode ());
1059                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1060                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1061                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1062                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1063                 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1064                 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1065                 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1066                 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1067 #ifdef DCPOMATIC_WINDOWS
1068                 checked_set (_win32_console, config->win32_console());
1069 #endif
1070         }
1071
1072         void maximum_j2k_bandwidth_changed ()
1073         {
1074                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1075         }
1076
1077         void frames_in_memory_multiplier_changed ()
1078         {
1079                 Config::instance()->set_frames_in_memory_multiplier (_frames_in_memory_multiplier->GetValue());
1080         }
1081
1082         void allow_any_dcp_frame_rate_changed ()
1083         {
1084                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1085         }
1086
1087         void only_servers_encode_changed ()
1088         {
1089                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1090         }
1091
1092         void dcp_metadata_filename_format_changed ()
1093         {
1094                 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1095         }
1096
1097         void dcp_asset_filename_format_changed ()
1098         {
1099                 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1100         }
1101
1102         void log_changed ()
1103         {
1104                 int types = 0;
1105                 if (_log_general->GetValue ()) {
1106                         types |= LogEntry::TYPE_GENERAL;
1107                 }
1108                 if (_log_warning->GetValue ()) {
1109                         types |= LogEntry::TYPE_WARNING;
1110                 }
1111                 if (_log_error->GetValue ())  {
1112                         types |= LogEntry::TYPE_ERROR;
1113                 }
1114                 if (_log_timing->GetValue ()) {
1115                         types |= LogEntry::TYPE_TIMING;
1116                 }
1117                 if (_log_debug_decode->GetValue ()) {
1118                         types |= LogEntry::TYPE_DEBUG_DECODE;
1119                 }
1120                 if (_log_debug_encode->GetValue ()) {
1121                         types |= LogEntry::TYPE_DEBUG_ENCODE;
1122                 }
1123                 if (_log_debug_email->GetValue ()) {
1124                         types |= LogEntry::TYPE_DEBUG_EMAIL;
1125                 }
1126                 Config::instance()->set_log_types (types);
1127         }
1128
1129 #ifdef DCPOMATIC_WINDOWS
1130         void win32_console_changed ()
1131         {
1132                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1133         }
1134 #endif
1135
1136         wxSpinCtrl* _maximum_j2k_bandwidth;
1137         wxSpinCtrl* _frames_in_memory_multiplier;
1138         wxCheckBox* _allow_any_dcp_frame_rate;
1139         wxCheckBox* _only_servers_encode;
1140         NameFormatEditor* _dcp_metadata_filename_format;
1141         NameFormatEditor* _dcp_asset_filename_format;
1142         wxCheckBox* _log_general;
1143         wxCheckBox* _log_warning;
1144         wxCheckBox* _log_error;
1145         wxCheckBox* _log_timing;
1146         wxCheckBox* _log_debug_decode;
1147         wxCheckBox* _log_debug_encode;
1148         wxCheckBox* _log_debug_email;
1149 #ifdef DCPOMATIC_WINDOWS
1150         wxCheckBox* _win32_console;
1151 #endif
1152 };
1153
1154 wxPreferencesEditor*
1155 create_full_config_dialog ()
1156 {
1157         wxPreferencesEditor* e = new wxPreferencesEditor ();
1158
1159 #ifdef DCPOMATIC_OSX
1160         /* Width that we force some of the config panels to be on OSX so that
1161            the containing window doesn't shrink too much when we select those panels.
1162            This is obviously an unpleasant hack.
1163         */
1164         wxSize ps = wxSize (520, -1);
1165         int const border = 16;
1166 #else
1167         wxSize ps = wxSize (-1, -1);
1168         int const border = 8;
1169 #endif
1170
1171         e->AddPage (new FullGeneralPage (ps, border));
1172         e->AddPage (new DefaultsPage (ps, border));
1173         e->AddPage (new EncodingServersPage (ps, border));
1174         e->AddPage (new KeysPage (ps, border, true));
1175         e->AddPage (new TMSPage (ps, border));
1176         e->AddPage (new KDMEmailPage (ps, border));
1177         e->AddPage (new CoverSheetPage (ps, border));
1178         e->AddPage (new AdvancedPage (ps, border));
1179         return e;
1180 }