2 Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
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.
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.
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.
20 /** @file src/config_dialog.cc
21 * @brief A dialogue to edit DCP-o-matic configuration.
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/colour_matrix.h>
32 #include <dcp/exceptions.h>
33 #include <dcp/signer.h>
34 #include "lib/config.h"
35 #include "lib/ratio.h"
36 #include "lib/scaler.h"
37 #include "lib/filter.h"
38 #include "lib/dcp_content_type.h"
39 #include "lib/colour_conversion.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include "config_dialog.h"
46 #include "editable_list.h"
47 #include "filter_dialog.h"
48 #include "dir_picker_ctrl.h"
49 #include "isdcf_metadata_dialog.h"
50 #include "preset_colour_conversion_dialog.h"
51 #include "server_dialog.h"
58 using boost::shared_ptr;
59 using boost::lexical_cast;
64 Page (wxSize panel_size, int border)
65 : _panel_size (panel_size)
74 class GeneralPage : public wxStockPreferencesPage, public Page
77 GeneralPage (wxSize panel_size, int border)
78 : wxStockPreferencesPage (Kind_General)
79 , Page (panel_size, border)
82 wxWindow* CreateWindow (wxWindow* parent)
84 wxPanel* panel = new wxPanel (parent);
85 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
88 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
89 table->AddGrowableCol (1, 1);
90 s->Add (table, 1, wxALL | wxEXPAND, _border);
92 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
93 table->Add (_set_language, 1);
94 _language = new wxChoice (panel, wxID_ANY);
95 _language->Append (wxT ("Deutsch"));
96 _language->Append (wxT ("English"));
97 _language->Append (wxT ("Español"));
98 _language->Append (wxT ("Français"));
99 _language->Append (wxT ("Italiano"));
100 _language->Append (wxT ("Nederlands"));
101 _language->Append (wxT ("Svenska"));
102 table->Add (_language);
104 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
105 wxFont font = restart->GetFont();
106 font.SetStyle (wxFONTSTYLE_ITALIC);
107 font.SetPointSize (font.GetPointSize() - 1);
108 restart->SetFont (font);
109 table->AddSpacer (0);
111 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
112 _num_local_encoding_threads = new wxSpinCtrl (panel);
113 table->Add (_num_local_encoding_threads, 1);
115 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
116 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
117 table->AddSpacer (0);
119 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
120 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
121 table->AddSpacer (0);
123 Config* config = Config::instance ();
125 _set_language->SetValue (config->language ());
127 if (config->language().get_value_or ("") == "fr") {
128 _language->SetSelection (3);
129 } else if (config->language().get_value_or ("") == "it") {
130 _language->SetSelection (4);
131 } else if (config->language().get_value_or ("") == "es") {
132 _language->SetSelection (2);
133 } else if (config->language().get_value_or ("") == "sv") {
134 _language->SetSelection (6);
135 } else if (config->language().get_value_or ("") == "de") {
136 _language->SetSelection (0);
137 } else if (config->language().get_value_or ("") == "nl") {
138 _language->SetSelection (5);
140 _language->SetSelection (1);
143 setup_language_sensitivity ();
145 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
146 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
148 _num_local_encoding_threads->SetRange (1, 128);
149 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
150 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
152 _check_for_updates->SetValue (config->check_for_updates ());
153 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
154 _check_for_test_updates->SetValue (config->check_for_test_updates ());
155 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
161 void setup_language_sensitivity ()
163 _language->Enable (_set_language->GetValue ());
166 void set_language_changed ()
168 setup_language_sensitivity ();
169 if (_set_language->GetValue ()) {
172 Config::instance()->unset_language ();
176 void language_changed ()
178 switch (_language->GetSelection ()) {
180 Config::instance()->set_language ("de");
183 Config::instance()->set_language ("en");
186 Config::instance()->set_language ("es");
189 Config::instance()->set_language ("fr");
192 Config::instance()->set_language ("it");
195 Config::instance()->set_language ("nl");
198 Config::instance()->set_language ("sv");
203 void check_for_updates_changed ()
205 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
208 void check_for_test_updates_changed ()
210 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
213 void num_local_encoding_threads_changed ()
215 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
218 wxCheckBox* _set_language;
220 wxSpinCtrl* _num_local_encoding_threads;
221 wxCheckBox* _check_for_updates;
222 wxCheckBox* _check_for_test_updates;
225 class DefaultsPage : public wxPreferencesPage, public Page
228 DefaultsPage (wxSize panel_size, int border)
229 : Page (panel_size, border)
232 wxString GetName () const
234 return _("Defaults");
238 wxBitmap GetLargeIcon () const
240 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
244 wxWindow* CreateWindow (wxWindow* parent)
246 wxPanel* panel = new wxPanel (parent);
247 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
250 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
251 table->AddGrowableCol (1, 1);
252 s->Add (table, 1, wxALL | wxEXPAND, _border);
255 add_label_to_sizer (table, panel, _("Default duration of still images"), true);
256 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
257 _still_length = new wxSpinCtrl (panel);
258 s->Add (_still_length);
259 add_label_to_sizer (s, panel, _("s"), false);
263 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
264 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
265 _directory = new DirPickerCtrl (panel);
267 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
269 table->Add (_directory, 1, wxEXPAND);
271 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
272 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
273 table->Add (_isdcf_metadata_button);
275 add_label_to_sizer (table, panel, _("Default scale to"), true);
276 _scale = new wxChoice (panel, wxID_ANY);
279 add_label_to_sizer (table, panel, _("Default container"), true);
280 _container = new wxChoice (panel, wxID_ANY);
281 table->Add (_container);
283 add_label_to_sizer (table, panel, _("Default content type"), true);
284 _dcp_content_type = new wxChoice (panel, wxID_ANY);
285 table->Add (_dcp_content_type);
288 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
289 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
290 _j2k_bandwidth = new wxSpinCtrl (panel);
291 s->Add (_j2k_bandwidth);
292 add_label_to_sizer (s, panel, _("Mbit/s"), false);
297 add_label_to_sizer (table, panel, _("Default audio delay"), true);
298 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
299 _audio_delay = new wxSpinCtrl (panel);
300 s->Add (_audio_delay);
301 add_label_to_sizer (s, panel, _("ms"), false);
305 add_label_to_sizer (table, panel, _("Default issuer"), true);
306 _issuer = new wxTextCtrl (panel, wxID_ANY);
307 table->Add (_issuer, 1, wxEXPAND);
309 Config* config = Config::instance ();
311 _still_length->SetRange (1, 3600);
312 _still_length->SetValue (config->default_still_length ());
313 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
315 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
316 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
318 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
320 vector<VideoContentScale> scales = VideoContentScale::all ();
321 for (size_t i = 0; i < scales.size(); ++i) {
322 _scale->Append (std_to_wx (scales[i].name ()));
323 if (scales[i] == config->default_scale ()) {
324 _scale->SetSelection (i);
328 vector<Ratio const *> ratios = Ratio::all ();
329 for (size_t i = 0; i < ratios.size(); ++i) {
330 _container->Append (std_to_wx (ratios[i]->nickname ()));
331 if (ratios[i] == config->default_container ()) {
332 _container->SetSelection (i);
336 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
337 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
339 vector<DCPContentType const *> const ct = DCPContentType::all ();
340 for (size_t i = 0; i < ct.size(); ++i) {
341 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
342 if (ct[i] == config->default_dcp_content_type ()) {
343 _dcp_content_type->SetSelection (i);
347 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
349 _j2k_bandwidth->SetRange (50, 250);
350 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
351 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
353 _audio_delay->SetRange (-1000, 1000);
354 _audio_delay->SetValue (config->default_audio_delay ());
355 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
357 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
358 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
364 void j2k_bandwidth_changed ()
366 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
369 void audio_delay_changed ()
371 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
374 void directory_changed ()
376 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
379 void edit_isdcf_metadata_clicked (wxWindow* parent)
381 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
383 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
387 void still_length_changed ()
389 Config::instance()->set_default_still_length (_still_length->GetValue ());
392 void scale_changed ()
394 vector<VideoContentScale> scale = VideoContentScale::all ();
395 Config::instance()->set_default_scale (scale[_scale->GetSelection()]);
398 void container_changed ()
400 vector<Ratio const *> ratio = Ratio::all ();
401 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
404 void dcp_content_type_changed ()
406 vector<DCPContentType const *> ct = DCPContentType::all ();
407 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
410 void issuer_changed ()
412 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
415 wxSpinCtrl* _j2k_bandwidth;
416 wxSpinCtrl* _audio_delay;
417 wxButton* _isdcf_metadata_button;
418 wxSpinCtrl* _still_length;
419 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
420 DirPickerCtrl* _directory;
422 wxDirPickerCtrl* _directory;
425 wxChoice* _container;
426 wxChoice* _dcp_content_type;
430 class EncodingServersPage : public wxPreferencesPage, public Page
433 EncodingServersPage (wxSize panel_size, int border)
434 : Page (panel_size, border)
437 wxString GetName () const
443 wxBitmap GetLargeIcon () const
445 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
449 wxWindow* CreateWindow (wxWindow* parent)
451 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
452 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
455 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
456 s->Add (_use_any_servers, 0, wxALL, _border);
458 vector<string> columns;
459 columns.push_back (wx_to_std (_("IP address / host name")));
460 _servers_list = new EditableList<string, ServerDialog> (
463 boost::bind (&Config::servers, Config::instance()),
464 boost::bind (&Config::set_servers, Config::instance(), _1),
465 boost::bind (&EncodingServersPage::server_column, this, _1)
468 s->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
470 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
471 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
478 void use_any_servers_changed ()
480 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
483 string server_column (string s)
488 wxCheckBox* _use_any_servers;
489 EditableList<string, ServerDialog>* _servers_list;
492 class ColourConversionsPage : public wxPreferencesPage, public Page
495 ColourConversionsPage (wxSize panel_size, int border)
496 : Page (panel_size, border)
499 wxString GetName () const
501 return _("Colour Conversions");
505 wxBitmap GetLargeIcon () const
507 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
510 wxWindow* CreateWindow (wxWindow* parent)
512 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
513 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
516 vector<string> columns;
517 columns.push_back (wx_to_std (_("Name")));
518 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
521 boost::bind (&Config::colour_conversions, Config::instance()),
522 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
523 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
527 s->Add (list, 1, wxEXPAND | wxALL, _border);
532 string colour_conversion_column (PresetColourConversion c)
538 class KeysPage : public wxPreferencesPage, public Page
541 KeysPage (wxSize panel_size, int border)
542 : Page (panel_size, border)
545 wxString GetName () const
551 wxBitmap GetLargeIcon () const
553 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
557 wxWindow* CreateWindow (wxWindow* parent)
559 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
560 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
561 _panel->SetSizer (overall_sizer);
563 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
564 overall_sizer->Add (m, 0, wxALL, _border);
566 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
567 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
569 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
574 ip.SetText (_("Type"));
576 _certificates->InsertColumn (0, ip);
582 ip.SetText (_("Thumbprint"));
585 wxFont font = ip.GetFont ();
586 font.SetFamily (wxFONTFAMILY_TELETYPE);
589 _certificates->InsertColumn (1, ip);
592 certificates_sizer->Add (_certificates, 1, wxEXPAND);
595 wxSizer* s = new wxBoxSizer (wxVERTICAL);
596 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
597 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
598 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
599 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
600 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
603 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
604 table->AddGrowableCol (1, 1);
605 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
607 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
609 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
610 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
611 wxFont font = _signer_private_key->GetFont ();
612 font.SetFamily (wxFONTFAMILY_TELETYPE);
613 _signer_private_key->SetFont (font);
614 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
615 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
616 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
620 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
622 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
623 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
624 wxFont font = _decryption_certificate->GetFont ();
625 font.SetFamily (wxFONTFAMILY_TELETYPE);
626 _decryption_certificate->SetFont (font);
627 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
628 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
629 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
633 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
635 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
636 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
637 wxFont font = _decryption_private_key->GetFont ();
638 font.SetFamily (wxFONTFAMILY_TELETYPE);
639 _decryption_private_key->SetFont (font);
640 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
641 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
642 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
646 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
647 table->Add (_export_decryption_certificate);
648 table->AddSpacer (0);
650 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
651 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
652 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
653 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
654 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
655 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
656 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
657 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
659 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
661 update_certificate_list ();
662 update_signer_private_key ();
663 update_decryption_certificate ();
664 update_decryption_private_key ();
665 update_sensitivity ();
671 void add_certificate ()
673 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
675 if (d->ShowModal() == wxID_OK) {
677 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
678 _signer->certificates().add (c);
679 Config::instance()->set_signer (_signer);
680 update_certificate_list ();
681 } catch (dcp::MiscError& e) {
682 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
688 update_sensitivity ();
691 void remove_certificate ()
693 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
698 _certificates->DeleteItem (i);
699 _signer->certificates().remove (i);
700 Config::instance()->set_signer (_signer);
702 update_sensitivity ();
705 void update_certificate_list ()
707 _certificates->DeleteAllItems ();
708 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
710 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
713 _certificates->InsertItem (item);
714 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
717 _certificates->SetItem (n, 0, _("Root"));
718 } else if (n == (certs.size() - 1)) {
719 _certificates->SetItem (n, 0, _("Leaf"));
721 _certificates->SetItem (n, 0, _("Intermediate"));
728 void update_sensitivity ()
730 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
733 void update_signer_private_key ()
735 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
738 void load_signer_private_key ()
740 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
742 if (d->ShowModal() == wxID_OK) {
744 boost::filesystem::path p (wx_to_std (d->GetPath ()));
745 if (boost::filesystem::file_size (p) > 1024) {
746 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
750 _signer->set_key (dcp::file_to_string (p));
751 Config::instance()->set_signer (_signer);
752 update_signer_private_key ();
753 } catch (dcp::MiscError& e) {
754 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
760 update_sensitivity ();
764 void load_decryption_certificate ()
766 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
768 if (d->ShowModal() == wxID_OK) {
770 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
771 Config::instance()->set_decryption_certificate (c);
772 update_decryption_certificate ();
773 } catch (dcp::MiscError& e) {
774 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
781 void update_decryption_certificate ()
783 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
786 void load_decryption_private_key ()
788 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
790 if (d->ShowModal() == wxID_OK) {
792 boost::filesystem::path p (wx_to_std (d->GetPath ()));
793 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
794 update_decryption_private_key ();
795 } catch (dcp::MiscError& e) {
796 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
803 void update_decryption_private_key ()
805 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
808 void export_decryption_certificate ()
810 wxFileDialog* d = new wxFileDialog (
811 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
812 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
815 if (d->ShowModal () == wxID_OK) {
816 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
818 throw OpenFileError (wx_to_std (d->GetPath ()));
821 string const s = Config::instance()->decryption_certificate().certificate (true);
822 fwrite (s.c_str(), 1, s.length(), f);
829 wxListCtrl* _certificates;
830 wxButton* _add_certificate;
831 wxButton* _remove_certificate;
832 wxStaticText* _signer_private_key;
833 wxButton* _load_signer_private_key;
834 wxStaticText* _decryption_certificate;
835 wxButton* _load_decryption_certificate;
836 wxStaticText* _decryption_private_key;
837 wxButton* _load_decryption_private_key;
838 wxButton* _export_decryption_certificate;
839 shared_ptr<dcp::Signer> _signer;
842 class TMSPage : public wxPreferencesPage, public Page
845 TMSPage (wxSize panel_size, int border)
846 : Page (panel_size, border)
849 wxString GetName () const
855 wxBitmap GetLargeIcon () const
857 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
861 wxWindow* CreateWindow (wxWindow* parent)
863 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
864 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
867 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
868 table->AddGrowableCol (1, 1);
869 s->Add (table, 1, wxALL | wxEXPAND, _border);
871 add_label_to_sizer (table, panel, _("IP address"), true);
872 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
873 table->Add (_tms_ip, 1, wxEXPAND);
875 add_label_to_sizer (table, panel, _("Target path"), true);
876 _tms_path = new wxTextCtrl (panel, wxID_ANY);
877 table->Add (_tms_path, 1, wxEXPAND);
879 add_label_to_sizer (table, panel, _("User name"), true);
880 _tms_user = new wxTextCtrl (panel, wxID_ANY);
881 table->Add (_tms_user, 1, wxEXPAND);
883 add_label_to_sizer (table, panel, _("Password"), true);
884 _tms_password = new wxTextCtrl (panel, wxID_ANY);
885 table->Add (_tms_password, 1, wxEXPAND);
887 Config* config = Config::instance ();
889 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
890 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
891 _tms_path->SetValue (std_to_wx (config->tms_path ()));
892 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
893 _tms_user->SetValue (std_to_wx (config->tms_user ()));
894 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
895 _tms_password->SetValue (std_to_wx (config->tms_password ()));
896 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
902 void tms_ip_changed ()
904 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
907 void tms_path_changed ()
909 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
912 void tms_user_changed ()
914 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
917 void tms_password_changed ()
919 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
923 wxTextCtrl* _tms_path;
924 wxTextCtrl* _tms_user;
925 wxTextCtrl* _tms_password;
928 class KDMEmailPage : public wxPreferencesPage, public Page
932 KDMEmailPage (wxSize panel_size, int border)
933 : Page (panel_size, border)
936 wxString GetName () const
938 return _("KDM Email");
942 wxBitmap GetLargeIcon () const
944 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
948 wxWindow* CreateWindow (wxWindow* parent)
950 /* We have to force both width and height of this one */
952 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
954 wxPanel* panel = new wxPanel (parent);
956 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
959 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
960 table->AddGrowableCol (1, 1);
961 s->Add (table, 1, wxEXPAND | wxALL, _border);
963 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
964 _mail_server = new wxTextCtrl (panel, wxID_ANY);
965 table->Add (_mail_server, 1, wxEXPAND | wxALL);
967 add_label_to_sizer (table, panel, _("Mail user name"), true);
968 _mail_user = new wxTextCtrl (panel, wxID_ANY);
969 table->Add (_mail_user, 1, wxEXPAND | wxALL);
971 add_label_to_sizer (table, panel, _("Mail password"), true);
972 _mail_password = new wxTextCtrl (panel, wxID_ANY);
973 table->Add (_mail_password, 1, wxEXPAND | wxALL);
975 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
976 wxFont font = plain->GetFont();
977 font.SetStyle (wxFONTSTYLE_ITALIC);
978 font.SetPointSize (font.GetPointSize() - 1);
979 plain->SetFont (font);
980 table->AddSpacer (0);
982 add_label_to_sizer (table, panel, _("Subject"), true);
983 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
984 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
986 add_label_to_sizer (table, panel, _("From address"), true);
987 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
988 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
990 add_label_to_sizer (table, panel, _("CC address"), true);
991 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
992 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
994 add_label_to_sizer (table, panel, _("BCC address"), true);
995 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
996 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
998 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
999 s->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1001 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1002 s->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1004 Config* config = Config::instance ();
1005 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1006 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1007 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1008 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1009 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1010 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1011 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1012 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1013 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1014 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1015 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1016 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1017 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1018 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1019 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1020 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1021 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1027 void mail_server_changed ()
1029 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1032 void mail_user_changed ()
1034 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1037 void mail_password_changed ()
1039 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1042 void kdm_subject_changed ()
1044 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1047 void kdm_from_changed ()
1049 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1052 void kdm_cc_changed ()
1054 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1057 void kdm_bcc_changed ()
1059 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1062 void kdm_email_changed ()
1064 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1067 void reset_kdm_email ()
1069 Config::instance()->reset_kdm_email ();
1070 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1073 wxTextCtrl* _mail_server;
1074 wxTextCtrl* _mail_user;
1075 wxTextCtrl* _mail_password;
1076 wxTextCtrl* _kdm_subject;
1077 wxTextCtrl* _kdm_from;
1078 wxTextCtrl* _kdm_cc;
1079 wxTextCtrl* _kdm_bcc;
1080 wxTextCtrl* _kdm_email;
1081 wxButton* _reset_kdm_email;
1084 /** @class AdvancedPage
1085 * @brief Advanced page of the preferences dialog.
1087 class AdvancedPage : public wxStockPreferencesPage, public Page
1091 AdvancedPage (wxSize panel_size, int border)
1092 : wxStockPreferencesPage (Kind_Advanced)
1093 , Page (panel_size, border)
1096 wxWindow* CreateWindow (wxWindow* parent)
1098 wxPanel* panel = new wxPanel (parent);
1100 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
1101 panel->SetSizer (s);
1103 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1104 table->AddGrowableCol (1, 1);
1105 s->Add (table, 1, wxALL | wxEXPAND, _border);
1108 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1109 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1110 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1111 s->Add (_maximum_j2k_bandwidth, 1);
1112 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1116 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1117 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1118 table->AddSpacer (0);
1120 int flags = wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL;
1122 flags |= wxALIGN_RIGHT;
1125 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1126 table->Add (m, 0, flags, 6);
1129 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1130 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1131 t->Add (_log_general, 1, wxEXPAND | wxALL);
1132 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1133 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1134 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1135 t->Add (_log_error, 1, wxEXPAND | wxALL);
1136 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1137 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1138 table->Add (t, 0, wxALL, 6);
1141 Config* config = Config::instance ();
1143 _maximum_j2k_bandwidth->SetRange (1, 500);
1144 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1145 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1146 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1147 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1148 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1149 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1150 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1151 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1152 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1153 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1154 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1155 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1162 void maximum_j2k_bandwidth_changed ()
1164 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1167 void allow_any_dcp_frame_rate_changed ()
1169 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1175 if (_log_general->GetValue ()) {
1176 types |= Log::TYPE_GENERAL;
1178 if (_log_warning->GetValue ()) {
1179 types |= Log::TYPE_WARNING;
1181 if (_log_error->GetValue ()) {
1182 types |= Log::TYPE_ERROR;
1184 if (_log_timing->GetValue ()) {
1185 types |= Log::TYPE_TIMING;
1187 Config::instance()->set_log_types (types);
1190 wxSpinCtrl* _maximum_j2k_bandwidth;
1191 wxCheckBox* _allow_any_dcp_frame_rate;
1192 wxCheckBox* _log_general;
1193 wxCheckBox* _log_warning;
1194 wxCheckBox* _log_error;
1195 wxCheckBox* _log_timing;
1198 wxPreferencesEditor*
1199 create_config_dialog ()
1201 wxPreferencesEditor* e = new wxPreferencesEditor ();
1203 #ifdef DCPOMATIC_OSX
1204 /* Width that we force some of the config panels to be on OSX so that
1205 the containing window doesn't shrink too much when we select those panels.
1206 This is obviously an unpleasant hack.
1208 wxSize ps = wxSize (480, -1);
1209 int const border = 16;
1211 wxSize ps = wxSize (-1, -1);
1212 int const border = 8;
1215 e->AddPage (new GeneralPage (ps, border));
1216 e->AddPage (new DefaultsPage (ps, border));
1217 e->AddPage (new EncodingServersPage (ps, border));
1218 e->AddPage (new ColourConversionsPage (ps, border));
1219 e->AddPage (new KeysPage (ps, border));
1220 e->AddPage (new TMSPage (ps, border));
1221 e->AddPage (new KDMEmailPage (ps, border));
1222 e->AddPage (new AdvancedPage (ps, border));