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