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