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