Tidy layout of keys prefs a bit (#460).
[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                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
602                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
603
604                 int r = 0;
605                 
606                 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
607                 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
608                 ++r;
609
610                 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
611                 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
612                 wxFont font = _signer_private_key->GetFont ();
613                 font.SetFamily (wxFONTFAMILY_TELETYPE);
614                 _signer_private_key->SetFont (font);
615                 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
616                 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
617                 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
618                 ++r;
619
620                 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
621                 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
622                 _decryption_certificate->SetFont (font);
623                 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
624                 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
625                 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
626                 ++r;
627
628                 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
629                 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
630                 _decryption_private_key->SetFont (font);
631                 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
632                 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
633                 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
634                 ++r;
635
636                 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
637                 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
638                 ++r;
639                 
640                 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
641                 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
642                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
643                 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
644                 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
645                 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
646                 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
647                 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
648                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
649         }
650
651         void config_changed ()
652         {
653                 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
654
655                 update_certificate_list ();
656                 update_signer_private_key ();
657                 update_decryption_certificate ();
658                 update_decryption_private_key ();
659                 update_sensitivity ();
660         }
661         
662         void add_certificate ()
663         {
664                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
665                 
666                 if (d->ShowModal() == wxID_OK) {
667                         try {
668                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
669                                 _signer->certificates().add (c);
670                                 Config::instance()->set_signer (_signer);
671                                 update_certificate_list ();
672                         } catch (dcp::MiscError& e) {
673                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
674                         }
675                 }
676                 
677                 d->Destroy ();
678
679                 update_sensitivity ();
680         }
681
682         void remove_certificate ()
683         {
684                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
685                 if (i == -1) {
686                         return;
687                 }
688                 
689                 _certificates->DeleteItem (i);
690                 _signer->certificates().remove (i);
691                 Config::instance()->set_signer (_signer);
692
693                 update_sensitivity ();
694         }
695
696         void update_certificate_list ()
697         {
698                 _certificates->DeleteAllItems ();
699                 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
700                 size_t n = 0;
701                 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
702                         wxListItem item;
703                         item.SetId (n);
704                         _certificates->InsertItem (item);
705                         _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
706
707                         if (n == 0) {
708                                 _certificates->SetItem (n, 0, _("Root"));
709                         } else if (n == (certs.size() - 1)) {
710                                 _certificates->SetItem (n, 0, _("Leaf"));
711                         } else {
712                                 _certificates->SetItem (n, 0, _("Intermediate"));
713                         }
714
715                         ++n;
716                 }
717         }
718
719         void remake_certificates ()
720         {
721                 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
722                 if (d->ShowModal () == wxID_OK) {
723                         _signer.reset (
724                                 new dcp::Signer (
725                                         openssl_path (),
726                                         d->organisation (),
727                                         d->organisational_unit (),
728                                         d->root_common_name (),
729                                         d->intermediate_common_name (),
730                                         d->leaf_common_name ()
731                                         )
732                                 );
733
734                         Config::instance()->set_signer (_signer);
735                         update_certificate_list ();
736                         update_signer_private_key ();
737                 }
738                 
739                 d->Destroy ();
740         }
741
742         void update_sensitivity ()
743         {
744                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
745         }
746
747         void update_signer_private_key ()
748         {
749                 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
750         }       
751
752         void load_signer_private_key ()
753         {
754                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
755
756                 if (d->ShowModal() == wxID_OK) {
757                         try {
758                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
759                                 if (boost::filesystem::file_size (p) > 1024) {
760                                         error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
761                                         return;
762                                 }
763                                 
764                                 _signer->set_key (dcp::file_to_string (p));
765                                 Config::instance()->set_signer (_signer);
766                                 update_signer_private_key ();
767                         } catch (dcp::MiscError& e) {
768                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
769                         }
770                 }
771                 
772                 d->Destroy ();
773
774                 update_sensitivity ();
775
776         }
777
778         void load_decryption_certificate ()
779         {
780                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
781                 
782                 if (d->ShowModal() == wxID_OK) {
783                         try {
784                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
785                                 Config::instance()->set_decryption_certificate (c);
786                                 update_decryption_certificate ();
787                         } catch (dcp::MiscError& e) {
788                                 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
789                         }
790                 }
791                 
792                 d->Destroy ();
793         }
794
795         void update_decryption_certificate ()
796         {
797                 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
798         }
799
800         void load_decryption_private_key ()
801         {
802                 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
803
804                 if (d->ShowModal() == wxID_OK) {
805                         try {
806                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
807                                 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
808                                 update_decryption_private_key ();
809                         } catch (dcp::MiscError& e) {
810                                 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
811                         }
812                 }
813                 
814                 d->Destroy ();
815         }
816
817         void update_decryption_private_key ()
818         {
819                 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
820         }
821
822         void export_decryption_certificate ()
823         {
824                 wxFileDialog* d = new wxFileDialog (
825                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
826                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
827                         );
828                 
829                 if (d->ShowModal () == wxID_OK) {
830                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
831                         if (!f) {
832                                 throw OpenFileError (wx_to_std (d->GetPath ()));
833                         }
834
835                         string const s = Config::instance()->decryption_certificate().certificate (true);
836                         fwrite (s.c_str(), 1, s.length(), f);
837                         fclose (f);
838                 }
839                 d->Destroy ();
840         }
841
842         wxListCtrl* _certificates;
843         wxButton* _add_certificate;
844         wxButton* _remove_certificate;
845         wxButton* _remake_certificates;
846         wxStaticText* _signer_private_key;
847         wxButton* _load_signer_private_key;
848         wxStaticText* _decryption_certificate;
849         wxButton* _load_decryption_certificate;
850         wxStaticText* _decryption_private_key;
851         wxButton* _load_decryption_private_key;
852         wxButton* _export_decryption_certificate;
853         shared_ptr<dcp::Signer> _signer;
854 };
855
856 class TMSPage : public StandardPage
857 {
858 public:
859         TMSPage (wxSize panel_size, int border)
860                 : StandardPage (panel_size, border)
861         {}
862
863         wxString GetName () const
864         {
865                 return _("TMS");
866         }
867
868 #ifdef DCPOMATIC_OSX    
869         wxBitmap GetLargeIcon () const
870         {
871                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
872         }
873 #endif  
874
875 private:        
876         void setup ()
877         {
878                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
879                 table->AddGrowableCol (1, 1);
880                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
881                 
882                 add_label_to_sizer (table, _panel, _("IP address"), true);
883                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
884                 table->Add (_tms_ip, 1, wxEXPAND);
885                 
886                 add_label_to_sizer (table, _panel, _("Target path"), true);
887                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
888                 table->Add (_tms_path, 1, wxEXPAND);
889                 
890                 add_label_to_sizer (table, _panel, _("User name"), true);
891                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
892                 table->Add (_tms_user, 1, wxEXPAND);
893                 
894                 add_label_to_sizer (table, _panel, _("Password"), true);
895                 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
896                 table->Add (_tms_password, 1, wxEXPAND);
897                 
898                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
899                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
900                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
901                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
902         }
903
904         void config_changed ()
905         {
906                 Config* config = Config::instance ();
907                 
908                 checked_set (_tms_ip, config->tms_ip ());
909                 checked_set (_tms_path, config->tms_path ());
910                 checked_set (_tms_user, config->tms_user ());
911                 checked_set (_tms_password, config->tms_password ());
912         }
913         
914         void tms_ip_changed ()
915         {
916                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
917         }
918         
919         void tms_path_changed ()
920         {
921                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
922         }
923         
924         void tms_user_changed ()
925         {
926                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
927         }
928         
929         void tms_password_changed ()
930         {
931                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
932         }
933
934         wxTextCtrl* _tms_ip;
935         wxTextCtrl* _tms_path;
936         wxTextCtrl* _tms_user;
937         wxTextCtrl* _tms_password;
938 };
939
940 class KDMEmailPage : public StandardPage
941 {
942 public:
943
944         KDMEmailPage (wxSize panel_size, int border)
945 #ifdef DCPOMATIC_OSX           
946                 /* We have to force both width and height of this one */
947                 : StandardPage (wxSize (480, 128), border)
948 #else
949                  : StandardPage (panel_size, border)
950 #endif          
951         {}
952         
953         wxString GetName () const
954         {
955                 return _("KDM Email");
956         }
957
958 #ifdef DCPOMATIC_OSX    
959         wxBitmap GetLargeIcon () const
960         {
961                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
962         }
963 #endif  
964
965 private:        
966         void setup ()
967         {
968                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
969                 table->AddGrowableCol (1, 1);
970                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
971
972                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
973                 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
974                 table->Add (_mail_server, 1, wxEXPAND | wxALL);
975                 
976                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
977                 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
978                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
979                 
980                 add_label_to_sizer (table, _panel, _("Mail password"), true);
981                 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
982                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
983                 
984                 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
985                 wxFont font = plain->GetFont();
986                 font.SetStyle (wxFONTSTYLE_ITALIC);
987                 font.SetPointSize (font.GetPointSize() - 1);
988                 plain->SetFont (font);
989                 table->AddSpacer (0);
990
991                 add_label_to_sizer (table, _panel, _("Subject"), true);
992                 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
993                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
994                 
995                 add_label_to_sizer (table, _panel, _("From address"), true);
996                 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
997                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
998
999                 add_label_to_sizer (table, _panel, _("CC address"), true);
1000                 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1001                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1002
1003                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1004                 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1005                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1006                 
1007                 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1008                 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1009
1010                 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1011                 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1012
1013                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1014                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1015                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1016                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1017                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1018                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1019                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1020                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1021                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1022         }
1023
1024         void config_changed ()
1025         {
1026                 Config* config = Config::instance ();
1027                 
1028                 checked_set (_mail_server, config->mail_server ());
1029                 checked_set (_mail_user, config->mail_user ());
1030                 checked_set (_mail_password, config->mail_password ());
1031                 checked_set (_kdm_subject, config->kdm_subject ());
1032                 checked_set (_kdm_from, config->kdm_from ());
1033                 checked_set (_kdm_cc, config->kdm_cc ());
1034                 checked_set (_kdm_bcc, config->kdm_bcc ());
1035                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1036         }
1037         
1038         void mail_server_changed ()
1039         {
1040                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1041         }
1042         
1043         void mail_user_changed ()
1044         {
1045                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1046         }
1047         
1048         void mail_password_changed ()
1049         {
1050                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1051         }
1052
1053         void kdm_subject_changed ()
1054         {
1055                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1056         }
1057         
1058         void kdm_from_changed ()
1059         {
1060                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1061         }
1062
1063         void kdm_cc_changed ()
1064         {
1065                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1066         }
1067
1068         void kdm_bcc_changed ()
1069         {
1070                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1071         }
1072         
1073         void kdm_email_changed ()
1074         {
1075                 if (_kdm_email->GetValue().IsEmpty ()) {
1076                         /* Sometimes we get sent an erroneous notification that the email
1077                            is empty; I don't know why.
1078                         */
1079                         return;
1080                 }
1081                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1082         }
1083
1084         void reset_kdm_email ()
1085         {
1086                 Config::instance()->reset_kdm_email ();
1087                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1088         }
1089
1090         wxTextCtrl* _mail_server;
1091         wxTextCtrl* _mail_user;
1092         wxTextCtrl* _mail_password;
1093         wxTextCtrl* _kdm_subject;
1094         wxTextCtrl* _kdm_from;
1095         wxTextCtrl* _kdm_cc;
1096         wxTextCtrl* _kdm_bcc;
1097         wxTextCtrl* _kdm_email;
1098         wxButton* _reset_kdm_email;
1099 };
1100
1101 /** @class AdvancedPage
1102  *  @brief Advanced page of the preferences dialog.
1103  */
1104 class AdvancedPage : public StockPage
1105 {
1106 public:
1107         AdvancedPage (wxSize panel_size, int border)
1108                 : StockPage (Kind_Advanced, panel_size, border)
1109                 , _maximum_j2k_bandwidth (0)
1110                 , _allow_any_dcp_frame_rate (0)
1111                 , _log_general (0)
1112                 , _log_warning (0)
1113                 , _log_error (0)
1114                 , _log_timing (0)
1115         {}
1116
1117 private:        
1118         void setup ()
1119         {
1120                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1121                 table->AddGrowableCol (1, 1);
1122                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1123
1124                 {
1125                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1126                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1127                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1128                         s->Add (_maximum_j2k_bandwidth, 1);
1129                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1130                         table->Add (s, 1);
1131                 }
1132
1133                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1134                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1135                 table->AddSpacer (0);
1136
1137 #ifdef __WXOSX__
1138                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1139                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1140 #else           
1141                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1142                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1143 #endif          
1144                 
1145                 {
1146                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1147                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1148                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1149                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1150                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1151                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1152                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1153                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1154                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1155                         table->Add (t, 0, wxALL, 6);
1156                 }
1157
1158 #ifdef DCPOMATIC_WINDOWS                
1159                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1160                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1161                 table->AddSpacer (0);
1162 #endif          
1163                 
1164                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1165                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1166                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1167                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1168                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1169                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1170                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1171 #ifdef DCPOMATIC_WINDOWS
1172                 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1173 #endif          
1174         }
1175
1176         void config_changed ()
1177         {
1178                 Config* config = Config::instance ();
1179                 
1180                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1181                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1182                 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1183                 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1184                 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1185                 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1186 #ifdef DCPOMATIC_WINDOWS
1187                 checked_set (_win32_console, config->win32_console());
1188 #endif
1189         }
1190
1191         void maximum_j2k_bandwidth_changed ()
1192         {
1193                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1194         }
1195
1196         void allow_any_dcp_frame_rate_changed ()
1197         {
1198                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1199         }
1200
1201         void log_changed ()
1202         {
1203                 int types = 0;
1204                 if (_log_general->GetValue ()) {
1205                         types |= Log::TYPE_GENERAL;
1206                 }
1207                 if (_log_warning->GetValue ()) {
1208                         types |= Log::TYPE_WARNING;
1209                 }
1210                 if (_log_error->GetValue ())  {
1211                         types |= Log::TYPE_ERROR;
1212                 }
1213                 if (_log_timing->GetValue ()) {
1214                         types |= Log::TYPE_TIMING;
1215                 }
1216                 Config::instance()->set_log_types (types);
1217         }
1218
1219 #ifdef DCPOMATIC_WINDOWS        
1220         void win32_console_changed ()
1221         {
1222                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1223         }
1224 #endif  
1225         
1226         wxSpinCtrl* _maximum_j2k_bandwidth;
1227         wxCheckBox* _allow_any_dcp_frame_rate;
1228         wxCheckBox* _log_general;
1229         wxCheckBox* _log_warning;
1230         wxCheckBox* _log_error;
1231         wxCheckBox* _log_timing;
1232 #ifdef DCPOMATIC_WINDOWS        
1233         wxCheckBox* _win32_console;
1234 #endif  
1235 };
1236         
1237 wxPreferencesEditor*
1238 create_config_dialog ()
1239 {
1240         wxPreferencesEditor* e = new wxPreferencesEditor ();
1241
1242 #ifdef DCPOMATIC_OSX
1243         /* Width that we force some of the config panels to be on OSX so that
1244            the containing window doesn't shrink too much when we select those panels.
1245            This is obviously an unpleasant hack.
1246         */
1247         wxSize ps = wxSize (520, -1);
1248         int const border = 16;
1249 #else
1250         wxSize ps = wxSize (-1, -1);
1251         int const border = 8;
1252 #endif
1253         
1254         e->AddPage (new GeneralPage (ps, border));
1255         e->AddPage (new DefaultsPage (ps, border));
1256         e->AddPage (new EncodingServersPage (ps, border));
1257         e->AddPage (new KeysPage (ps, border));
1258         e->AddPage (new TMSPage (ps, border));
1259         e->AddPage (new KDMEmailPage (ps, border));
1260         e->AddPage (new AdvancedPage (ps, border));
1261         return e;
1262 }