Fix crash when re-making certificates after deleting existing ones (#690).
[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, 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_DIR_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                 _still_length->SetRange (1, 3600);
429                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
430
431                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
432
433                 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
434
435                 vector<Ratio const *> ratios = Ratio::all ();
436                 for (size_t i = 0; i < ratios.size(); ++i) {
437                         _container->Append (std_to_wx (ratios[i]->nickname ()));
438                 }
439
440                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
441
442                 vector<DCPContentType const *> const ct = DCPContentType::all ();
443                 for (size_t i = 0; i < ct.size(); ++i) {
444                         _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
445                 }
446
447                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
448
449                 _j2k_bandwidth->SetRange (50, 250);
450                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
451
452                 _audio_delay->SetRange (-1000, 1000);
453                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
454         }
455
456         void config_changed ()
457         {
458                 Config* config = Config::instance ();
459
460                 vector<Ratio const *> ratios = Ratio::all ();
461                 for (size_t i = 0; i < ratios.size(); ++i) {
462                         if (ratios[i] == config->default_container ()) {
463                                 _container->SetSelection (i);
464                         }
465                 }
466
467                 vector<DCPContentType const *> const ct = DCPContentType::all ();
468                 for (size_t i = 0; i < ct.size(); ++i) {
469                         if (ct[i] == config->default_dcp_content_type ()) {
470                                 _dcp_content_type->SetSelection (i);
471                         }
472                 }
473
474                 checked_set (_still_length, config->default_still_length ());
475                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
476                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
477                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
478                 checked_set (_audio_delay, config->default_audio_delay ());
479         }
480
481         void j2k_bandwidth_changed ()
482         {
483                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
484         }
485
486         void audio_delay_changed ()
487         {
488                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
489         }
490
491         void directory_changed ()
492         {
493                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
494         }
495
496         void edit_isdcf_metadata_clicked ()
497         {
498                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
499                 d->ShowModal ();
500                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
501                 d->Destroy ();
502         }
503
504         void still_length_changed ()
505         {
506                 Config::instance()->set_default_still_length (_still_length->GetValue ());
507         }
508
509         void container_changed ()
510         {
511                 vector<Ratio const *> ratio = Ratio::all ();
512                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
513         }
514
515         void dcp_content_type_changed ()
516         {
517                 vector<DCPContentType const *> ct = DCPContentType::all ();
518                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
519         }
520
521         wxSpinCtrl* _j2k_bandwidth;
522         wxSpinCtrl* _audio_delay;
523         wxButton* _isdcf_metadata_button;
524         wxSpinCtrl* _still_length;
525 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
526         DirPickerCtrl* _directory;
527 #else
528         wxDirPickerCtrl* _directory;
529 #endif
530         wxChoice* _container;
531         wxChoice* _dcp_content_type;
532 };
533
534 class EncodingServersPage : public StandardPage
535 {
536 public:
537         EncodingServersPage (wxSize panel_size, int border)
538                 : StandardPage (panel_size, border)
539         {}
540
541         wxString GetName () const
542         {
543                 return _("Servers");
544         }
545
546 #ifdef DCPOMATIC_OSX
547         wxBitmap GetLargeIcon () const
548         {
549                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
550         }
551 #endif
552
553 private:
554         void setup ()
555         {
556                 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
557                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
558
559                 vector<string> columns;
560                 columns.push_back (wx_to_std (_("IP address / host name")));
561                 _servers_list = new EditableList<string, ServerDialog> (
562                         _panel,
563                         columns,
564                         boost::bind (&Config::servers, Config::instance()),
565                         boost::bind (&Config::set_servers, Config::instance(), _1),
566                         boost::bind (&EncodingServersPage::server_column, this, _1)
567                         );
568
569                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
570
571                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
572         }
573
574         void config_changed ()
575         {
576                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
577                 _servers_list->refresh ();
578         }
579
580         void use_any_servers_changed ()
581         {
582                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
583         }
584
585         string server_column (string s)
586         {
587                 return s;
588         }
589
590         wxCheckBox* _use_any_servers;
591         EditableList<string, ServerDialog>* _servers_list;
592 };
593
594 class CertificateChainEditor : public wxPanel
595 {
596 public:
597         CertificateChainEditor (
598                 wxWindow* parent,
599                 wxString title,
600                 int border,
601                 function<void (shared_ptr<dcp::CertificateChain>)> set,
602                 function<shared_ptr<const dcp::CertificateChain> (void)> get
603                 )
604                 : wxPanel (parent)
605                 , _set (set)
606                 , _get (get)
607         {
608                 wxFont subheading_font (*wxNORMAL_FONT);
609                 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
610
611                 _sizer = new wxBoxSizer (wxVERTICAL);
612
613                 {
614                         wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
615                         m->SetFont (subheading_font);
616                         _sizer->Add (m, 0, wxALL, border);
617                 }
618
619                 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
620                 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
621
622                 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
623
624                 {
625                         wxListItem ip;
626                         ip.SetId (0);
627                         ip.SetText (_("Type"));
628                         ip.SetWidth (100);
629                         _certificates->InsertColumn (0, ip);
630                 }
631
632                 {
633                         wxListItem ip;
634                         ip.SetId (1);
635                         ip.SetText (_("Thumbprint"));
636                         ip.SetWidth (300);
637
638                         wxFont font = ip.GetFont ();
639                         font.SetFamily (wxFONTFAMILY_TELETYPE);
640                         ip.SetFont (font);
641
642                         _certificates->InsertColumn (1, ip);
643                 }
644
645                 certificates_sizer->Add (_certificates, 1, wxEXPAND);
646
647                 {
648                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
649                         _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
650                         s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
651                         _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
652                         s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
653                         _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
654                         s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
655                         certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
656                 }
657
658                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
659                 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
660                 int r = 0;
661
662                 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
663                 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
664                 wxFont font = _private_key->GetFont ();
665                 font.SetFamily (wxFONTFAMILY_TELETYPE);
666                 _private_key->SetFont (font);
667                 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
668                 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
669                 table->Add (_load_private_key, wxGBPosition (r, 2));
670                 ++r;
671
672                 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
673                 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
674                 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
675                 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
676                 ++r;
677
678                 _add_certificate->Bind     (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::add_certificate, this));
679                 _remove_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remove_certificate, this));
680                 _export_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::export_certificate, this));
681                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
682                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
683                 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remake_certificates, this));
684                 _load_private_key->Bind    (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::load_private_key, this));
685
686                 SetSizerAndFit (_sizer);
687         }
688
689         void config_changed ()
690         {
691                 _chain.reset (new dcp::CertificateChain (*_get().get ()));
692
693                 update_certificate_list ();
694                 update_private_key ();
695                 update_sensitivity ();
696         }
697
698         void add_button (wxWindow* button)
699         {
700                 _button_sizer->Add (button);
701                 _sizer->Layout ();
702         }
703
704 private:
705         void add_certificate ()
706         {
707                 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
708
709                 if (d->ShowModal() == wxID_OK) {
710                         try {
711                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
712                                 _chain->add (c);
713                                 _set (_chain);
714                                 update_certificate_list ();
715                         } catch (dcp::MiscError& e) {
716                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
717                         }
718                 }
719
720                 d->Destroy ();
721
722                 update_sensitivity ();
723         }
724
725         void remove_certificate ()
726         {
727                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
728                 if (i == -1) {
729                         return;
730                 }
731
732                 _certificates->DeleteItem (i);
733                 _chain->remove (i);
734                 _set (_chain);
735
736                 update_sensitivity ();
737         }
738
739         void export_certificate ()
740         {
741                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
742                 if (i == -1) {
743                         return;
744                 }
745
746                 wxFileDialog* d = new wxFileDialog (
747                         this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
748                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
749                         );
750
751                 dcp::CertificateChain::List all = _chain->root_to_leaf ();
752                 dcp::CertificateChain::List::iterator j = all.begin ();
753                 for (int k = 0; k < i; ++k) {
754                         ++j;
755                 }
756
757                 if (d->ShowModal () == wxID_OK) {
758                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
759                         if (!f) {
760                                 throw OpenFileError (wx_to_std (d->GetPath ()));
761                         }
762
763                         string const s = j->certificate (true);
764                         fwrite (s.c_str(), 1, s.length(), f);
765                         fclose (f);
766                 }
767                 d->Destroy ();
768         }
769
770         void update_certificate_list ()
771         {
772                 _certificates->DeleteAllItems ();
773                 size_t n = 0;
774                 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
775                 BOOST_FOREACH (dcp::Certificate const & i, certs) {
776                         wxListItem item;
777                         item.SetId (n);
778                         _certificates->InsertItem (item);
779                         _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
780
781                         if (n == 0) {
782                                 _certificates->SetItem (n, 0, _("Root"));
783                         } else if (n == (certs.size() - 1)) {
784                                 _certificates->SetItem (n, 0, _("Leaf"));
785                         } else {
786                                 _certificates->SetItem (n, 0, _("Intermediate"));
787                         }
788
789                         ++n;
790                 }
791         }
792
793         void remake_certificates ()
794         {
795                 shared_ptr<const dcp::CertificateChain> chain = _get ();
796
797                 string subject_organization_name;
798                 string subject_organizational_unit_name;
799                 string root_common_name;
800                 string intermediate_common_name;
801                 string leaf_common_name;
802
803                 dcp::CertificateChain::List all = chain->root_to_leaf ();
804
805                 if (all.size() >= 1) {
806                         /* Have a root */
807                         subject_organization_name = chain->root().subject_organization_name ();
808                         subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
809                         root_common_name = chain->root().subject_common_name ();
810                 }
811
812                 if (all.size() >= 2) {
813                         /* Have a leaf */
814                         leaf_common_name = chain->leaf().subject_common_name ();
815                 }
816
817                 if (all.size() >= 3) {
818                         /* Have an intermediate */
819                         dcp::CertificateChain::List::iterator i = all.begin ();
820                         ++i;
821                         intermediate_common_name = i->subject_common_name ();
822                 }
823
824                 MakeChainDialog* d = new MakeChainDialog (
825                         this,
826                         subject_organization_name,
827                         subject_organizational_unit_name,
828                         root_common_name,
829                         intermediate_common_name,
830                         leaf_common_name
831                         );
832
833                 if (d->ShowModal () == wxID_OK) {
834                         _chain.reset (
835                                 new dcp::CertificateChain (
836                                         openssl_path (),
837                                         d->organisation (),
838                                         d->organisational_unit (),
839                                         d->root_common_name (),
840                                         d->intermediate_common_name (),
841                                         d->leaf_common_name ()
842                                         )
843                                 );
844
845                         _set (_chain);
846                         update_certificate_list ();
847                         update_private_key ();
848                 }
849
850                 d->Destroy ();
851         }
852
853         void update_sensitivity ()
854         {
855                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
856                 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
857         }
858
859         void update_private_key ()
860         {
861                 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
862                 _sizer->Layout ();
863         }
864
865         void load_private_key ()
866         {
867                 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
868
869                 if (d->ShowModal() == wxID_OK) {
870                         try {
871                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
872                                 if (boost::filesystem::file_size (p) > 1024) {
873                                         error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
874                                         return;
875                                 }
876
877                                 _chain->set_key (dcp::file_to_string (p));
878                                 _set (_chain);
879                                 update_private_key ();
880                         } catch (dcp::MiscError& e) {
881                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
882                         }
883                 }
884
885                 d->Destroy ();
886
887                 update_sensitivity ();
888         }
889
890         wxListCtrl* _certificates;
891         wxButton* _add_certificate;
892         wxButton* _export_certificate;
893         wxButton* _remove_certificate;
894         wxButton* _remake_certificates;
895         wxStaticText* _private_key;
896         wxButton* _load_private_key;
897         wxSizer* _sizer;
898         wxBoxSizer* _button_sizer;
899         shared_ptr<dcp::CertificateChain> _chain;
900         boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
901         boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
902 };
903
904 class KeysPage : public StandardPage
905 {
906 public:
907         KeysPage (wxSize panel_size, int border)
908                 : StandardPage (panel_size, border)
909         {}
910
911         wxString GetName () const
912         {
913                 return _("Keys");
914         }
915
916 #ifdef DCPOMATIC_OSX
917         wxBitmap GetLargeIcon () const
918         {
919                 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
920         }
921 #endif
922
923 private:
924
925         void setup ()
926         {
927                 _signer = new CertificateChainEditor (
928                         _panel, _("Signing DCPs and KDMs"), _border,
929                         boost::bind (&Config::set_signer_chain, Config::instance (), _1),
930                         boost::bind (&Config::signer_chain, Config::instance ())
931                         );
932
933                 _panel->GetSizer()->Add (_signer);
934
935                 _decryption = new CertificateChainEditor (
936                         _panel, _("Decrypting DCPs"), _border,
937                         boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
938                         boost::bind (&Config::decryption_chain, Config::instance ())
939                         );
940
941                 _panel->GetSizer()->Add (_decryption);
942
943                 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
944                 _decryption->add_button (_export_decryption_certificate);
945
946                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
947         }
948
949         void export_decryption_certificate ()
950         {
951                 wxFileDialog* d = new wxFileDialog (
952                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
953                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
954                         );
955
956                 if (d->ShowModal () == wxID_OK) {
957                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
958                         if (!f) {
959                                 throw OpenFileError (wx_to_std (d->GetPath ()));
960                         }
961
962                         string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
963                         fwrite (s.c_str(), 1, s.length(), f);
964                         fclose (f);
965                 }
966                 d->Destroy ();
967         }
968
969         void config_changed ()
970         {
971                 _signer->config_changed ();
972                 _decryption->config_changed ();
973         }
974
975         CertificateChainEditor* _signer;
976         CertificateChainEditor* _decryption;
977         wxButton* _export_decryption_certificate;
978 };
979
980 class TMSPage : public StandardPage
981 {
982 public:
983         TMSPage (wxSize panel_size, int border)
984                 : StandardPage (panel_size, border)
985         {}
986
987         wxString GetName () const
988         {
989                 return _("TMS");
990         }
991
992 #ifdef DCPOMATIC_OSX
993         wxBitmap GetLargeIcon () const
994         {
995                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
996         }
997 #endif
998
999 private:
1000         void setup ()
1001         {
1002                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1003                 table->AddGrowableCol (1, 1);
1004                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1005
1006                 add_label_to_sizer (table, _panel, _("Protocol"), true);
1007                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1008                 table->Add (_tms_protocol, 1, wxEXPAND);
1009
1010                 add_label_to_sizer (table, _panel, _("IP address"), true);
1011                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1012                 table->Add (_tms_ip, 1, wxEXPAND);
1013
1014                 add_label_to_sizer (table, _panel, _("Target path"), true);
1015                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1016                 table->Add (_tms_path, 1, wxEXPAND);
1017
1018                 add_label_to_sizer (table, _panel, _("User name"), true);
1019                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1020                 table->Add (_tms_user, 1, wxEXPAND);
1021
1022                 add_label_to_sizer (table, _panel, _("Password"), true);
1023                 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1024                 table->Add (_tms_password, 1, wxEXPAND);
1025
1026                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1027                 _tms_protocol->Append (_("FTP (for Dolby)"));
1028
1029                 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
1030                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
1031                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
1032                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
1033                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
1034         }
1035
1036         void config_changed ()
1037         {
1038                 Config* config = Config::instance ();
1039
1040                 checked_set (_tms_protocol, config->tms_protocol ());
1041                 checked_set (_tms_ip, config->tms_ip ());
1042                 checked_set (_tms_path, config->tms_path ());
1043                 checked_set (_tms_user, config->tms_user ());
1044                 checked_set (_tms_password, config->tms_password ());
1045         }
1046
1047         void tms_protocol_changed ()
1048         {
1049                 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1050         }
1051
1052         void tms_ip_changed ()
1053         {
1054                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1055         }
1056
1057         void tms_path_changed ()
1058         {
1059                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1060         }
1061
1062         void tms_user_changed ()
1063         {
1064                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1065         }
1066
1067         void tms_password_changed ()
1068         {
1069                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1070         }
1071
1072         wxChoice* _tms_protocol;
1073         wxTextCtrl* _tms_ip;
1074         wxTextCtrl* _tms_path;
1075         wxTextCtrl* _tms_user;
1076         wxTextCtrl* _tms_password;
1077 };
1078
1079 class KDMEmailPage : public StandardPage
1080 {
1081 public:
1082
1083         KDMEmailPage (wxSize panel_size, int border)
1084 #ifdef DCPOMATIC_OSX
1085                 /* We have to force both width and height of this one */
1086                 : StandardPage (wxSize (480, 128), border)
1087 #else
1088                  : StandardPage (panel_size, border)
1089 #endif
1090         {}
1091
1092         wxString GetName () const
1093         {
1094                 return _("KDM Email");
1095         }
1096
1097 #ifdef DCPOMATIC_OSX
1098         wxBitmap GetLargeIcon () const
1099         {
1100                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1101         }
1102 #endif
1103
1104 private:
1105         void setup ()
1106         {
1107                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1108                 table->AddGrowableCol (1, 1);
1109                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1110
1111                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1112                 {
1113                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1114                         _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1115                         s->Add (_mail_server, 1, wxEXPAND | wxALL);
1116                         add_label_to_sizer (s, _panel, _("port"), false);
1117                         _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1118                         _mail_port->SetRange (0, 65535);
1119                         s->Add (_mail_port);
1120                         table->Add (s, 1, wxEXPAND | wxALL);
1121                 }
1122
1123                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1124                 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1125                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1126
1127                 add_label_to_sizer (table, _panel, _("Mail password"), true);
1128                 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1129                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1130
1131                 add_label_to_sizer (table, _panel, _("Subject"), true);
1132                 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1133                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1134
1135                 add_label_to_sizer (table, _panel, _("From address"), true);
1136                 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1137                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1138
1139                 add_label_to_sizer (table, _panel, _("CC address"), true);
1140                 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1141                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1142
1143                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1144                 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1145                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1146
1147                 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1148                 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1149
1150                 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1151                 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1152
1153                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1154                 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1155                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1156                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1157                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1158                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1159                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1160                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1161                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1162                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1163         }
1164
1165         void config_changed ()
1166         {
1167                 Config* config = Config::instance ();
1168
1169                 checked_set (_mail_server, config->mail_server ());
1170                 checked_set (_mail_port, config->mail_port ());
1171                 checked_set (_mail_user, config->mail_user ());
1172                 checked_set (_mail_password, config->mail_password ());
1173                 checked_set (_kdm_subject, config->kdm_subject ());
1174                 checked_set (_kdm_from, config->kdm_from ());
1175                 checked_set (_kdm_cc, config->kdm_cc ());
1176                 checked_set (_kdm_bcc, config->kdm_bcc ());
1177                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1178         }
1179
1180         void mail_server_changed ()
1181         {
1182                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1183         }
1184
1185         void mail_port_changed ()
1186         {
1187                 Config::instance()->set_mail_port (_mail_port->GetValue ());
1188         }
1189
1190         void mail_user_changed ()
1191         {
1192                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1193         }
1194
1195         void mail_password_changed ()
1196         {
1197                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1198         }
1199
1200         void kdm_subject_changed ()
1201         {
1202                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1203         }
1204
1205         void kdm_from_changed ()
1206         {
1207                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1208         }
1209
1210         void kdm_cc_changed ()
1211         {
1212                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1213         }
1214
1215         void kdm_bcc_changed ()
1216         {
1217                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1218         }
1219
1220         void kdm_email_changed ()
1221         {
1222                 if (_kdm_email->GetValue().IsEmpty ()) {
1223                         /* Sometimes we get sent an erroneous notification that the email
1224                            is empty; I don't know why.
1225                         */
1226                         return;
1227                 }
1228                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1229         }
1230
1231         void reset_kdm_email ()
1232         {
1233                 Config::instance()->reset_kdm_email ();
1234                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1235         }
1236
1237         wxTextCtrl* _mail_server;
1238         wxSpinCtrl* _mail_port;
1239         wxTextCtrl* _mail_user;
1240         wxTextCtrl* _mail_password;
1241         wxTextCtrl* _kdm_subject;
1242         wxTextCtrl* _kdm_from;
1243         wxTextCtrl* _kdm_cc;
1244         wxTextCtrl* _kdm_bcc;
1245         wxTextCtrl* _kdm_email;
1246         wxButton* _reset_kdm_email;
1247 };
1248
1249 /** @class AdvancedPage
1250  *  @brief Advanced page of the preferences dialog.
1251  */
1252 class AdvancedPage : public StockPage
1253 {
1254 public:
1255         AdvancedPage (wxSize panel_size, int border)
1256                 : StockPage (Kind_Advanced, panel_size, border)
1257                 , _maximum_j2k_bandwidth (0)
1258                 , _allow_any_dcp_frame_rate (0)
1259                 , _only_servers_encode (0)
1260                 , _log_general (0)
1261                 , _log_warning (0)
1262                 , _log_error (0)
1263                 , _log_timing (0)
1264                 , _log_debug_decode (0)
1265                 , _log_debug_encode (0)
1266         {}
1267
1268 private:
1269         void setup ()
1270         {
1271                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1272                 table->AddGrowableCol (1, 1);
1273                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1274
1275                 {
1276                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1277                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1278                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1279                         s->Add (_maximum_j2k_bandwidth, 1);
1280                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1281                         table->Add (s, 1);
1282                 }
1283
1284                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1285                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1286                 table->AddSpacer (0);
1287
1288                 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1289                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1290                 table->AddSpacer (0);
1291
1292 #ifdef __WXOSX__
1293                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1294                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1295 #else
1296                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1297                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1298 #endif
1299
1300                 {
1301                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1302                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1303                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1304                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1305                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1306                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1307                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1308                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1309                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1310                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1311                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1312                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1313                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1314                         table->Add (t, 0, wxALL, 6);
1315                 }
1316
1317 #ifdef DCPOMATIC_WINDOWS
1318                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1319                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1320                 table->AddSpacer (0);
1321 #endif
1322
1323                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1324                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1325                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1326                 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1327                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1328                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1329                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1330                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1331                 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1332                 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1333 #ifdef DCPOMATIC_WINDOWS
1334                 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1335 #endif
1336         }
1337
1338         void config_changed ()
1339         {
1340                 Config* config = Config::instance ();
1341
1342                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1343                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1344                 checked_set (_only_servers_encode, config->only_servers_encode ());
1345                 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1346                 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1347                 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1348                 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1349                 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1350                 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1351 #ifdef DCPOMATIC_WINDOWS
1352                 checked_set (_win32_console, config->win32_console());
1353 #endif
1354         }
1355
1356         void maximum_j2k_bandwidth_changed ()
1357         {
1358                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1359         }
1360
1361         void allow_any_dcp_frame_rate_changed ()
1362         {
1363                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1364         }
1365
1366         void only_servers_encode_changed ()
1367         {
1368                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1369         }
1370
1371         void log_changed ()
1372         {
1373                 int types = 0;
1374                 if (_log_general->GetValue ()) {
1375                         types |= Log::TYPE_GENERAL;
1376                 }
1377                 if (_log_warning->GetValue ()) {
1378                         types |= Log::TYPE_WARNING;
1379                 }
1380                 if (_log_error->GetValue ())  {
1381                         types |= Log::TYPE_ERROR;
1382                 }
1383                 if (_log_timing->GetValue ()) {
1384                         types |= Log::TYPE_TIMING;
1385                 }
1386                 if (_log_debug_decode->GetValue ()) {
1387                         types |= Log::TYPE_DEBUG_DECODE;
1388                 }
1389                 if (_log_debug_encode->GetValue ()) {
1390                         types |= Log::TYPE_DEBUG_ENCODE;
1391                 }
1392                 Config::instance()->set_log_types (types);
1393         }
1394
1395 #ifdef DCPOMATIC_WINDOWS
1396         void win32_console_changed ()
1397         {
1398                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1399         }
1400 #endif
1401
1402         wxSpinCtrl* _maximum_j2k_bandwidth;
1403         wxCheckBox* _allow_any_dcp_frame_rate;
1404         wxCheckBox* _only_servers_encode;
1405         wxCheckBox* _log_general;
1406         wxCheckBox* _log_warning;
1407         wxCheckBox* _log_error;
1408         wxCheckBox* _log_timing;
1409         wxCheckBox* _log_debug_decode;
1410         wxCheckBox* _log_debug_encode;
1411 #ifdef DCPOMATIC_WINDOWS
1412         wxCheckBox* _win32_console;
1413 #endif
1414 };
1415
1416 wxPreferencesEditor*
1417 create_config_dialog ()
1418 {
1419         wxPreferencesEditor* e = new wxPreferencesEditor ();
1420
1421 #ifdef DCPOMATIC_OSX
1422         /* Width that we force some of the config panels to be on OSX so that
1423            the containing window doesn't shrink too much when we select those panels.
1424            This is obviously an unpleasant hack.
1425         */
1426         wxSize ps = wxSize (520, -1);
1427         int const border = 16;
1428 #else
1429         wxSize ps = wxSize (-1, -1);
1430         int const border = 8;
1431 #endif
1432
1433         e->AddPage (new GeneralPage (ps, border));
1434         e->AddPage (new DefaultsPage (ps, border));
1435         e->AddPage (new EncodingServersPage (ps, border));
1436         e->AddPage (new KeysPage (ps, border));
1437         e->AddPage (new TMSPage (ps, border));
1438         e->AddPage (new KDMEmailPage (ps, border));
1439         e->AddPage (new AdvancedPage (ps, border));
1440         return e;
1441 }