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