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