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