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