2 Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 #include "config_dialog.h"
22 #include "static_text.h"
23 #include "check_box.h"
24 #include "nag_dialog.h"
25 #include "dcpomatic_button.h"
26 #include "audio_mapping_view.h"
27 #include <dcp/raw_convert.h>
36 using boost::optional;
37 using boost::shared_ptr;
38 using boost::function;
47 Page::Page (wxSize panel_size, int border)
50 , _panel_size (panel_size)
51 , _window_exists (false)
53 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
57 Page::create_window (wxWindow* parent)
59 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
60 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
64 _window_exists = true;
67 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
73 Page::config_changed_wrapper ()
81 Page::window_destroyed ()
83 _window_exists = false;
87 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
88 : wxStockPreferencesPage (kind)
89 , Page (panel_size, border)
95 StockPage::CreateWindow (wxWindow* parent)
97 return create_window (parent);
100 StandardPage::StandardPage (wxSize panel_size, int border)
101 : Page (panel_size, border)
107 StandardPage::CreateWindow (wxWindow* parent)
109 return create_window (parent);
112 GeneralPage::GeneralPage (wxSize panel_size, int border)
113 : StockPage (Kind_General, panel_size, border)
119 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
121 _set_language = new CheckBox (_panel, _("Set language"));
122 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
123 _language = new wxChoice (_panel, wxID_ANY);
124 vector<pair<string, string> > languages;
125 languages.push_back (make_pair ("Čeština", "cs_CZ"));
126 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
127 languages.push_back (make_pair ("Dansk", "da_DK"));
128 languages.push_back (make_pair ("Deutsch", "de_DE"));
129 languages.push_back (make_pair ("English", "en_GB"));
130 languages.push_back (make_pair ("Español", "es_ES"));
131 languages.push_back (make_pair ("Français", "fr_FR"));
132 languages.push_back (make_pair ("Italiano", "it_IT"));
133 languages.push_back (make_pair ("Nederlands", "nl_NL"));
134 languages.push_back (make_pair ("Русский", "ru_RU"));
135 languages.push_back (make_pair ("Polski", "pl_PL"));
136 languages.push_back (make_pair ("Português europeu", "pt_PT"));
137 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
138 languages.push_back (make_pair ("Svenska", "sv_SE"));
139 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
140 languages.push_back (make_pair ("Türkçe", "tr_TR"));
141 languages.push_back (make_pair ("українська мова", "uk_UA"));
142 checked_set (_language, languages);
143 table->Add (_language, wxGBPosition (r, 1));
146 wxStaticText* restart = add_label_to_sizer (
147 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
149 wxFont font = restart->GetFont();
150 font.SetStyle (wxFONTSTYLE_ITALIC);
151 font.SetPointSize (font.GetPointSize() - 1);
152 restart->SetFont (font);
155 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
156 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
160 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
162 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
163 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
166 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
167 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
170 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
171 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
175 GeneralPage::config_changed ()
177 Config* config = Config::instance ();
179 checked_set (_set_language, static_cast<bool>(config->language()));
181 /* Backwards compatibility of config file */
183 map<string, string> compat_map;
184 compat_map["fr"] = "fr_FR";
185 compat_map["it"] = "it_IT";
186 compat_map["es"] = "es_ES";
187 compat_map["sv"] = "sv_SE";
188 compat_map["de"] = "de_DE";
189 compat_map["nl"] = "nl_NL";
190 compat_map["ru"] = "ru_RU";
191 compat_map["pl"] = "pl_PL";
192 compat_map["da"] = "da_DK";
193 compat_map["pt"] = "pt_PT";
194 compat_map["sk"] = "sk_SK";
195 compat_map["cs"] = "cs_CZ";
196 compat_map["uk"] = "uk_UA";
198 string lang = config->language().get_value_or ("en_GB");
199 if (compat_map.find (lang) != compat_map.end ()) {
200 lang = compat_map[lang];
203 checked_set (_language, lang);
205 checked_set (_check_for_updates, config->check_for_updates ());
206 checked_set (_check_for_test_updates, config->check_for_test_updates ());
208 setup_sensitivity ();
212 GeneralPage::setup_sensitivity ()
214 _language->Enable (_set_language->GetValue ());
215 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
219 GeneralPage::set_language_changed ()
221 setup_sensitivity ();
222 if (_set_language->GetValue ()) {
225 Config::instance()->unset_language ();
230 GeneralPage::language_changed ()
232 int const sel = _language->GetSelection ();
234 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
236 Config::instance()->unset_language ();
241 GeneralPage::check_for_updates_changed ()
243 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
247 GeneralPage::check_for_test_updates_changed ()
249 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
252 CertificateChainEditor::CertificateChainEditor (
256 function<void (shared_ptr<dcp::CertificateChain>)> set,
257 function<shared_ptr<const dcp::CertificateChain> (void)> get,
258 function<bool (void)> nag_alter
260 : wxDialog (parent, wxID_ANY, title)
263 , _nag_alter (nag_alter)
265 wxFont subheading_font (*wxNORMAL_FONT);
266 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
268 _sizer = new wxBoxSizer (wxVERTICAL);
271 wxStaticText* m = new StaticText (this, title);
272 m->SetFont (subheading_font);
273 _sizer->Add (m, 0, wxALL, border);
276 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
277 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
279 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
284 ip.SetText (_("Type"));
286 _certificates->InsertColumn (0, ip);
292 ip.SetText (_("Thumbprint"));
295 wxFont font = ip.GetFont ();
296 font.SetFamily (wxFONTFAMILY_TELETYPE);
299 _certificates->InsertColumn (1, ip);
302 certificates_sizer->Add (_certificates, 1, wxEXPAND);
305 wxSizer* s = new wxBoxSizer (wxVERTICAL);
306 _add_certificate = new Button (this, _("Add..."));
307 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
308 _remove_certificate = new Button (this, _("Remove"));
309 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
310 _export_certificate = new Button (this, _("Export"));
311 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
312 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
315 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
316 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
319 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
320 _private_key = new StaticText (this, wxT(""));
321 wxFont font = _private_key->GetFont ();
322 font.SetFamily (wxFONTFAMILY_TELETYPE);
323 _private_key->SetFont (font);
324 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
325 _import_private_key = new Button (this, _("Import..."));
326 table->Add (_import_private_key, wxGBPosition (r, 2));
327 _export_private_key = new Button (this, _("Export..."));
328 table->Add (_export_private_key, wxGBPosition (r, 3));
331 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
332 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
333 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
334 _export_chain = new Button (this, _("Export chain..."));
335 _button_sizer->Add (_export_chain, 1, wxRIGHT, border);
336 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
339 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
340 font = *wxSMALL_FONT;
341 font.SetWeight (wxFONTWEIGHT_BOLD);
342 _private_key_bad->SetFont (font);
343 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
346 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
347 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
348 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
349 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
350 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
351 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
352 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
353 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
354 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
356 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
358 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
361 SetSizerAndFit (_sizer);
363 update_certificate_list ();
364 update_private_key ();
365 update_sensitivity ();
369 CertificateChainEditor::add_button (wxWindow* button)
371 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
376 CertificateChainEditor::add_certificate ()
378 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
380 if (d->ShowModal() == wxID_OK) {
385 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
386 } catch (boost::filesystem::filesystem_error& e) {
387 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
392 if (!extra.empty ()) {
395 _("This file contains other certificates (or other data) after its first certificate. "
396 "Only the first certificate will be used.")
399 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
401 if (!chain->chain_valid ()) {
404 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
405 "Add certificates in order from root to intermediate to leaf.")
410 update_certificate_list ();
412 } catch (dcp::MiscError& e) {
413 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
419 update_sensitivity ();
423 CertificateChainEditor::remove_certificate ()
426 /* Cancel was clicked */
430 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
435 _certificates->DeleteItem (i);
436 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
440 update_sensitivity ();
441 update_certificate_list ();
445 CertificateChainEditor::export_certificate ()
447 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
452 wxFileDialog* d = new wxFileDialog (
453 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
454 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
457 dcp::CertificateChain::List all = _get()->root_to_leaf ();
458 dcp::CertificateChain::List::iterator j = all.begin ();
459 for (int k = 0; k < i; ++k) {
463 if (d->ShowModal () == wxID_OK) {
464 boost::filesystem::path path (wx_to_std(d->GetPath()));
465 FILE* f = fopen_boost (path, "w");
467 throw OpenFileError (path, errno, OpenFileError::WRITE);
470 string const s = j->certificate (true);
471 checked_fwrite (s.c_str(), s.length(), f, path);
478 CertificateChainEditor::export_chain ()
480 wxFileDialog* d = new wxFileDialog (
481 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
482 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
485 if (d->ShowModal () == wxID_OK) {
486 boost::filesystem::path path (wx_to_std(d->GetPath()));
487 FILE* f = fopen_boost (path, "w");
489 throw OpenFileError (path, errno, OpenFileError::WRITE);
492 string const s = _get()->chain();
493 checked_fwrite (s.c_str(), s.length(), f, path);
501 CertificateChainEditor::update_certificate_list ()
503 _certificates->DeleteAllItems ();
505 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
506 BOOST_FOREACH (dcp::Certificate const & i, certs) {
509 _certificates->InsertItem (item);
510 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
513 _certificates->SetItem (n, 0, _("Root"));
514 } else if (n == (certs.size() - 1)) {
515 _certificates->SetItem (n, 0, _("Leaf"));
517 _certificates->SetItem (n, 0, _("Intermediate"));
523 static wxColour normal = _private_key_bad->GetForegroundColour ();
525 if (_get()->private_key_valid()) {
526 _private_key_bad->Hide ();
527 _private_key_bad->SetForegroundColour (normal);
529 _private_key_bad->Show ();
530 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
535 CertificateChainEditor::remake_certificates ()
537 shared_ptr<const dcp::CertificateChain> chain = _get();
539 string subject_organization_name;
540 string subject_organizational_unit_name;
541 string root_common_name;
542 string intermediate_common_name;
543 string leaf_common_name;
545 dcp::CertificateChain::List all = chain->root_to_leaf ();
547 if (all.size() >= 1) {
549 subject_organization_name = chain->root().subject_organization_name ();
550 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
551 root_common_name = chain->root().subject_common_name ();
554 if (all.size() >= 2) {
556 leaf_common_name = chain->leaf().subject_common_name ();
559 if (all.size() >= 3) {
560 /* Have an intermediate */
561 dcp::CertificateChain::List::iterator i = all.begin ();
563 intermediate_common_name = i->subject_common_name ();
567 /* Cancel was clicked */
571 MakeChainDialog* d = new MakeChainDialog (
573 subject_organization_name,
574 subject_organizational_unit_name,
576 intermediate_common_name,
580 if (d->ShowModal () == wxID_OK) {
582 shared_ptr<dcp::CertificateChain> (
583 new dcp::CertificateChain (
586 d->organisational_unit (),
587 d->root_common_name (),
588 d->intermediate_common_name (),
589 d->leaf_common_name ()
594 update_certificate_list ();
595 update_private_key ();
602 CertificateChainEditor::update_sensitivity ()
604 /* We can only remove the leaf certificate */
605 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
606 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
610 CertificateChainEditor::update_private_key ()
612 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
617 CertificateChainEditor::import_private_key ()
619 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
621 if (d->ShowModal() == wxID_OK) {
623 boost::filesystem::path p (wx_to_std (d->GetPath ()));
624 if (boost::filesystem::file_size (p) > 8192) {
627 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
632 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
633 chain->set_key (dcp::file_to_string (p));
635 update_private_key ();
636 } catch (dcp::MiscError& e) {
637 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
643 update_sensitivity ();
647 CertificateChainEditor::export_private_key ()
649 optional<string> key = _get()->key();
654 wxFileDialog* d = new wxFileDialog (
655 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
656 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
659 if (d->ShowModal () == wxID_OK) {
660 boost::filesystem::path path (wx_to_std(d->GetPath()));
661 FILE* f = fopen_boost (path, "w");
663 throw OpenFileError (path, errno, OpenFileError::WRITE);
666 string const s = _get()->key().get ();
667 checked_fwrite (s.c_str(), s.length(), f, path);
674 KeysPage::GetName () const
682 wxFont subheading_font (*wxNORMAL_FONT);
683 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
685 wxSizer* sizer = _panel->GetSizer();
688 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
689 m->SetFont (subheading_font);
690 sizer->Add (m, 0, wxALL, _border);
693 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
694 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
695 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
696 sizer->Add (export_settings, 0, wxLEFT, _border);
697 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
698 sizer->Add (import_settings, 0, wxLEFT, _border);
699 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
700 sizer->Add (decryption_advanced, 0, wxALL, _border);
702 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
703 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
704 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
705 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
708 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
709 m->SetFont (subheading_font);
710 sizer->Add (m, 0, wxALL, _border);
713 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
714 sizer->Add (signing_advanced, 0, wxLEFT, _border);
715 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
719 KeysPage::decryption_advanced ()
721 CertificateChainEditor* c = new CertificateChainEditor (
722 _panel, _("Decrypting KDMs"), _border,
723 bind (&Config::set_decryption_chain, Config::instance (), _1),
724 bind (&Config::decryption_chain, Config::instance ()),
725 bind (&KeysPage::nag_alter_decryption_chain, this)
732 KeysPage::signing_advanced ()
734 CertificateChainEditor* c = new CertificateChainEditor (
735 _panel, _("Signing DCPs and KDMs"), _border,
736 bind (&Config::set_signer_chain, Config::instance (), _1),
737 bind (&Config::signer_chain, Config::instance ()),
745 KeysPage::export_decryption_chain_and_key ()
747 wxFileDialog* d = new wxFileDialog (
748 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
749 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
752 if (d->ShowModal () == wxID_OK) {
753 boost::filesystem::path path (wx_to_std(d->GetPath()));
754 FILE* f = fopen_boost (path, "w");
756 throw OpenFileError (path, errno, OpenFileError::WRITE);
759 string const chain = Config::instance()->decryption_chain()->chain();
760 checked_fwrite (chain.c_str(), chain.length(), f, path);
761 optional<string> const key = Config::instance()->decryption_chain()->key();
762 DCPOMATIC_ASSERT (key);
763 checked_fwrite (key->c_str(), key->length(), f, path);
771 KeysPage::import_decryption_chain_and_key ()
773 if (NagDialog::maybe_nag (
775 Config::NAG_IMPORT_DECRYPTION_CHAIN,
776 _("If you continue with this operation you will no longer be able to use any DKDMs that you have created with the current certificates and key. Also, any KDMs that have been sent to you for those certificates will become useless. Proceed with caution!"),
782 wxFileDialog* d = new wxFileDialog (
783 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
786 if (d->ShowModal () == wxID_OK) {
787 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
789 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
791 throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE);
797 if (fgets (buffer, 128, f) == 0) {
801 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
802 new_chain->add (dcp::Certificate (current));
804 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
805 new_chain->set_key (current);
811 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
812 Config::instance()->set_decryption_chain (new_chain);
814 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
821 KeysPage::nag_alter_decryption_chain ()
823 return NagDialog::maybe_nag (
825 Config::NAG_ALTER_DECRYPTION_CHAIN,
826 _("If you continue with this operation you will no longer be able to use any DKDMs that you have created. Also, any KDMs that have been sent to you will become useless. Proceed with caution!"),
832 KeysPage::export_decryption_certificate ()
834 wxFileDialog* d = new wxFileDialog (
835 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
836 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
839 if (d->ShowModal () == wxID_OK) {
840 boost::filesystem::path path (wx_to_std(d->GetPath()));
841 FILE* f = fopen_boost (path, "w");
843 throw OpenFileError (path, errno, OpenFileError::WRITE);
846 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
847 checked_fwrite (s.c_str(), s.length(), f, path);
855 SoundPage::GetName () const
863 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
864 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
868 _sound = new CheckBox (_panel, _("Play sound via"));
869 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
870 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
871 _sound_output = new wxChoice (_panel, wxID_ANY);
872 s->Add (_sound_output, 0);
873 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
874 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
875 table->Add (s, wxGBPosition(r, 1));
878 add_label_to_sizer (table, _panel, _("Mapping"), true, wxGBPosition(r, 0));
879 _map = new AudioMappingView (_panel, _("DCP"), _("DCP"), _("Output"), _("output"));
880 _map->SetSize (-1, 600);
881 table->Add (_map, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
884 _reset_to_default = new Button (_panel, _("Reset to default"));
885 table->Add (_reset_to_default, wxGBPosition(r, 1));
888 wxFont font = _sound_output_details->GetFont();
889 font.SetStyle (wxFONTSTYLE_ITALIC);
890 font.SetPointSize (font.GetPointSize() - 1);
891 _sound_output_details->SetFont (font);
893 RtAudio audio (DCPOMATIC_RTAUDIO_API);
894 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
895 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
896 if (dev.probed && dev.outputChannels > 0) {
897 _sound_output->Append (std_to_wx (dev.name));
901 _sound->Bind (wxEVT_CHECKBOX, bind(&SoundPage::sound_changed, this));
902 _sound_output->Bind (wxEVT_CHOICE, bind(&SoundPage::sound_output_changed, this));
903 _map->Changed.connect (bind(&SoundPage::map_changed, this, _1));
904 _reset_to_default->Bind (wxEVT_BUTTON, bind(&SoundPage::reset_to_default, this));
908 SoundPage::reset_to_default ()
910 Config::instance()->set_audio_mapping_to_default ();
914 SoundPage::map_changed (AudioMapping m)
916 Config::instance()->set_audio_mapping (m);
920 SoundPage::sound_changed ()
922 Config::instance()->set_sound (_sound->GetValue ());
926 SoundPage::sound_output_changed ()
928 RtAudio audio (DCPOMATIC_RTAUDIO_API);
929 optional<string> const so = get_sound_output();
930 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
931 Config::instance()->unset_sound_output ();
933 Config::instance()->set_sound_output (*so);
938 SoundPage::config_changed ()
940 Config* config = Config::instance ();
942 checked_set (_sound, config->sound ());
944 optional<string> const current_so = get_sound_output ();
945 optional<string> configured_so;
947 if (config->sound_output()) {
948 configured_so = config->sound_output().get();
950 /* No configured output means we should use the default */
951 RtAudio audio (DCPOMATIC_RTAUDIO_API);
953 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
954 } catch (RtAudioError& e) {
955 /* Probably no audio devices at all */
959 if (configured_so && current_so != configured_so) {
960 /* Update _sound_output with the configured value */
962 while (i < _sound_output->GetCount()) {
963 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
964 _sound_output->SetSelection (i);
971 RtAudio audio (DCPOMATIC_RTAUDIO_API);
973 map<int, wxString> apis;
974 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
975 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
976 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
977 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
978 apis[RtAudio::UNIX_JACK] = _("JACK");
979 apis[RtAudio::LINUX_ALSA] = _("ALSA");
980 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
981 apis[RtAudio::LINUX_OSS] = _("OSS");
982 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
986 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
987 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
988 if (info.name == *configured_so && info.outputChannels > 0) {
989 channels = info.outputChannels;
994 _sound_output_details->SetLabel (
995 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
998 _map->set (Config::instance()->audio_mapping(channels));
1000 vector<string> input;
1001 for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
1002 input.push_back (short_audio_channel_name(i));
1004 _map->set_input_channels (input);
1006 vector<string> output;
1007 for (int i = 0; i < channels; ++i) {
1008 output.push_back (dcp::raw_convert<string>(i));
1010 _map->set_output_channels (output);
1012 setup_sensitivity ();
1016 SoundPage::setup_sensitivity ()
1018 _sound_output->Enable (_sound->GetValue());
1021 /** @return Currently-selected preview sound output in the dialogue */
1023 SoundPage::get_sound_output ()
1025 int const sel = _sound_output->GetSelection ();
1026 if (sel == wxNOT_FOUND) {
1027 return optional<string> ();
1030 return wx_to_std (_sound_output->GetString (sel));