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