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