Merge remote-tracking branch 'origin/master' into 2.0
[dcpomatic.git] / src / wx / config_dialog.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file src/config_dialog.cc
21  *  @brief A dialogue to edit DCP-o-matic configuration.
22  */
23
24 #include <iostream>
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/colour_matrix.h>
32 #include <dcp/exceptions.h>
33 #include <dcp/signer.h>
34 #include "lib/config.h"
35 #include "lib/ratio.h"
36 #include "lib/scaler.h"
37 #include "lib/filter.h"
38 #include "lib/dcp_content_type.h"
39 #include "lib/colour_conversion.h"
40 #include "lib/log.h"
41 #include "lib/util.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include "config_dialog.h"
45 #include "wx_util.h"
46 #include "editable_list.h"
47 #include "filter_dialog.h"
48 #include "dir_picker_ctrl.h"
49 #include "isdcf_metadata_dialog.h"
50 #include "preset_colour_conversion_dialog.h"
51 #include "server_dialog.h"
52 #include "make_signer_chain_dialog.h"
53
54 using std::vector;
55 using std::string;
56 using std::list;
57 using std::cout;
58 using boost::bind;
59 using boost::shared_ptr;
60 using boost::lexical_cast;
61
62 class Page
63 {
64 public:
65         Page (wxSize panel_size, int border)
66                 : _panel_size (panel_size)
67                 , _border (border)
68         {}
69
70 protected:
71         wxPanel* make_panel (wxWindow* parent)
72         {
73                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
74                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
75                 panel->SetSizer (s);
76                 return panel;
77         }
78         
79         wxSize _panel_size;
80         int _border;
81 };
82
83 class GeneralPage : public wxStockPreferencesPage, public Page
84 {
85 public:
86         GeneralPage (wxSize panel_size, int border)
87                 : wxStockPreferencesPage (Kind_General)
88                 , Page (panel_size, border)
89         {}
90
91         wxWindow* CreateWindow (wxWindow* parent)
92         {
93                 wxPanel* panel = make_panel (parent);
94
95                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
96                 table->AddGrowableCol (1, 1);
97                 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
98                 
99                 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
100                 table->Add (_set_language, 1);
101                 _language = new wxChoice (panel, wxID_ANY);
102                 _language->Append (wxT ("Deutsch"));
103                 _language->Append (wxT ("English"));
104                 _language->Append (wxT ("Español"));
105                 _language->Append (wxT ("Français"));
106                 _language->Append (wxT ("Italiano"));
107                 _language->Append (wxT ("Nederlands"));
108                 _language->Append (wxT ("Svenska"));
109                 table->Add (_language);
110                 
111                 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
112                 wxFont font = restart->GetFont();
113                 font.SetStyle (wxFONTSTYLE_ITALIC);
114                 font.SetPointSize (font.GetPointSize() - 1);
115                 restart->SetFont (font);
116                 table->AddSpacer (0);
117                 
118                 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
119                 _num_local_encoding_threads = new wxSpinCtrl (panel);
120                 table->Add (_num_local_encoding_threads, 1);
121
122                 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
123                 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
124                 table->AddSpacer (0);
125                 
126                 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
127                 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
128                 table->AddSpacer (0);
129                 
130                 Config* config = Config::instance ();
131                 
132                 _set_language->SetValue (config->language ());
133                 
134                 if (config->language().get_value_or ("") == "fr") {
135                         _language->SetSelection (3);
136                 } else if (config->language().get_value_or ("") == "it") {
137                         _language->SetSelection (4);
138                 } else if (config->language().get_value_or ("") == "es") {
139                         _language->SetSelection (2);
140                 } else if (config->language().get_value_or ("") == "sv") {
141                         _language->SetSelection (6);
142                 } else if (config->language().get_value_or ("") == "de") {
143                         _language->SetSelection (0);
144                 } else if (config->language().get_value_or ("") == "nl") {
145                         _language->SetSelection (5);
146                 } else {
147                         _language->SetSelection (1);
148                 }
149                 
150                 setup_language_sensitivity ();
151                 
152                 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
153                 _language->Bind     (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&GeneralPage::language_changed,     this));
154                 
155                 _num_local_encoding_threads->SetRange (1, 128);
156                 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
157                 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
158
159                 _check_for_updates->SetValue (config->check_for_updates ());
160                 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
161                 _check_for_test_updates->SetValue (config->check_for_test_updates ());
162                 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
163                 
164                 return panel;
165         }
166
167 private:        
168         void setup_language_sensitivity ()
169         {
170                 _language->Enable (_set_language->GetValue ());
171         }
172
173         void set_language_changed ()
174         {
175                 setup_language_sensitivity ();
176                 if (_set_language->GetValue ()) {
177                         language_changed ();
178                 } else {
179                         Config::instance()->unset_language ();
180                 }
181         }
182
183         void language_changed ()
184         {
185                 switch (_language->GetSelection ()) {
186                 case 0:
187                         Config::instance()->set_language ("de");
188                         break;
189                 case 1:
190                         Config::instance()->set_language ("en");
191                         break;
192                 case 2:
193                         Config::instance()->set_language ("es");
194                         break;
195                 case 3:
196                         Config::instance()->set_language ("fr");
197                         break;
198                 case 4:
199                         Config::instance()->set_language ("it");
200                         break;
201                 case 5:
202                         Config::instance()->set_language ("nl");
203                         break;
204                 case 6:
205                         Config::instance()->set_language ("sv");
206                         break;
207                 }
208         }
209         
210         void check_for_updates_changed ()
211         {
212                 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
213         }
214         
215         void check_for_test_updates_changed ()
216         {
217                 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
218         }
219
220         void num_local_encoding_threads_changed ()
221         {
222                 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
223         }
224
225         wxCheckBox* _set_language;
226         wxChoice* _language;
227         wxSpinCtrl* _num_local_encoding_threads;
228         wxCheckBox* _check_for_updates;
229         wxCheckBox* _check_for_test_updates;
230 };
231
232 class DefaultsPage : public wxPreferencesPage, public Page
233 {
234 public:
235         DefaultsPage (wxSize panel_size, int border)
236                 : Page (panel_size, border)
237         {}
238         
239         wxString GetName () const
240         {
241                 return _("Defaults");
242         }
243
244 #ifdef DCPOMATIC_OSX    
245         wxBitmap GetLargeIcon () const
246         {
247                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
248         }
249 #endif  
250
251         wxWindow* CreateWindow (wxWindow* parent)
252         {
253                 wxPanel* panel = make_panel (parent);
254
255                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
256                 table->AddGrowableCol (1, 1);
257                 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
258                 
259                 {
260                         add_label_to_sizer (table, panel, _("Default duration of still images"), true);
261                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
262                         _still_length = new wxSpinCtrl (panel);
263                         s->Add (_still_length);
264                         add_label_to_sizer (s, panel, _("s"), false);
265                         table->Add (s, 1);
266                 }
267                 
268                 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
269 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
270                 _directory = new DirPickerCtrl (panel);
271 #else   
272                 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
273 #endif
274                 table->Add (_directory, 1, wxEXPAND);
275                 
276                 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
277                 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
278                 table->Add (_isdcf_metadata_button);
279
280                 add_label_to_sizer (table, panel, _("Default scale to"), true);
281                 _scale = new wxChoice (panel, wxID_ANY);
282                 table->Add (_scale);
283                 
284                 add_label_to_sizer (table, panel, _("Default container"), true);
285                 _container = new wxChoice (panel, wxID_ANY);
286                 table->Add (_container);
287                 
288                 add_label_to_sizer (table, panel, _("Default content type"), true);
289                 _dcp_content_type = new wxChoice (panel, wxID_ANY);
290                 table->Add (_dcp_content_type);
291                 
292                 {
293                         add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
294                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
295                         _j2k_bandwidth = new wxSpinCtrl (panel);
296                         s->Add (_j2k_bandwidth);
297                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
298                         table->Add (s, 1);
299                 }
300                 
301                 {
302                         add_label_to_sizer (table, panel, _("Default audio delay"), true);
303                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
304                         _audio_delay = new wxSpinCtrl (panel);
305                         s->Add (_audio_delay);
306                         add_label_to_sizer (s, panel, _("ms"), false);
307                         table->Add (s, 1);
308                 }
309
310                 add_label_to_sizer (table, panel, _("Default issuer"), true);
311                 _issuer = new wxTextCtrl (panel, wxID_ANY);
312                 table->Add (_issuer, 1, wxEXPAND);
313
314                 Config* config = Config::instance ();
315                 
316                 _still_length->SetRange (1, 3600);
317                 _still_length->SetValue (config->default_still_length ());
318                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
319                 
320                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
321                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
322                 
323                 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
324                 
325                 vector<VideoContentScale> scales = VideoContentScale::all ();
326                 for (size_t i = 0; i < scales.size(); ++i) {
327                         _scale->Append (std_to_wx (scales[i].name ()));
328                         if (scales[i] == config->default_scale ()) {
329                                 _scale->SetSelection (i);
330                         }
331                 }
332
333                 vector<Ratio const *> ratios = Ratio::all ();
334                 for (size_t i = 0; i < ratios.size(); ++i) {
335                         _container->Append (std_to_wx (ratios[i]->nickname ()));
336                         if (ratios[i] == config->default_container ()) {
337                                 _container->SetSelection (i);
338                         }
339                 }
340                 
341                 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
342                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
343                 
344                 vector<DCPContentType const *> const ct = DCPContentType::all ();
345                 for (size_t i = 0; i < ct.size(); ++i) {
346                         _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
347                         if (ct[i] == config->default_dcp_content_type ()) {
348                                 _dcp_content_type->SetSelection (i);
349                         }
350                 }
351                 
352                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
353                 
354                 _j2k_bandwidth->SetRange (50, 250);
355                 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
356                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
357                 
358                 _audio_delay->SetRange (-1000, 1000);
359                 _audio_delay->SetValue (config->default_audio_delay ());
360                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
361
362                 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
363                 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
364
365                 return panel;
366         }
367
368 private:
369         void j2k_bandwidth_changed ()
370         {
371                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
372         }
373         
374         void audio_delay_changed ()
375         {
376                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
377         }
378
379         void directory_changed ()
380         {
381                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
382         }
383
384         void edit_isdcf_metadata_clicked (wxWindow* parent)
385         {
386                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
387                 d->ShowModal ();
388                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
389                 d->Destroy ();
390         }
391
392         void still_length_changed ()
393         {
394                 Config::instance()->set_default_still_length (_still_length->GetValue ());
395         }
396
397         void scale_changed ()
398         {
399                 vector<VideoContentScale> scale = VideoContentScale::all ();
400                 Config::instance()->set_default_scale (scale[_scale->GetSelection()]);
401         }
402         
403         void container_changed ()
404         {
405                 vector<Ratio const *> ratio = Ratio::all ();
406                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
407         }
408         
409         void dcp_content_type_changed ()
410         {
411                 vector<DCPContentType const *> ct = DCPContentType::all ();
412                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
413         }
414
415         void issuer_changed ()
416         {
417                 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
418         }
419         
420         wxSpinCtrl* _j2k_bandwidth;
421         wxSpinCtrl* _audio_delay;
422         wxButton* _isdcf_metadata_button;
423         wxSpinCtrl* _still_length;
424 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
425         DirPickerCtrl* _directory;
426 #else
427         wxDirPickerCtrl* _directory;
428 #endif
429         wxChoice* _scale;
430         wxChoice* _container;
431         wxChoice* _dcp_content_type;
432         wxTextCtrl* _issuer;
433 };
434
435 class EncodingServersPage : public wxPreferencesPage, public Page
436 {
437 public:
438         EncodingServersPage (wxSize panel_size, int border)
439                 : Page (panel_size, border)
440         {}
441         
442         wxString GetName () const
443         {
444                 return _("Servers");
445         }
446
447 #ifdef DCPOMATIC_OSX    
448         wxBitmap GetLargeIcon () const
449         {
450                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
451         }
452 #endif  
453
454         wxWindow* CreateWindow (wxWindow* parent)
455         {
456                 wxPanel* panel = make_panel (parent);
457                 
458                 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
459                 panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
460                 
461                 vector<string> columns;
462                 columns.push_back (wx_to_std (_("IP address / host name")));
463                 _servers_list = new EditableList<string, ServerDialog> (
464                         panel,
465                         columns,
466                         boost::bind (&Config::servers, Config::instance()),
467                         boost::bind (&Config::set_servers, Config::instance(), _1),
468                         boost::bind (&EncodingServersPage::server_column, this, _1)
469                         );
470                 
471                 panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
472                 
473                 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
474                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
475
476                 return panel;
477         }
478
479 private:        
480
481         void use_any_servers_changed ()
482         {
483                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
484         }
485
486         string server_column (string s)
487         {
488                 return s;
489         }
490
491         wxCheckBox* _use_any_servers;
492         EditableList<string, ServerDialog>* _servers_list;
493 };
494
495 class ColourConversionsPage : public wxPreferencesPage, public Page
496 {
497 public:
498         ColourConversionsPage (wxSize panel_size, int border)
499                 : Page (panel_size, border)
500         {}
501         
502         wxString GetName () const
503         {
504                 return _("Colour Conversions");
505         }
506
507 #ifdef DCPOMATIC_OSX    
508         wxBitmap GetLargeIcon () const
509         {
510                 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
511         }
512 #endif  
513         wxWindow* CreateWindow (wxWindow* parent)
514         {
515                 wxPanel* panel = make_panel (parent);
516
517                 vector<string> columns;
518                 columns.push_back (wx_to_std (_("Name")));
519                 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
520                         panel,
521                         columns,
522                         boost::bind (&Config::colour_conversions, Config::instance()),
523                         boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
524                         boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
525                         300
526                         );
527
528                 panel->GetSizer()->Add (list, 1, wxEXPAND | wxALL, _border);
529                 return panel;
530         }
531
532 private:
533         string colour_conversion_column (PresetColourConversion c)
534         {
535                 return c.name;
536         }
537 };
538
539 class KeysPage : public wxPreferencesPage, public Page
540 {
541 public:
542         KeysPage (wxSize panel_size, int border)
543                 : Page (panel_size, border)
544         {}
545
546         wxString GetName () const
547         {
548                 return _("Keys");
549         }
550
551 #ifdef DCPOMATIC_OSX
552         wxBitmap GetLargeIcon () const
553         {
554                 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
555         }
556 #endif  
557
558         wxWindow* CreateWindow (wxWindow* parent)
559         {
560                 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
561                 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
562                 _panel->SetSizer (overall_sizer);
563
564                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
565                 overall_sizer->Add (m, 0, wxALL, _border);
566                 
567                 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
568                 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
569                 
570                 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
571
572                 {
573                         wxListItem ip;
574                         ip.SetId (0);
575                         ip.SetText (_("Type"));
576                         ip.SetWidth (100);
577                         _certificates->InsertColumn (0, ip);
578                 }
579
580                 {
581                         wxListItem ip;
582                         ip.SetId (1);
583                         ip.SetText (_("Thumbprint"));
584                         ip.SetWidth (300);
585
586                         wxFont font = ip.GetFont ();
587                         font.SetFamily (wxFONTFAMILY_TELETYPE);
588                         ip.SetFont (font);
589                         
590                         _certificates->InsertColumn (1, ip);
591                 }
592
593                 certificates_sizer->Add (_certificates, 1, wxEXPAND);
594
595                 {
596                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
597                         _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
598                         s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
599                         _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
600                         s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
601                         certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
602                 }
603
604                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
605                 table->AddGrowableCol (1, 1);
606                 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
607
608                 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
609                 table->Add (_remake_certificates, 0);
610                 table->AddSpacer (0);
611
612                 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
613                 {
614                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
615                         _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
616                         wxFont font = _signer_private_key->GetFont ();
617                         font.SetFamily (wxFONTFAMILY_TELETYPE);
618                         _signer_private_key->SetFont (font);
619                         s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
620                         _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
621                         s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
622                         table->Add (s, 0);
623                 }
624
625                 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
626                 {
627                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
628                         _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
629                         wxFont font = _decryption_certificate->GetFont ();
630                         font.SetFamily (wxFONTFAMILY_TELETYPE);
631                         _decryption_certificate->SetFont (font);
632                         s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
633                         _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
634                         s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
635                         table->Add (s, 0);
636                 }
637
638                 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
639                 {
640                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
641                         _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
642                         wxFont font = _decryption_private_key->GetFont ();
643                         font.SetFamily (wxFONTFAMILY_TELETYPE);
644                         _decryption_private_key->SetFont (font);
645                         s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
646                         _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
647                         s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
648                         table->Add (s, 0);
649                 }
650
651                 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
652                 table->Add (_export_decryption_certificate);
653                 table->AddSpacer (0);
654                 
655                 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
656                 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
657                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
658                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
659                 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
660                 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
661                 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
662                 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
663                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
664
665                 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
666
667                 update_certificate_list ();
668                 update_signer_private_key ();
669                 update_decryption_certificate ();
670                 update_decryption_private_key ();
671                 update_sensitivity ();
672
673                 return _panel;
674         }
675
676 private:
677         void add_certificate ()
678         {
679                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
680                 
681                 if (d->ShowModal() == wxID_OK) {
682                         try {
683                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
684                                 _signer->certificates().add (c);
685                                 Config::instance()->set_signer (_signer);
686                                 update_certificate_list ();
687                         } catch (dcp::MiscError& e) {
688                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
689                         }
690                 }
691                 
692                 d->Destroy ();
693
694                 update_sensitivity ();
695         }
696
697         void remove_certificate ()
698         {
699                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
700                 if (i == -1) {
701                         return;
702                 }
703                 
704                 _certificates->DeleteItem (i);
705                 _signer->certificates().remove (i);
706                 Config::instance()->set_signer (_signer);
707
708                 update_sensitivity ();
709         }
710
711         void update_certificate_list ()
712         {
713                 _certificates->DeleteAllItems ();
714                 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
715                 size_t n = 0;
716                 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
717                         wxListItem item;
718                         item.SetId (n);
719                         _certificates->InsertItem (item);
720                         _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
721
722                         if (n == 0) {
723                                 _certificates->SetItem (n, 0, _("Root"));
724                         } else if (n == (certs.size() - 1)) {
725                                 _certificates->SetItem (n, 0, _("Leaf"));
726                         } else {
727                                 _certificates->SetItem (n, 0, _("Intermediate"));
728                         }
729
730                         ++n;
731                 }
732         }
733
734         void remake_certificates ()
735         {
736                 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
737                 if (d->ShowModal () == wxID_OK) {
738                         _signer.reset (
739                                 new dcp::Signer (
740                                         openssl_path (),
741                                         d->organisation (),
742                                         d->organisational_unit (),
743                                         d->root_common_name (),
744                                         d->intermediate_common_name (),
745                                         d->leaf_common_name ()
746                                         )
747                                 );
748
749                         Config::instance()->set_signer (_signer);
750                         update_certificate_list ();
751                         update_signer_private_key ();
752                 }
753                 
754                 d->Destroy ();
755         }
756
757         void update_sensitivity ()
758         {
759                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
760         }
761
762         void update_signer_private_key ()
763         {
764                 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
765         }       
766
767         void load_signer_private_key ()
768         {
769                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
770
771                 if (d->ShowModal() == wxID_OK) {
772                         try {
773                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
774                                 if (boost::filesystem::file_size (p) > 1024) {
775                                         error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
776                                         return;
777                                 }
778                                 
779                                 _signer->set_key (dcp::file_to_string (p));
780                                 Config::instance()->set_signer (_signer);
781                                 update_signer_private_key ();
782                         } catch (dcp::MiscError& e) {
783                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
784                         }
785                 }
786                 
787                 d->Destroy ();
788
789                 update_sensitivity ();
790
791         }
792
793         void load_decryption_certificate ()
794         {
795                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
796                 
797                 if (d->ShowModal() == wxID_OK) {
798                         try {
799                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
800                                 Config::instance()->set_decryption_certificate (c);
801                                 update_decryption_certificate ();
802                         } catch (dcp::MiscError& e) {
803                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
804                         }
805                 }
806                 
807                 d->Destroy ();
808         }
809
810         void update_decryption_certificate ()
811         {
812                 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
813         }
814
815         void load_decryption_private_key ()
816         {
817                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
818
819                 if (d->ShowModal() == wxID_OK) {
820                         try {
821                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
822                                 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
823                                 update_decryption_private_key ();
824                         } catch (dcp::MiscError& e) {
825                                 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
826                         }
827                 }
828                 
829                 d->Destroy ();
830         }
831
832         void update_decryption_private_key ()
833         {
834                 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
835         }
836
837         void export_decryption_certificate ()
838         {
839                 wxFileDialog* d = new wxFileDialog (
840                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
841                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
842                         );
843                 
844                 if (d->ShowModal () == wxID_OK) {
845                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
846                         if (!f) {
847                                 throw OpenFileError (wx_to_std (d->GetPath ()));
848                         }
849
850                         string const s = Config::instance()->decryption_certificate().certificate (true);
851                         fwrite (s.c_str(), 1, s.length(), f);
852                         fclose (f);
853                 }
854                 d->Destroy ();
855         }
856
857         wxPanel* _panel;
858         wxListCtrl* _certificates;
859         wxButton* _add_certificate;
860         wxButton* _remove_certificate;
861         wxButton* _remake_certificates;
862         wxStaticText* _signer_private_key;
863         wxButton* _load_signer_private_key;
864         wxStaticText* _decryption_certificate;
865         wxButton* _load_decryption_certificate;
866         wxStaticText* _decryption_private_key;
867         wxButton* _load_decryption_private_key;
868         wxButton* _export_decryption_certificate;
869         shared_ptr<dcp::Signer> _signer;
870 };
871
872 class TMSPage : public wxPreferencesPage, public Page
873 {
874 public:
875         TMSPage (wxSize panel_size, int border)
876                 : Page (panel_size, border)
877         {}
878
879         wxString GetName () const
880         {
881                 return _("TMS");
882         }
883
884 #ifdef DCPOMATIC_OSX    
885         wxBitmap GetLargeIcon () const
886         {
887                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
888         }
889 #endif  
890
891         wxWindow* CreateWindow (wxWindow* parent)
892         {
893                 wxPanel* panel = make_panel (parent);
894
895                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
896                 table->AddGrowableCol (1, 1);
897                 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
898                 
899                 add_label_to_sizer (table, panel, _("IP address"), true);
900                 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
901                 table->Add (_tms_ip, 1, wxEXPAND);
902                 
903                 add_label_to_sizer (table, panel, _("Target path"), true);
904                 _tms_path = new wxTextCtrl (panel, wxID_ANY);
905                 table->Add (_tms_path, 1, wxEXPAND);
906                 
907                 add_label_to_sizer (table, panel, _("User name"), true);
908                 _tms_user = new wxTextCtrl (panel, wxID_ANY);
909                 table->Add (_tms_user, 1, wxEXPAND);
910                 
911                 add_label_to_sizer (table, panel, _("Password"), true);
912                 _tms_password = new wxTextCtrl (panel, wxID_ANY);
913                 table->Add (_tms_password, 1, wxEXPAND);
914                 
915                 Config* config = Config::instance ();
916                 
917                 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
918                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
919                 _tms_path->SetValue (std_to_wx (config->tms_path ()));
920                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
921                 _tms_user->SetValue (std_to_wx (config->tms_user ()));
922                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
923                 _tms_password->SetValue (std_to_wx (config->tms_password ()));
924                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
925
926                 return panel;
927         }
928
929 private:
930         void tms_ip_changed ()
931         {
932                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
933         }
934         
935         void tms_path_changed ()
936         {
937                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
938         }
939         
940         void tms_user_changed ()
941         {
942                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
943         }
944         
945         void tms_password_changed ()
946         {
947                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
948         }
949
950         wxTextCtrl* _tms_ip;
951         wxTextCtrl* _tms_path;
952         wxTextCtrl* _tms_user;
953         wxTextCtrl* _tms_password;
954 };
955
956 class KDMEmailPage : public wxPreferencesPage, public Page
957 {
958 public:
959
960         KDMEmailPage (wxSize panel_size, int border)
961                 : Page (panel_size, border)
962         {}
963         
964         wxString GetName () const
965         {
966                 return _("KDM Email");
967         }
968
969 #ifdef DCPOMATIC_OSX    
970         wxBitmap GetLargeIcon () const
971         {
972                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
973         }
974 #endif  
975
976         wxWindow* CreateWindow (wxWindow* parent)
977         {
978 #ifdef DCPOMATIC_OSX            
979                 /* We have to force both width and height of this one */
980                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
981 #else
982                 wxPanel* panel = new wxPanel (parent);
983 #endif
984                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
985                 panel->SetSizer (s);
986
987                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
988                 table->AddGrowableCol (1, 1);
989                 panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
990
991                 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
992                 _mail_server = new wxTextCtrl (panel, wxID_ANY);
993                 table->Add (_mail_server, 1, wxEXPAND | wxALL);
994                 
995                 add_label_to_sizer (table, panel, _("Mail user name"), true);
996                 _mail_user = new wxTextCtrl (panel, wxID_ANY);
997                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
998                 
999                 add_label_to_sizer (table, panel, _("Mail password"), true);
1000                 _mail_password = new wxTextCtrl (panel, wxID_ANY);
1001                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1002                 
1003                 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
1004                 wxFont font = plain->GetFont();
1005                 font.SetStyle (wxFONTSTYLE_ITALIC);
1006                 font.SetPointSize (font.GetPointSize() - 1);
1007                 plain->SetFont (font);
1008                 table->AddSpacer (0);
1009
1010                 add_label_to_sizer (table, panel, _("Subject"), true);
1011                 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
1012                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1013                 
1014                 add_label_to_sizer (table, panel, _("From address"), true);
1015                 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
1016                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1017
1018                 add_label_to_sizer (table, panel, _("CC address"), true);
1019                 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
1020                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1021
1022                 add_label_to_sizer (table, panel, _("BCC address"), true);
1023                 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
1024                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1025                 
1026                 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1027                 panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1028
1029                 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1030                 panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1031
1032                 Config* config = Config::instance ();
1033                 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1034                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1035                 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1036                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1037                 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1038                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1039                 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1040                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1041                 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1042                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1043                 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1044                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1045                 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1046                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1047                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1048                 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1049                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1050
1051                 return panel;
1052         }
1053
1054 private:
1055         void mail_server_changed ()
1056         {
1057                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1058         }
1059         
1060         void mail_user_changed ()
1061         {
1062                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1063         }
1064         
1065         void mail_password_changed ()
1066         {
1067                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1068         }
1069
1070         void kdm_subject_changed ()
1071         {
1072                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1073         }
1074         
1075         void kdm_from_changed ()
1076         {
1077                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1078         }
1079
1080         void kdm_cc_changed ()
1081         {
1082                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1083         }
1084
1085         void kdm_bcc_changed ()
1086         {
1087                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1088         }
1089         
1090         void kdm_email_changed ()
1091         {
1092                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1093         }
1094
1095         void reset_kdm_email ()
1096         {
1097                 Config::instance()->reset_kdm_email ();
1098                 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1099         }
1100
1101         wxTextCtrl* _mail_server;
1102         wxTextCtrl* _mail_user;
1103         wxTextCtrl* _mail_password;
1104         wxTextCtrl* _kdm_subject;
1105         wxTextCtrl* _kdm_from;
1106         wxTextCtrl* _kdm_cc;
1107         wxTextCtrl* _kdm_bcc;
1108         wxTextCtrl* _kdm_email;
1109         wxButton* _reset_kdm_email;
1110 };
1111
1112 /** @class AdvancedPage
1113  *  @brief Advanced page of the preferences dialog.
1114  */
1115 class AdvancedPage : public wxStockPreferencesPage, public Page
1116 {
1117 public:
1118
1119         AdvancedPage (wxSize panel_size, int border)
1120                 : wxStockPreferencesPage (Kind_Advanced)
1121                 , Page (panel_size, border)
1122         {}
1123         
1124         wxWindow* CreateWindow (wxWindow* parent)
1125         {
1126                 wxPanel* panel = make_panel (parent);
1127
1128                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1129                 table->AddGrowableCol (1, 1);
1130                 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1131
1132                 {
1133                         add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1134                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1135                         _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1136                         s->Add (_maximum_j2k_bandwidth, 1);
1137                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
1138                         table->Add (s, 1);
1139                 }
1140
1141                 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1142                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1143                 table->AddSpacer (0);
1144
1145 #ifdef __WXOSX__
1146                 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log:"));
1147                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1148 #else           
1149                 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1150                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1151 #endif          
1152                 
1153                 {
1154                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1155                         _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1156                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1157                         _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1158                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1159                         _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1160                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1161                         _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1162                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1163                         table->Add (t, 0, wxALL, 6);
1164                 }
1165
1166 #ifdef DCPOMATIC_WINDOWS                
1167                 _win32_console = new wxCheckBox (panel, wxID_ANY, _("Open console window"));
1168                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1169                 table->AddSpacer (0);
1170 #endif          
1171                 
1172                 Config* config = Config::instance ();
1173                 
1174                 _maximum_j2k_bandwidth->SetRange (1, 500);
1175                 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1176                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1177                 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1178                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1179                 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1180                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1181                 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1182                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1183                 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1184                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1185                 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1186                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1187 #ifdef DCPOMATIC_WINDOWS
1188                 _win32_console->SetValue (config->win32_console());
1189                 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1190 #endif          
1191                 
1192                 return panel;
1193         }
1194
1195 private:
1196
1197         void maximum_j2k_bandwidth_changed ()
1198         {
1199                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1200         }
1201
1202         void allow_any_dcp_frame_rate_changed ()
1203         {
1204                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1205         }
1206
1207         void log_changed ()
1208         {
1209                 int types = 0;
1210                 if (_log_general->GetValue ()) {
1211                         types |= Log::TYPE_GENERAL;
1212                 }
1213                 if (_log_warning->GetValue ()) {
1214                         types |= Log::TYPE_WARNING;
1215                 }
1216                 if (_log_error->GetValue ())  {
1217                         types |= Log::TYPE_ERROR;
1218                 }
1219                 if (_log_timing->GetValue ()) {
1220                         types |= Log::TYPE_TIMING;
1221                 }
1222                 Config::instance()->set_log_types (types);
1223         }
1224
1225 #ifdef DCPOMATIC_WINDOWS        
1226         void win32_console_changed ()
1227         {
1228                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1229         }
1230 #endif  
1231         
1232         wxSpinCtrl* _maximum_j2k_bandwidth;
1233         wxCheckBox* _allow_any_dcp_frame_rate;
1234         wxCheckBox* _log_general;
1235         wxCheckBox* _log_warning;
1236         wxCheckBox* _log_error;
1237         wxCheckBox* _log_timing;
1238 #ifdef DCPOMATIC_WINDOWS        
1239         wxCheckBox* _win32_console;
1240 #endif  
1241 };
1242         
1243 wxPreferencesEditor*
1244 create_config_dialog ()
1245 {
1246         wxPreferencesEditor* e = new wxPreferencesEditor ();
1247
1248 #ifdef DCPOMATIC_OSX
1249         /* Width that we force some of the config panels to be on OSX so that
1250            the containing window doesn't shrink too much when we select those panels.
1251            This is obviously an unpleasant hack.
1252         */
1253         wxSize ps = wxSize (520, -1);
1254         int const border = 16;
1255 #else
1256         wxSize ps = wxSize (-1, -1);
1257         int const border = 8;
1258 #endif
1259         
1260         e->AddPage (new GeneralPage (ps, border));
1261         e->AddPage (new DefaultsPage (ps, border));
1262         e->AddPage (new EncodingServersPage (ps, border));
1263         e->AddPage (new ColourConversionsPage (ps, border));
1264         e->AddPage (new KeysPage (ps, border));
1265         e->AddPage (new TMSPage (ps, border));
1266         e->AddPage (new KDMEmailPage (ps, border));
1267         e->AddPage (new AdvancedPage (ps, border));
1268         return e;
1269 }