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