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"
34 using boost::optional;
35 using boost::shared_ptr;
36 using boost::function;
45 Page::Page (wxSize panel_size, int border)
48 , _panel_size (panel_size)
49 , _window_exists (false)
51 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
55 Page::create_window (wxWindow* parent)
57 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
58 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
62 _window_exists = true;
65 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
71 Page::config_changed_wrapper ()
79 Page::window_destroyed ()
81 _window_exists = false;
85 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
86 : wxStockPreferencesPage (kind)
87 , Page (panel_size, border)
93 StockPage::CreateWindow (wxWindow* parent)
95 return create_window (parent);
98 StandardPage::StandardPage (wxSize panel_size, int border)
99 : Page (panel_size, border)
105 StandardPage::CreateWindow (wxWindow* parent)
107 return create_window (parent);
110 GeneralPage::GeneralPage (wxSize panel_size, int border)
111 : StockPage (Kind_General, panel_size, border)
117 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
119 _set_language = new CheckBox (_panel, _("Set language"));
120 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
121 _language = new wxChoice (_panel, wxID_ANY);
122 vector<pair<string, string> > languages;
123 languages.push_back (make_pair ("Čeština", "cs_CZ"));
124 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
125 languages.push_back (make_pair ("Dansk", "da_DK"));
126 languages.push_back (make_pair ("Deutsch", "de_DE"));
127 languages.push_back (make_pair ("English", "en_GB"));
128 languages.push_back (make_pair ("Español", "es_ES"));
129 languages.push_back (make_pair ("Français", "fr_FR"));
130 languages.push_back (make_pair ("Italiano", "it_IT"));
131 languages.push_back (make_pair ("Nederlands", "nl_NL"));
132 languages.push_back (make_pair ("Русский", "ru_RU"));
133 languages.push_back (make_pair ("Polski", "pl_PL"));
134 languages.push_back (make_pair ("Português europeu", "pt_PT"));
135 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
136 languages.push_back (make_pair ("Svenska", "sv_SE"));
137 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
138 languages.push_back (make_pair ("Türkçe", "tr_TR"));
139 languages.push_back (make_pair ("українська мова", "uk_UA"));
140 checked_set (_language, languages);
141 table->Add (_language, wxGBPosition (r, 1));
144 wxStaticText* restart = add_label_to_sizer (
145 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
147 wxFont font = restart->GetFont();
148 font.SetStyle (wxFONTSTYLE_ITALIC);
149 font.SetPointSize (font.GetPointSize() - 1);
150 restart->SetFont (font);
153 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
154 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
158 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
160 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
161 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
164 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
165 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
168 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
169 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
173 GeneralPage::config_changed ()
175 Config* config = Config::instance ();
177 checked_set (_set_language, static_cast<bool>(config->language()));
179 /* Backwards compatibility of config file */
181 map<string, string> compat_map;
182 compat_map["fr"] = "fr_FR";
183 compat_map["it"] = "it_IT";
184 compat_map["es"] = "es_ES";
185 compat_map["sv"] = "sv_SE";
186 compat_map["de"] = "de_DE";
187 compat_map["nl"] = "nl_NL";
188 compat_map["ru"] = "ru_RU";
189 compat_map["pl"] = "pl_PL";
190 compat_map["da"] = "da_DK";
191 compat_map["pt"] = "pt_PT";
192 compat_map["sk"] = "sk_SK";
193 compat_map["cs"] = "cs_CZ";
194 compat_map["uk"] = "uk_UA";
196 string lang = config->language().get_value_or ("en_GB");
197 if (compat_map.find (lang) != compat_map.end ()) {
198 lang = compat_map[lang];
201 checked_set (_language, lang);
203 checked_set (_check_for_updates, config->check_for_updates ());
204 checked_set (_check_for_test_updates, config->check_for_test_updates ());
206 setup_sensitivity ();
210 GeneralPage::setup_sensitivity ()
212 _language->Enable (_set_language->GetValue ());
213 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
217 GeneralPage::set_language_changed ()
219 setup_sensitivity ();
220 if (_set_language->GetValue ()) {
223 Config::instance()->unset_language ();
228 GeneralPage::language_changed ()
230 int const sel = _language->GetSelection ();
232 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
234 Config::instance()->unset_language ();
239 GeneralPage::check_for_updates_changed ()
241 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
245 GeneralPage::check_for_test_updates_changed ()
247 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
250 CertificateChainEditor::CertificateChainEditor (
254 function<void (shared_ptr<dcp::CertificateChain>)> set,
255 function<shared_ptr<const dcp::CertificateChain> (void)> get,
256 function<bool (void)> nag_alter
258 : wxDialog (parent, wxID_ANY, title)
261 , _nag_alter (nag_alter)
263 wxFont subheading_font (*wxNORMAL_FONT);
264 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
266 _sizer = new wxBoxSizer (wxVERTICAL);
269 wxStaticText* m = new StaticText (this, title);
270 m->SetFont (subheading_font);
271 _sizer->Add (m, 0, wxALL, border);
274 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
275 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
277 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
282 ip.SetText (_("Type"));
284 _certificates->InsertColumn (0, ip);
290 ip.SetText (_("Thumbprint"));
293 wxFont font = ip.GetFont ();
294 font.SetFamily (wxFONTFAMILY_TELETYPE);
297 _certificates->InsertColumn (1, ip);
300 certificates_sizer->Add (_certificates, 1, wxEXPAND);
303 wxSizer* s = new wxBoxSizer (wxVERTICAL);
304 _add_certificate = new Button (this, _("Add..."));
305 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
306 _remove_certificate = new Button (this, _("Remove"));
307 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
308 _export_certificate = new Button (this, _("Export"));
309 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
310 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
313 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
314 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
317 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
318 _private_key = new StaticText (this, wxT(""));
319 wxFont font = _private_key->GetFont ();
320 font.SetFamily (wxFONTFAMILY_TELETYPE);
321 _private_key->SetFont (font);
322 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
323 _import_private_key = new Button (this, _("Import..."));
324 table->Add (_import_private_key, wxGBPosition (r, 2));
325 _export_private_key = new Button (this, _("Export..."));
326 table->Add (_export_private_key, wxGBPosition (r, 3));
329 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
330 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
331 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
332 _export_chain = new Button (this, _("Export chain..."));
333 _button_sizer->Add (_export_chain, 1, wxRIGHT, border);
334 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
337 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
338 font = *wxSMALL_FONT;
339 font.SetWeight (wxFONTWEIGHT_BOLD);
340 _private_key_bad->SetFont (font);
341 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
344 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
345 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
346 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
347 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
348 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
349 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
350 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
351 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
352 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
354 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
356 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
359 SetSizerAndFit (_sizer);
361 update_certificate_list ();
362 update_private_key ();
363 update_sensitivity ();
367 CertificateChainEditor::add_button (wxWindow* button)
369 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
374 CertificateChainEditor::add_certificate ()
376 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
378 if (d->ShowModal() == wxID_OK) {
383 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
384 } catch (boost::filesystem::filesystem_error& e) {
385 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
390 if (!extra.empty ()) {
393 _("This file contains other certificates (or other data) after its first certificate. "
394 "Only the first certificate will be used.")
397 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
399 if (!chain->chain_valid ()) {
402 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
403 "Add certificates in order from root to intermediate to leaf.")
408 update_certificate_list ();
410 } catch (dcp::MiscError& e) {
411 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
417 update_sensitivity ();
421 CertificateChainEditor::remove_certificate ()
424 /* Cancel was clicked */
428 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
433 _certificates->DeleteItem (i);
434 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
438 update_sensitivity ();
439 update_certificate_list ();
443 CertificateChainEditor::export_certificate ()
445 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
450 wxFileDialog* d = new wxFileDialog (
451 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
452 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
455 dcp::CertificateChain::List all = _get()->root_to_leaf ();
456 dcp::CertificateChain::List::iterator j = all.begin ();
457 for (int k = 0; k < i; ++k) {
461 if (d->ShowModal () == wxID_OK) {
462 boost::filesystem::path path (wx_to_std(d->GetPath()));
463 FILE* f = fopen_boost (path, "w");
465 throw OpenFileError (path, errno, OpenFileError::WRITE);
468 string const s = j->certificate (true);
469 checked_fwrite (s.c_str(), s.length(), f, path);
476 CertificateChainEditor::export_chain ()
478 wxFileDialog* d = new wxFileDialog (
479 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
480 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
483 if (d->ShowModal () == wxID_OK) {
484 boost::filesystem::path path (wx_to_std(d->GetPath()));
485 FILE* f = fopen_boost (path, "w");
487 throw OpenFileError (path, errno, OpenFileError::WRITE);
490 string const s = _get()->chain();
491 checked_fwrite (s.c_str(), s.length(), f, path);
499 CertificateChainEditor::update_certificate_list ()
501 _certificates->DeleteAllItems ();
503 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
504 BOOST_FOREACH (dcp::Certificate const & i, certs) {
507 _certificates->InsertItem (item);
508 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
511 _certificates->SetItem (n, 0, _("Root"));
512 } else if (n == (certs.size() - 1)) {
513 _certificates->SetItem (n, 0, _("Leaf"));
515 _certificates->SetItem (n, 0, _("Intermediate"));
521 static wxColour normal = _private_key_bad->GetForegroundColour ();
523 if (_get()->private_key_valid()) {
524 _private_key_bad->Hide ();
525 _private_key_bad->SetForegroundColour (normal);
527 _private_key_bad->Show ();
528 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
533 CertificateChainEditor::remake_certificates ()
535 shared_ptr<const dcp::CertificateChain> chain = _get();
537 string subject_organization_name;
538 string subject_organizational_unit_name;
539 string root_common_name;
540 string intermediate_common_name;
541 string leaf_common_name;
543 dcp::CertificateChain::List all = chain->root_to_leaf ();
545 if (all.size() >= 1) {
547 subject_organization_name = chain->root().subject_organization_name ();
548 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
549 root_common_name = chain->root().subject_common_name ();
552 if (all.size() >= 2) {
554 leaf_common_name = chain->leaf().subject_common_name ();
557 if (all.size() >= 3) {
558 /* Have an intermediate */
559 dcp::CertificateChain::List::iterator i = all.begin ();
561 intermediate_common_name = i->subject_common_name ();
565 /* Cancel was clicked */
569 MakeChainDialog* d = new MakeChainDialog (
571 subject_organization_name,
572 subject_organizational_unit_name,
574 intermediate_common_name,
578 if (d->ShowModal () == wxID_OK) {
580 shared_ptr<dcp::CertificateChain> (
581 new dcp::CertificateChain (
584 d->organisational_unit (),
585 d->root_common_name (),
586 d->intermediate_common_name (),
587 d->leaf_common_name ()
592 update_certificate_list ();
593 update_private_key ();
600 CertificateChainEditor::update_sensitivity ()
602 /* We can only remove the leaf certificate */
603 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
604 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
608 CertificateChainEditor::update_private_key ()
610 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
615 CertificateChainEditor::import_private_key ()
617 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
619 if (d->ShowModal() == wxID_OK) {
621 boost::filesystem::path p (wx_to_std (d->GetPath ()));
622 if (boost::filesystem::file_size (p) > 8192) {
625 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
630 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
631 chain->set_key (dcp::file_to_string (p));
633 update_private_key ();
634 } catch (dcp::MiscError& e) {
635 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
641 update_sensitivity ();
645 CertificateChainEditor::export_private_key ()
647 optional<string> key = _get()->key();
652 wxFileDialog* d = new wxFileDialog (
653 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
654 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
657 if (d->ShowModal () == wxID_OK) {
658 boost::filesystem::path path (wx_to_std(d->GetPath()));
659 FILE* f = fopen_boost (path, "w");
661 throw OpenFileError (path, errno, OpenFileError::WRITE);
664 string const s = _get()->key().get ();
665 checked_fwrite (s.c_str(), s.length(), f, path);
672 KeysPage::GetName () const
680 wxFont subheading_font (*wxNORMAL_FONT);
681 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
683 wxSizer* sizer = _panel->GetSizer();
686 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
687 m->SetFont (subheading_font);
688 sizer->Add (m, 0, wxALL, _border);
691 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
692 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
693 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
694 sizer->Add (export_settings, 0, wxLEFT, _border);
695 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
696 sizer->Add (import_settings, 0, wxLEFT, _border);
697 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
698 sizer->Add (decryption_advanced, 0, wxALL, _border);
700 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
701 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
702 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
703 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
706 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
707 m->SetFont (subheading_font);
708 sizer->Add (m, 0, wxALL, _border);
711 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
712 sizer->Add (signing_advanced, 0, wxLEFT, _border);
713 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
717 KeysPage::decryption_advanced ()
719 CertificateChainEditor* c = new CertificateChainEditor (
720 _panel, _("Decrypting KDMs"), _border,
721 bind (&Config::set_decryption_chain, Config::instance (), _1),
722 bind (&Config::decryption_chain, Config::instance ()),
723 bind (&KeysPage::nag_alter_decryption_chain, this)
730 KeysPage::signing_advanced ()
732 CertificateChainEditor* c = new CertificateChainEditor (
733 _panel, _("Signing DCPs and KDMs"), _border,
734 bind (&Config::set_signer_chain, Config::instance (), _1),
735 bind (&Config::signer_chain, Config::instance ()),
743 KeysPage::export_decryption_chain_and_key ()
745 wxFileDialog* d = new wxFileDialog (
746 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
747 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
750 if (d->ShowModal () == wxID_OK) {
751 boost::filesystem::path path (wx_to_std(d->GetPath()));
752 FILE* f = fopen_boost (path, "w");
754 throw OpenFileError (path, errno, OpenFileError::WRITE);
757 string const chain = Config::instance()->decryption_chain()->chain();
758 checked_fwrite (chain.c_str(), chain.length(), f, path);
759 optional<string> const key = Config::instance()->decryption_chain()->key();
760 DCPOMATIC_ASSERT (key);
761 checked_fwrite (key->c_str(), key->length(), f, path);
769 KeysPage::import_decryption_chain_and_key ()
771 if (NagDialog::maybe_nag (
773 Config::NAG_IMPORT_DECRYPTION_CHAIN,
774 _("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!"),
780 wxFileDialog* d = new wxFileDialog (
781 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
784 if (d->ShowModal () == wxID_OK) {
785 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
787 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
789 throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE);
795 if (fgets (buffer, 128, f) == 0) {
799 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
800 new_chain->add (dcp::Certificate (current));
802 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
803 new_chain->set_key (current);
809 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
810 Config::instance()->set_decryption_chain (new_chain);
812 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
819 KeysPage::nag_alter_decryption_chain ()
821 return NagDialog::maybe_nag (
823 Config::NAG_ALTER_DECRYPTION_CHAIN,
824 _("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!"),
830 KeysPage::export_decryption_certificate ()
832 wxFileDialog* d = new wxFileDialog (
833 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
834 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
837 if (d->ShowModal () == wxID_OK) {
838 boost::filesystem::path path (wx_to_std(d->GetPath()));
839 FILE* f = fopen_boost (path, "w");
841 throw OpenFileError (path, errno, OpenFileError::WRITE);
844 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
845 checked_fwrite (s.c_str(), s.length(), f, path);
853 SoundPage::GetName () const
861 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
862 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
866 _sound = new CheckBox (_panel, _("Play sound via"));
867 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
868 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
869 _sound_output = new wxChoice (_panel, wxID_ANY);
870 s->Add (_sound_output, 0);
871 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
872 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
873 table->Add (s, wxGBPosition(r, 1));
876 wxFont font = _sound_output_details->GetFont();
877 font.SetStyle (wxFONTSTYLE_ITALIC);
878 font.SetPointSize (font.GetPointSize() - 1);
879 _sound_output_details->SetFont (font);
881 RtAudio audio (DCPOMATIC_RTAUDIO_API);
882 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
883 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
884 if (dev.probed && dev.outputChannels > 0) {
885 _sound_output->Append (std_to_wx (dev.name));
889 _sound->Bind (wxEVT_CHECKBOX, bind (&SoundPage::sound_changed, this));
890 _sound_output->Bind (wxEVT_CHOICE, bind (&SoundPage::sound_output_changed, this));
894 SoundPage::sound_changed ()
896 Config::instance()->set_sound (_sound->GetValue ());
900 SoundPage::sound_output_changed ()
902 RtAudio audio (DCPOMATIC_RTAUDIO_API);
903 optional<string> const so = get_sound_output();
904 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
905 Config::instance()->unset_sound_output ();
907 Config::instance()->set_sound_output (*so);
912 SoundPage::config_changed ()
914 Config* config = Config::instance ();
916 checked_set (_sound, config->sound ());
918 optional<string> const current_so = get_sound_output ();
919 optional<string> configured_so;
921 if (config->sound_output()) {
922 configured_so = config->sound_output().get();
924 /* No configured output means we should use the default */
925 RtAudio audio (DCPOMATIC_RTAUDIO_API);
927 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
928 } catch (RtAudioError& e) {
929 /* Probably no audio devices at all */
933 if (configured_so && current_so != configured_so) {
934 /* Update _sound_output with the configured value */
936 while (i < _sound_output->GetCount()) {
937 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
938 _sound_output->SetSelection (i);
945 RtAudio audio (DCPOMATIC_RTAUDIO_API);
947 map<int, wxString> apis;
948 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
949 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
950 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
951 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
952 apis[RtAudio::UNIX_JACK] = _("JACK");
953 apis[RtAudio::LINUX_ALSA] = _("ALSA");
954 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
955 apis[RtAudio::LINUX_OSS] = _("OSS");
956 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
960 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
961 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
962 if (info.name == *configured_so && info.outputChannels > 0) {
963 channels = info.outputChannels;
968 _sound_output_details->SetLabel (
969 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
972 setup_sensitivity ();
976 SoundPage::setup_sensitivity ()
978 _sound_output->Enable (_sound->GetValue());
981 /** @return Currently-selected preview sound output in the dialogue */
983 SoundPage::get_sound_output ()
985 int const sel = _sound_output->GetSelection ();
986 if (sel == wxNOT_FOUND) {
987 return optional<string> ();
990 return wx_to_std (_sound_output->GetString (sel));