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