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