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                 Config* config = Config::instance ();
310                 
311                 _still_length->SetRange (1, 3600);
312                 _still_length->SetValue (config->default_still_length ());
313                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
314                 
315                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
316                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
317                 
318                 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
319                 
320                 vector<VideoContentScale> scales = VideoContentScale::all ();
321                 for (size_t i = 0; i < scales.size(); ++i) {
322                         _scale->Append (std_to_wx (scales[i].name ()));
323                         if (scales[i] == config->default_scale ()) {
324                                 _scale->SetSelection (i);
325                         }
326                 }
327
328                 vector<Ratio const *> ratios = Ratio::all ();
329                 for (size_t i = 0; i < ratios.size(); ++i) {
330                         _container->Append (std_to_wx (ratios[i]->nickname ()));
331                         if (ratios[i] == config->default_container ()) {
332                                 _container->SetSelection (i);
333                         }
334                 }
335                 
336                 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
337                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
338                 
339                 vector<DCPContentType const *> const ct = DCPContentType::all ();
340                 for (size_t i = 0; i < ct.size(); ++i) {
341                         _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
342                         if (ct[i] == config->default_dcp_content_type ()) {
343                                 _dcp_content_type->SetSelection (i);
344                         }
345                 }
346                 
347                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
348                 
349                 _j2k_bandwidth->SetRange (50, 250);
350                 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
351                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
352                 
353                 _audio_delay->SetRange (-1000, 1000);
354                 _audio_delay->SetValue (config->default_audio_delay ());
355                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
356
357                 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
358                 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
359
360                 return panel;
361         }
362
363 private:
364         void j2k_bandwidth_changed ()
365         {
366                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
367         }
368         
369         void audio_delay_changed ()
370         {
371                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
372         }
373
374         void directory_changed ()
375         {
376                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
377         }
378
379         void edit_isdcf_metadata_clicked (wxWindow* parent)
380         {
381                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
382                 d->ShowModal ();
383                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
384                 d->Destroy ();
385         }
386
387         void still_length_changed ()
388         {
389                 Config::instance()->set_default_still_length (_still_length->GetValue ());
390         }
391
392         void scale_changed ()
393         {
394                 vector<VideoContentScale> scale = VideoContentScale::all ();
395                 Config::instance()->set_default_scale (scale[_scale->GetSelection()]);
396         }
397         
398         void container_changed ()
399         {
400                 vector<Ratio const *> ratio = Ratio::all ();
401                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
402         }
403         
404         void dcp_content_type_changed ()
405         {
406                 vector<DCPContentType const *> ct = DCPContentType::all ();
407                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
408         }
409
410         void issuer_changed ()
411         {
412                 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
413         }
414         
415         wxSpinCtrl* _j2k_bandwidth;
416         wxSpinCtrl* _audio_delay;
417         wxButton* _isdcf_metadata_button;
418         wxSpinCtrl* _still_length;
419 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
420         DirPickerCtrl* _directory;
421 #else
422         wxDirPickerCtrl* _directory;
423 #endif
424         wxChoice* _scale;
425         wxChoice* _container;
426         wxChoice* _dcp_content_type;
427         wxTextCtrl* _issuer;
428 };
429
430 class EncodingServersPage : public wxPreferencesPage, public Page
431 {
432 public:
433         EncodingServersPage (wxSize panel_size, int border)
434                 : Page (panel_size, border)
435         {}
436         
437         wxString GetName () const
438         {
439                 return _("Servers");
440         }
441
442 #ifdef DCPOMATIC_OSX    
443         wxBitmap GetLargeIcon () const
444         {
445                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
446         }
447 #endif  
448
449         wxWindow* CreateWindow (wxWindow* parent)
450         {
451                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
452                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
453                 panel->SetSizer (s);
454                 
455                 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
456                 s->Add (_use_any_servers, 0, wxALL, _border);
457                 
458                 vector<string> columns;
459                 columns.push_back (wx_to_std (_("IP address / host name")));
460                 _servers_list = new EditableList<string, ServerDialog> (
461                         panel,
462                         columns,
463                         boost::bind (&Config::servers, Config::instance()),
464                         boost::bind (&Config::set_servers, Config::instance(), _1),
465                         boost::bind (&EncodingServersPage::server_column, this, _1)
466                         );
467                 
468                 s->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
469                 
470                 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
471                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
472
473                 return panel;
474         }
475
476 private:        
477
478         void use_any_servers_changed ()
479         {
480                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
481         }
482
483         string server_column (string s)
484         {
485                 return s;
486         }
487
488         wxCheckBox* _use_any_servers;
489         EditableList<string, ServerDialog>* _servers_list;
490 };
491
492 class ColourConversionsPage : public wxPreferencesPage, public Page
493 {
494 public:
495         ColourConversionsPage (wxSize panel_size, int border)
496                 : Page (panel_size, border)
497         {}
498         
499         wxString GetName () const
500         {
501                 return _("Colour Conversions");
502         }
503
504 #ifdef DCPOMATIC_OSX    
505         wxBitmap GetLargeIcon () const
506         {
507                 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
508         }
509 #endif  
510         wxWindow* CreateWindow (wxWindow* parent)
511         {
512                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
513                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
514                 panel->SetSizer (s);
515
516                 vector<string> columns;
517                 columns.push_back (wx_to_std (_("Name")));
518                 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
519                         panel,
520                         columns,
521                         boost::bind (&Config::colour_conversions, Config::instance()),
522                         boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
523                         boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
524                         300
525                         );
526
527                 s->Add (list, 1, wxEXPAND | wxALL, _border);
528                 return panel;
529         }
530
531 private:
532         string colour_conversion_column (PresetColourConversion c)
533         {
534                 return c.name;
535         }
536 };
537
538 class KeysPage : public wxPreferencesPage, public Page
539 {
540 public:
541         KeysPage (wxSize panel_size, int border)
542                 : Page (panel_size, border)
543         {}
544
545         wxString GetName () const
546         {
547                 return _("Keys");
548         }
549
550 #ifdef DCPOMATIC_OSX
551         wxBitmap GetLargeIcon () const
552         {
553                 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
554         }
555 #endif  
556
557         wxWindow* CreateWindow (wxWindow* parent)
558         {
559                 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
560                 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
561                 _panel->SetSizer (overall_sizer);
562
563                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
564                 overall_sizer->Add (m, 0, wxALL, _border);
565                 
566                 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
567                 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
568                 
569                 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
570
571                 {
572                         wxListItem ip;
573                         ip.SetId (0);
574                         ip.SetText (_("Type"));
575                         ip.SetWidth (100);
576                         _certificates->InsertColumn (0, ip);
577                 }
578
579                 {
580                         wxListItem ip;
581                         ip.SetId (1);
582                         ip.SetText (_("Thumbprint"));
583                         ip.SetWidth (300);
584
585                         wxFont font = ip.GetFont ();
586                         font.SetFamily (wxFONTFAMILY_TELETYPE);
587                         ip.SetFont (font);
588                         
589                         _certificates->InsertColumn (1, ip);
590                 }
591
592                 certificates_sizer->Add (_certificates, 1, wxEXPAND);
593
594                 {
595                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
596                         _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
597                         s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
598                         _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
599                         s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
600                         certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
601                 }
602
603                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
604                 table->AddGrowableCol (1, 1);
605                 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
606
607                 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
608                 {
609                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
610                         _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
611                         wxFont font = _signer_private_key->GetFont ();
612                         font.SetFamily (wxFONTFAMILY_TELETYPE);
613                         _signer_private_key->SetFont (font);
614                         s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
615                         _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
616                         s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
617                         table->Add (s, 0);
618                 }
619
620                 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
621                 {
622                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
623                         _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
624                         wxFont font = _decryption_certificate->GetFont ();
625                         font.SetFamily (wxFONTFAMILY_TELETYPE);
626                         _decryption_certificate->SetFont (font);
627                         s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
628                         _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
629                         s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
630                         table->Add (s, 0);
631                 }
632
633                 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
634                 {
635                         wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
636                         _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
637                         wxFont font = _decryption_private_key->GetFont ();
638                         font.SetFamily (wxFONTFAMILY_TELETYPE);
639                         _decryption_private_key->SetFont (font);
640                         s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
641                         _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
642                         s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
643                         table->Add (s, 0);
644                 }
645
646                 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
647                 table->Add (_export_decryption_certificate);
648                 table->AddSpacer (0);
649                 
650                 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
651                 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
652                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
653                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
654                 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
655                 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
656                 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
657                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
658
659                 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
660
661                 update_certificate_list ();
662                 update_signer_private_key ();
663                 update_decryption_certificate ();
664                 update_decryption_private_key ();
665                 update_sensitivity ();
666
667                 return _panel;
668         }
669
670 private:
671         void add_certificate ()
672         {
673                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
674                 
675                 if (d->ShowModal() == wxID_OK) {
676                         try {
677                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
678                                 _signer->certificates().add (c);
679                                 Config::instance()->set_signer (_signer);
680                                 update_certificate_list ();
681                         } catch (dcp::MiscError& e) {
682                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
683                         }
684                 }
685                 
686                 d->Destroy ();
687
688                 update_sensitivity ();
689         }
690
691         void remove_certificate ()
692         {
693                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
694                 if (i == -1) {
695                         return;
696                 }
697                 
698                 _certificates->DeleteItem (i);
699                 _signer->certificates().remove (i);
700                 Config::instance()->set_signer (_signer);
701
702                 update_sensitivity ();
703         }
704
705         void update_certificate_list ()
706         {
707                 _certificates->DeleteAllItems ();
708                 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
709                 size_t n = 0;
710                 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
711                         wxListItem item;
712                         item.SetId (n);
713                         _certificates->InsertItem (item);
714                         _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
715
716                         if (n == 0) {
717                                 _certificates->SetItem (n, 0, _("Root"));
718                         } else if (n == (certs.size() - 1)) {
719                                 _certificates->SetItem (n, 0, _("Leaf"));
720                         } else {
721                                 _certificates->SetItem (n, 0, _("Intermediate"));
722                         }
723
724                         ++n;
725                 }
726         }
727
728         void update_sensitivity ()
729         {
730                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
731         }
732
733         void update_signer_private_key ()
734         {
735                 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
736         }       
737
738         void load_signer_private_key ()
739         {
740                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
741
742                 if (d->ShowModal() == wxID_OK) {
743                         try {
744                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
745                                 if (boost::filesystem::file_size (p) > 1024) {
746                                         error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
747                                         return;
748                                 }
749                                 
750                                 _signer->set_key (dcp::file_to_string (p));
751                                 Config::instance()->set_signer (_signer);
752                                 update_signer_private_key ();
753                         } catch (dcp::MiscError& e) {
754                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
755                         }
756                 }
757                 
758                 d->Destroy ();
759
760                 update_sensitivity ();
761
762         }
763
764         void load_decryption_certificate ()
765         {
766                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
767                 
768                 if (d->ShowModal() == wxID_OK) {
769                         try {
770                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
771                                 Config::instance()->set_decryption_certificate (c);
772                                 update_decryption_certificate ();
773                         } catch (dcp::MiscError& e) {
774                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
775                         }
776                 }
777                 
778                 d->Destroy ();
779         }
780
781         void update_decryption_certificate ()
782         {
783                 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
784         }
785
786         void load_decryption_private_key ()
787         {
788                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
789
790                 if (d->ShowModal() == wxID_OK) {
791                         try {
792                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
793                                 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
794                                 update_decryption_private_key ();
795                         } catch (dcp::MiscError& e) {
796                                 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
797                         }
798                 }
799                 
800                 d->Destroy ();
801         }
802
803         void update_decryption_private_key ()
804         {
805                 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
806         }
807
808         void export_decryption_certificate ()
809         {
810                 wxFileDialog* d = new wxFileDialog (
811                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
812                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
813                         );
814                 
815                 if (d->ShowModal () == wxID_OK) {
816                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
817                         if (!f) {
818                                 throw OpenFileError (wx_to_std (d->GetPath ()));
819                         }
820
821                         string const s = Config::instance()->decryption_certificate().certificate (true);
822                         fwrite (s.c_str(), 1, s.length(), f);
823                         fclose (f);
824                 }
825                 d->Destroy ();
826         }
827
828         wxPanel* _panel;
829         wxListCtrl* _certificates;
830         wxButton* _add_certificate;
831         wxButton* _remove_certificate;
832         wxStaticText* _signer_private_key;
833         wxButton* _load_signer_private_key;
834         wxStaticText* _decryption_certificate;
835         wxButton* _load_decryption_certificate;
836         wxStaticText* _decryption_private_key;
837         wxButton* _load_decryption_private_key;
838         wxButton* _export_decryption_certificate;
839         shared_ptr<dcp::Signer> _signer;
840 };
841
842 class TMSPage : public wxPreferencesPage, public Page
843 {
844 public:
845         TMSPage (wxSize panel_size, int border)
846                 : Page (panel_size, border)
847         {}
848
849         wxString GetName () const
850         {
851                 return _("TMS");
852         }
853
854 #ifdef DCPOMATIC_OSX    
855         wxBitmap GetLargeIcon () const
856         {
857                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
858         }
859 #endif  
860
861         wxWindow* CreateWindow (wxWindow* parent)
862         {
863                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
864                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
865                 panel->SetSizer (s);
866
867                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
868                 table->AddGrowableCol (1, 1);
869                 s->Add (table, 1, wxALL | wxEXPAND, _border);
870                 
871                 add_label_to_sizer (table, panel, _("IP address"), true);
872                 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
873                 table->Add (_tms_ip, 1, wxEXPAND);
874                 
875                 add_label_to_sizer (table, panel, _("Target path"), true);
876                 _tms_path = new wxTextCtrl (panel, wxID_ANY);
877                 table->Add (_tms_path, 1, wxEXPAND);
878                 
879                 add_label_to_sizer (table, panel, _("User name"), true);
880                 _tms_user = new wxTextCtrl (panel, wxID_ANY);
881                 table->Add (_tms_user, 1, wxEXPAND);
882                 
883                 add_label_to_sizer (table, panel, _("Password"), true);
884                 _tms_password = new wxTextCtrl (panel, wxID_ANY);
885                 table->Add (_tms_password, 1, wxEXPAND);
886                 
887                 Config* config = Config::instance ();
888                 
889                 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
890                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
891                 _tms_path->SetValue (std_to_wx (config->tms_path ()));
892                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
893                 _tms_user->SetValue (std_to_wx (config->tms_user ()));
894                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
895                 _tms_password->SetValue (std_to_wx (config->tms_password ()));
896                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
897
898                 return panel;
899         }
900
901 private:
902         void tms_ip_changed ()
903         {
904                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
905         }
906         
907         void tms_path_changed ()
908         {
909                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
910         }
911         
912         void tms_user_changed ()
913         {
914                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
915         }
916         
917         void tms_password_changed ()
918         {
919                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
920         }
921
922         wxTextCtrl* _tms_ip;
923         wxTextCtrl* _tms_path;
924         wxTextCtrl* _tms_user;
925         wxTextCtrl* _tms_password;
926 };
927
928 class KDMEmailPage : public wxPreferencesPage, public Page
929 {
930 public:
931
932         KDMEmailPage (wxSize panel_size, int border)
933                 : Page (panel_size, border)
934         {}
935         
936         wxString GetName () const
937         {
938                 return _("KDM Email");
939         }
940
941 #ifdef DCPOMATIC_OSX    
942         wxBitmap GetLargeIcon () const
943         {
944                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
945         }
946 #endif  
947
948         wxWindow* CreateWindow (wxWindow* parent)
949         {
950                 /* We have to force both width and height of this one */
951 #ifdef DCPOMATIC_OSX
952                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
953 #else           
954                 wxPanel* panel = new wxPanel (parent);
955 #endif          
956                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
957                 panel->SetSizer (s);
958
959                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
960                 table->AddGrowableCol (1, 1);
961                 s->Add (table, 1, wxEXPAND | wxALL, _border);
962
963                 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
964                 _mail_server = new wxTextCtrl (panel, wxID_ANY);
965                 table->Add (_mail_server, 1, wxEXPAND | wxALL);
966                 
967                 add_label_to_sizer (table, panel, _("Mail user name"), true);
968                 _mail_user = new wxTextCtrl (panel, wxID_ANY);
969                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
970                 
971                 add_label_to_sizer (table, panel, _("Mail password"), true);
972                 _mail_password = new wxTextCtrl (panel, wxID_ANY);
973                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
974                 
975                 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
976                 wxFont font = plain->GetFont();
977                 font.SetStyle (wxFONTSTYLE_ITALIC);
978                 font.SetPointSize (font.GetPointSize() - 1);
979                 plain->SetFont (font);
980                 table->AddSpacer (0);
981
982                 add_label_to_sizer (table, panel, _("Subject"), true);
983                 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
984                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
985                 
986                 add_label_to_sizer (table, panel, _("From address"), true);
987                 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
988                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
989
990                 add_label_to_sizer (table, panel, _("CC address"), true);
991                 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
992                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
993
994                 add_label_to_sizer (table, panel, _("BCC address"), true);
995                 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
996                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
997                 
998                 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
999                 s->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1000
1001                 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1002                 s->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1003
1004                 Config* config = Config::instance ();
1005                 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1006                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1007                 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1008                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1009                 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1010                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1011                 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1012                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1013                 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1014                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1015                 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1016                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1017                 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1018                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1019                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1020                 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1021                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1022
1023                 return panel;
1024         }
1025
1026 private:
1027         void mail_server_changed ()
1028         {
1029                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1030         }
1031         
1032         void mail_user_changed ()
1033         {
1034                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1035         }
1036         
1037         void mail_password_changed ()
1038         {
1039                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1040         }
1041
1042         void kdm_subject_changed ()
1043         {
1044                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1045         }
1046         
1047         void kdm_from_changed ()
1048         {
1049                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1050         }
1051
1052         void kdm_cc_changed ()
1053         {
1054                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1055         }
1056
1057         void kdm_bcc_changed ()
1058         {
1059                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1060         }
1061         
1062         void kdm_email_changed ()
1063         {
1064                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1065         }
1066
1067         void reset_kdm_email ()
1068         {
1069                 Config::instance()->reset_kdm_email ();
1070                 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1071         }
1072
1073         wxTextCtrl* _mail_server;
1074         wxTextCtrl* _mail_user;
1075         wxTextCtrl* _mail_password;
1076         wxTextCtrl* _kdm_subject;
1077         wxTextCtrl* _kdm_from;
1078         wxTextCtrl* _kdm_cc;
1079         wxTextCtrl* _kdm_bcc;
1080         wxTextCtrl* _kdm_email;
1081         wxButton* _reset_kdm_email;
1082 };
1083
1084 /** @class AdvancedPage
1085  *  @brief Advanced page of the preferences dialog.
1086  */
1087 class AdvancedPage : public wxStockPreferencesPage, public Page
1088 {
1089 public:
1090
1091         AdvancedPage (wxSize panel_size, int border)
1092                 : wxStockPreferencesPage (Kind_Advanced)
1093                 , Page (panel_size, border)
1094         {}
1095         
1096         wxWindow* CreateWindow (wxWindow* parent)
1097         {
1098                 wxPanel* panel = new wxPanel (parent);
1099
1100                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
1101                 panel->SetSizer (s);
1102
1103                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1104                 table->AddGrowableCol (1, 1);
1105                 s->Add (table, 1, wxALL | wxEXPAND, _border);
1106
1107                 {
1108                         add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1109                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1110                         _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1111                         s->Add (_maximum_j2k_bandwidth, 1);
1112                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
1113                         table->Add (s, 1);
1114                 }
1115
1116                 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1117                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1118                 table->AddSpacer (0);
1119
1120                 add_label_to_sizer (table, panel, _("Log"), true);
1121                 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1122                 table->Add (_log_general, 1, wxEXPAND | wxALL);
1123                 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1124                 table->AddSpacer (0);
1125                 table->Add (_log_warning, 1, wxEXPAND | wxALL);
1126                 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1127                 table->AddSpacer (0);
1128                 table->Add (_log_error, 1, wxEXPAND | wxALL);
1129                 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1130                 table->AddSpacer (0);
1131                 table->Add (_log_timing, 1, wxEXPAND | wxALL);
1132
1133                 Config* config = Config::instance ();
1134                 
1135                 _maximum_j2k_bandwidth->SetRange (1, 500);
1136                 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1137                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1138                 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1139                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1140                 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1141                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1142                 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1143                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1144                 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1145                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1146                 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1147                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1148                 
1149                 return panel;
1150         }
1151
1152 private:
1153
1154         void maximum_j2k_bandwidth_changed ()
1155         {
1156                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1157         }
1158
1159         void allow_any_dcp_frame_rate_changed ()
1160         {
1161                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1162         }
1163
1164         void log_changed ()
1165         {
1166                 int types = 0;
1167                 if (_log_general->GetValue ()) {
1168                         types |= Log::TYPE_GENERAL;
1169                 }
1170                 if (_log_warning->GetValue ()) {
1171                         types |= Log::TYPE_WARNING;
1172                 }
1173                 if (_log_error->GetValue ())  {
1174                         types |= Log::TYPE_ERROR;
1175                 }
1176                 if (_log_timing->GetValue ()) {
1177                         types |= Log::TYPE_TIMING;
1178                 }
1179                 Config::instance()->set_log_types (types);
1180         }
1181         
1182         wxSpinCtrl* _maximum_j2k_bandwidth;
1183         wxCheckBox* _allow_any_dcp_frame_rate;
1184         wxCheckBox* _log_general;
1185         wxCheckBox* _log_warning;
1186         wxCheckBox* _log_error;
1187         wxCheckBox* _log_timing;
1188 };
1189         
1190 wxPreferencesEditor*
1191 create_config_dialog ()
1192 {
1193         wxPreferencesEditor* e = new wxPreferencesEditor ();
1194
1195 #ifdef DCPOMATIC_OSX
1196         /* Width that we force some of the config panels to be on OSX so that
1197            the containing window doesn't shrink too much when we select those panels.
1198            This is obviously an unpleasant hack.
1199         */
1200         wxSize ps = wxSize (480, -1);
1201         int const border = 16;
1202 #else
1203         wxSize ps = wxSize (-1, -1);
1204         int const border = 8;
1205 #endif
1206         
1207         e->AddPage (new GeneralPage (ps, border));
1208         e->AddPage (new DefaultsPage (ps, border));
1209         e->AddPage (new EncodingServersPage (ps, border));
1210         e->AddPage (new ColourConversionsPage (ps, border));
1211         e->AddPage (new KeysPage (ps, border));
1212         e->AddPage (new TMSPage (ps, border));
1213         e->AddPage (new KDMEmailPage (ps, border));
1214         e->AddPage (new AdvancedPage (ps, border));
1215         return e;
1216 }