d7d2ad34966741c7e4a33ad2420e4f1ac5114404
[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                 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1011                 s->Add (_kdm_email, 1.5, wxEXPAND | wxALL, _border);
1012
1013                 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1014                 s->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1015
1016                 Config* config = Config::instance ();
1017                 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1018                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1019                 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1020                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1021                 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1022                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1023                 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1024                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1025                 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1026                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1027                 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1028                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1029                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1030                 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1031                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1032
1033                 return panel;
1034         }
1035
1036 private:
1037         void mail_server_changed ()
1038         {
1039                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1040         }
1041         
1042         void mail_user_changed ()
1043         {
1044                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1045         }
1046         
1047         void mail_password_changed ()
1048         {
1049                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1050         }
1051
1052         void kdm_subject_changed ()
1053         {
1054                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1055         }
1056         
1057         void kdm_from_changed ()
1058         {
1059                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1060         }
1061
1062         void kdm_cc_changed ()
1063         {
1064                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1065         }
1066         
1067         void kdm_email_changed ()
1068         {
1069                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1070         }
1071
1072         void reset_kdm_email ()
1073         {
1074                 Config::instance()->reset_kdm_email ();
1075                 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1076         }
1077
1078         wxTextCtrl* _mail_server;
1079         wxTextCtrl* _mail_user;
1080         wxTextCtrl* _mail_password;
1081         wxTextCtrl* _kdm_subject;
1082         wxTextCtrl* _kdm_from;
1083         wxTextCtrl* _kdm_cc;
1084         wxTextCtrl* _kdm_email;
1085         wxButton* _reset_kdm_email;
1086 };
1087
1088 class AdvancedPage : public wxStockPreferencesPage, public Page
1089 {
1090 public:
1091
1092         AdvancedPage (wxSize panel_size, int border)
1093                 : wxStockPreferencesPage (Kind_Advanced)
1094                 , Page (panel_size, border)
1095         {}
1096         
1097         wxWindow* CreateWindow (wxWindow* parent)
1098         {
1099                 wxPanel* panel = new wxPanel (parent);
1100
1101                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
1102                 panel->SetSizer (s);
1103
1104                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1105                 table->AddGrowableCol (1, 1);
1106                 s->Add (table, 1, wxALL | wxEXPAND, _border);
1107
1108                 {
1109                         add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1110                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1111                         _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1112                         s->Add (_maximum_j2k_bandwidth, 1);
1113                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
1114                         table->Add (s, 1);
1115                 }
1116
1117                 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1118                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1119                 table->AddSpacer (0);
1120
1121                 add_label_to_sizer (table, panel, _("Log"), true);
1122                 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1123                 table->Add (_log_general, 1, wxEXPAND | wxALL);
1124                 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1125                 table->AddSpacer (0);
1126                 table->Add (_log_warning, 1, wxEXPAND | wxALL);
1127                 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1128                 table->AddSpacer (0);
1129                 table->Add (_log_error, 1, wxEXPAND | wxALL);
1130                 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1131                 table->AddSpacer (0);
1132                 table->Add (_log_timing, 1, wxEXPAND | wxALL);
1133
1134                 Config* config = Config::instance ();
1135                 
1136                 _maximum_j2k_bandwidth->SetRange (1, 500);
1137                 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1138                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1139                 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1140                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1141                 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1142                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1143                 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1144                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1145                 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1146                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1147                 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1148                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1149                 
1150                 return panel;
1151         }
1152
1153 private:
1154
1155         void maximum_j2k_bandwidth_changed ()
1156         {
1157                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1158         }
1159
1160         void allow_any_dcp_frame_rate_changed ()
1161         {
1162                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1163         }
1164
1165         void log_changed ()
1166         {
1167                 int types = 0;
1168                 if (_log_general->GetValue ()) {
1169                         types |= Log::TYPE_GENERAL;
1170                 }
1171                 if (_log_warning->GetValue ()) {
1172                         types |= Log::TYPE_WARNING;
1173                 }
1174                 if (_log_error->GetValue ())  {
1175                         types |= Log::TYPE_ERROR;
1176                 }
1177                 if (_log_timing->GetValue ()) {
1178                         types |= Log::TYPE_TIMING;
1179                 }
1180                 Config::instance()->set_log_types (types);
1181         }
1182         
1183         wxSpinCtrl* _maximum_j2k_bandwidth;
1184         wxCheckBox* _allow_any_dcp_frame_rate;
1185         wxCheckBox* _log_general;
1186         wxCheckBox* _log_warning;
1187         wxCheckBox* _log_error;
1188         wxCheckBox* _log_timing;
1189 };
1190         
1191 wxPreferencesEditor*
1192 create_config_dialog ()
1193 {
1194         wxPreferencesEditor* e = new wxPreferencesEditor ();
1195
1196 #ifdef DCPOMATIC_OSX
1197         /* Width that we force some of the config panels to be on OSX so that
1198            the containing window doesn't shrink too much when we select those panels.
1199            This is obviously an unpleasant hack.
1200         */
1201         wxSize ps = wxSize (480, -1);
1202         int const border = 16;
1203 #else
1204         wxSize ps = wxSize (-1, -1);
1205         int const border = 8;
1206 #endif
1207         
1208         e->AddPage (new GeneralPage (ps, border));
1209         e->AddPage (new DefaultsPage (ps, border));
1210         e->AddPage (new EncodingServersPage (ps, border));
1211         e->AddPage (new ColourConversionsPage (ps, border));
1212         e->AddPage (new KeysPage (ps, border));
1213         e->AddPage (new TMSPage (ps, border));
1214         e->AddPage (new KDMEmailPage (ps, border));
1215         e->AddPage (new AdvancedPage (ps, border));
1216         return e;
1217 }