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