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