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));
58 Page::CreateWindow (wxWindow* parent)
60 return create_window (parent);
65 Page::create_window (wxWindow* parent)
67 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
68 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
72 _window_exists = true;
75 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
81 Page::config_changed_wrapper ()
89 Page::window_destroyed ()
91 _window_exists = false;
95 GeneralPage::GeneralPage (wxSize panel_size, int border)
96 : Page (panel_size, border)
103 GeneralPage::GetName () const
110 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
112 _set_language = new CheckBox (_panel, _("Set language"));
113 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
114 _language = new wxChoice (_panel, wxID_ANY);
115 vector<pair<string, string> > languages;
116 languages.push_back (make_pair ("Čeština", "cs_CZ"));
117 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
118 languages.push_back (make_pair ("Dansk", "da_DK"));
119 languages.push_back (make_pair ("Deutsch", "de_DE"));
120 languages.push_back (make_pair ("English", "en_GB"));
121 languages.push_back (make_pair ("Español", "es_ES"));
122 languages.push_back (make_pair ("Français", "fr_FR"));
123 languages.push_back (make_pair ("Italiano", "it_IT"));
124 languages.push_back (make_pair ("Nederlands", "nl_NL"));
125 languages.push_back (make_pair ("Русский", "ru_RU"));
126 languages.push_back (make_pair ("Polski", "pl_PL"));
127 languages.push_back (make_pair ("Português europeu", "pt_PT"));
128 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
129 languages.push_back (make_pair ("Svenska", "sv_SE"));
130 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
131 languages.push_back (make_pair ("Türkçe", "tr_TR"));
132 languages.push_back (make_pair ("українська мова", "uk_UA"));
133 checked_set (_language, languages);
134 table->Add (_language, wxGBPosition (r, 1));
137 wxStaticText* restart = add_label_to_sizer (
138 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
140 wxFont font = restart->GetFont();
141 font.SetStyle (wxFONTSTYLE_ITALIC);
142 font.SetPointSize (font.GetPointSize() - 1);
143 restart->SetFont (font);
146 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
147 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
151 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
153 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
154 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
157 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
158 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
161 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
162 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
166 GeneralPage::config_changed ()
168 Config* config = Config::instance ();
170 checked_set (_set_language, static_cast<bool>(config->language()));
172 /* Backwards compatibility of config file */
174 map<string, string> compat_map;
175 compat_map["fr"] = "fr_FR";
176 compat_map["it"] = "it_IT";
177 compat_map["es"] = "es_ES";
178 compat_map["sv"] = "sv_SE";
179 compat_map["de"] = "de_DE";
180 compat_map["nl"] = "nl_NL";
181 compat_map["ru"] = "ru_RU";
182 compat_map["pl"] = "pl_PL";
183 compat_map["da"] = "da_DK";
184 compat_map["pt"] = "pt_PT";
185 compat_map["sk"] = "sk_SK";
186 compat_map["cs"] = "cs_CZ";
187 compat_map["uk"] = "uk_UA";
189 string lang = config->language().get_value_or ("en_GB");
190 if (compat_map.find (lang) != compat_map.end ()) {
191 lang = compat_map[lang];
194 checked_set (_language, lang);
196 checked_set (_check_for_updates, config->check_for_updates ());
197 checked_set (_check_for_test_updates, config->check_for_test_updates ());
199 setup_sensitivity ();
203 GeneralPage::setup_sensitivity ()
205 _language->Enable (_set_language->GetValue ());
206 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
210 GeneralPage::set_language_changed ()
212 setup_sensitivity ();
213 if (_set_language->GetValue ()) {
216 Config::instance()->unset_language ();
221 GeneralPage::language_changed ()
223 int const sel = _language->GetSelection ();
225 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
227 Config::instance()->unset_language ();
232 GeneralPage::check_for_updates_changed ()
234 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
238 GeneralPage::check_for_test_updates_changed ()
240 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
243 CertificateChainEditor::CertificateChainEditor (
247 function<void (shared_ptr<dcp::CertificateChain>)> set,
248 function<shared_ptr<const dcp::CertificateChain> (void)> get,
249 function<bool (void)> nag_alter
251 : wxDialog (parent, wxID_ANY, title)
254 , _nag_alter (nag_alter)
256 _sizer = new wxBoxSizer (wxVERTICAL);
258 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
259 _sizer->Add (certificates_sizer, 0, wxALL, border);
261 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
266 ip.SetText (_("Type"));
268 _certificates->InsertColumn (0, ip);
274 ip.SetText (_("Thumbprint"));
277 wxFont font = ip.GetFont ();
278 font.SetFamily (wxFONTFAMILY_TELETYPE);
281 _certificates->InsertColumn (1, ip);
284 certificates_sizer->Add (_certificates, 1, wxEXPAND);
287 wxSizer* s = new wxBoxSizer (wxVERTICAL);
288 _add_certificate = new Button (this, _("Add..."));
289 s->Add (_add_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
290 _remove_certificate = new Button (this, _("Remove"));
291 s->Add (_remove_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
292 _export_certificate = new Button (this, _("Export certificate..."));
293 s->Add (_export_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
294 _export_chain = new Button (this, _("Export chain..."));
295 s->Add (_export_chain, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
296 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
299 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
300 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
303 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
304 _private_key = new StaticText (this, wxT(""));
305 wxFont font = _private_key->GetFont ();
306 font.SetFamily (wxFONTFAMILY_TELETYPE);
307 _private_key->SetFont (font);
308 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
309 _import_private_key = new Button (this, _("Import..."));
310 table->Add (_import_private_key, wxGBPosition (r, 2));
311 _export_private_key = new Button (this, _("Export..."));
312 table->Add (_export_private_key, wxGBPosition (r, 3));
315 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
316 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
317 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
318 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
321 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
322 font = *wxSMALL_FONT;
323 font.SetWeight (wxFONTWEIGHT_BOLD);
324 _private_key_bad->SetFont (font);
325 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
328 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
329 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
330 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
331 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
332 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
333 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
334 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
335 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
336 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
338 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
340 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
343 SetSizerAndFit (_sizer);
345 update_certificate_list ();
346 update_private_key ();
347 update_sensitivity ();
351 CertificateChainEditor::add_button (wxWindow* button)
353 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
358 CertificateChainEditor::add_certificate ()
360 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
362 if (d->ShowModal() == wxID_OK) {
367 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
368 } catch (boost::filesystem::filesystem_error& e) {
369 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
374 if (!extra.empty ()) {
377 _("This file contains other certificates (or other data) after its first certificate. "
378 "Only the first certificate will be used.")
381 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
383 if (!chain->chain_valid ()) {
386 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
387 "Add certificates in order from root to intermediate to leaf.")
392 update_certificate_list ();
394 } catch (dcp::MiscError& e) {
395 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
401 update_sensitivity ();
405 CertificateChainEditor::remove_certificate ()
408 /* Cancel was clicked */
412 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
417 _certificates->DeleteItem (i);
418 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
422 update_sensitivity ();
423 update_certificate_list ();
427 CertificateChainEditor::export_certificate ()
429 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
434 wxFileDialog* d = new wxFileDialog (
435 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
436 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
439 dcp::CertificateChain::List all = _get()->root_to_leaf ();
440 dcp::CertificateChain::List::iterator j = all.begin ();
441 for (int k = 0; k < i; ++k) {
445 if (d->ShowModal () == wxID_OK) {
446 boost::filesystem::path path (wx_to_std(d->GetPath()));
447 FILE* f = fopen_boost (path, "w");
449 throw OpenFileError (path, errno, OpenFileError::WRITE);
452 string const s = j->certificate (true);
453 checked_fwrite (s.c_str(), s.length(), f, path);
460 CertificateChainEditor::export_chain ()
462 wxFileDialog* d = new wxFileDialog (
463 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
464 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
467 if (d->ShowModal () == wxID_OK) {
468 boost::filesystem::path path (wx_to_std(d->GetPath()));
469 FILE* f = fopen_boost (path, "w");
471 throw OpenFileError (path, errno, OpenFileError::WRITE);
474 string const s = _get()->chain();
475 checked_fwrite (s.c_str(), s.length(), f, path);
483 CertificateChainEditor::update_certificate_list ()
485 _certificates->DeleteAllItems ();
487 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
488 BOOST_FOREACH (dcp::Certificate const & i, certs) {
491 _certificates->InsertItem (item);
492 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
495 _certificates->SetItem (n, 0, _("Root"));
496 } else if (n == (certs.size() - 1)) {
497 _certificates->SetItem (n, 0, _("Leaf"));
499 _certificates->SetItem (n, 0, _("Intermediate"));
505 static wxColour normal = _private_key_bad->GetForegroundColour ();
507 if (_get()->private_key_valid()) {
508 _private_key_bad->Hide ();
509 _private_key_bad->SetForegroundColour (normal);
511 _private_key_bad->Show ();
512 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
517 CertificateChainEditor::remake_certificates ()
519 shared_ptr<const dcp::CertificateChain> chain = _get();
521 string subject_organization_name;
522 string subject_organizational_unit_name;
523 string root_common_name;
524 string intermediate_common_name;
525 string leaf_common_name;
527 dcp::CertificateChain::List all = chain->root_to_leaf ();
529 if (all.size() >= 1) {
531 subject_organization_name = chain->root().subject_organization_name ();
532 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
533 root_common_name = chain->root().subject_common_name ();
536 if (all.size() >= 2) {
538 leaf_common_name = chain->leaf().subject_common_name ();
541 if (all.size() >= 3) {
542 /* Have an intermediate */
543 dcp::CertificateChain::List::iterator i = all.begin ();
545 intermediate_common_name = i->subject_common_name ();
549 /* Cancel was clicked */
553 MakeChainDialog* d = new MakeChainDialog (
555 subject_organization_name,
556 subject_organizational_unit_name,
558 intermediate_common_name,
562 if (d->ShowModal () == wxID_OK) {
564 shared_ptr<dcp::CertificateChain> (
565 new dcp::CertificateChain (
568 d->organisational_unit (),
569 d->root_common_name (),
570 d->intermediate_common_name (),
571 d->leaf_common_name ()
576 update_certificate_list ();
577 update_private_key ();
584 CertificateChainEditor::update_sensitivity ()
586 /* We can only remove the leaf certificate */
587 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
588 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
592 CertificateChainEditor::update_private_key ()
594 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
599 CertificateChainEditor::import_private_key ()
601 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
603 if (d->ShowModal() == wxID_OK) {
605 boost::filesystem::path p (wx_to_std (d->GetPath ()));
606 if (boost::filesystem::file_size (p) > 8192) {
609 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
614 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
615 chain->set_key (dcp::file_to_string (p));
617 update_private_key ();
618 } catch (dcp::MiscError& e) {
619 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
625 update_sensitivity ();
629 CertificateChainEditor::export_private_key ()
631 optional<string> key = _get()->key();
636 wxFileDialog* d = new wxFileDialog (
637 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
638 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
641 if (d->ShowModal () == wxID_OK) {
642 boost::filesystem::path path (wx_to_std(d->GetPath()));
643 FILE* f = fopen_boost (path, "w");
645 throw OpenFileError (path, errno, OpenFileError::WRITE);
648 string const s = _get()->key().get ();
649 checked_fwrite (s.c_str(), s.length(), f, path);
656 KeysPage::GetName () const
664 wxFont subheading_font (*wxNORMAL_FONT);
665 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
667 wxSizer* sizer = _panel->GetSizer();
670 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
671 m->SetFont (subheading_font);
672 sizer->Add (m, 0, wxALL, _border);
675 wxSizer* buttons = new wxBoxSizer (wxVERTICAL);
677 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
678 buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
679 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
680 buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
681 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
682 buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
683 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
684 buttons->Add (decryption_advanced, 0);
686 sizer->Add (buttons, 0, wxLEFT, _border);
688 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
689 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
690 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
691 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
694 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
695 m->SetFont (subheading_font);
696 sizer->Add (m, 0, wxALL, _border);
699 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
700 sizer->Add (signing_advanced, 0, wxLEFT | wxBOTTOM, _border);
701 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
705 KeysPage::decryption_advanced ()
707 CertificateChainEditor* c = new CertificateChainEditor (
708 _panel, _("Decrypting KDMs"), _border,
709 bind (&Config::set_decryption_chain, Config::instance (), _1),
710 bind (&Config::decryption_chain, Config::instance ()),
711 bind (&KeysPage::nag_alter_decryption_chain, this)
718 KeysPage::signing_advanced ()
720 CertificateChainEditor* c = new CertificateChainEditor (
721 _panel, _("Signing DCPs and KDMs"), _border,
722 bind (&Config::set_signer_chain, Config::instance (), _1),
723 bind (&Config::signer_chain, Config::instance ()),
731 KeysPage::export_decryption_chain_and_key ()
733 wxFileDialog* d = new wxFileDialog (
734 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
735 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
738 if (d->ShowModal () == wxID_OK) {
739 boost::filesystem::path path (wx_to_std(d->GetPath()));
740 FILE* f = fopen_boost (path, "w");
742 throw OpenFileError (path, errno, OpenFileError::WRITE);
745 string const chain = Config::instance()->decryption_chain()->chain();
746 checked_fwrite (chain.c_str(), chain.length(), f, path);
747 optional<string> const key = Config::instance()->decryption_chain()->key();
748 DCPOMATIC_ASSERT (key);
749 checked_fwrite (key->c_str(), key->length(), f, path);
757 KeysPage::import_decryption_chain_and_key ()
759 if (NagDialog::maybe_nag (
761 Config::NAG_IMPORT_DECRYPTION_CHAIN,
762 _("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!"),
768 wxFileDialog* d = new wxFileDialog (
769 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
772 if (d->ShowModal () == wxID_OK) {
773 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
775 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
777 throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE);
783 if (fgets (buffer, 128, f) == 0) {
787 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
788 new_chain->add (dcp::Certificate (current));
790 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
791 new_chain->set_key (current);
797 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
798 Config::instance()->set_decryption_chain (new_chain);
800 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
807 KeysPage::nag_alter_decryption_chain ()
809 return NagDialog::maybe_nag (
811 Config::NAG_ALTER_DECRYPTION_CHAIN,
812 _("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!"),
818 KeysPage::export_decryption_certificate ()
820 wxFileDialog* d = new wxFileDialog (
821 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
822 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
825 if (d->ShowModal () == wxID_OK) {
826 boost::filesystem::path path (wx_to_std(d->GetPath()));
827 FILE* f = fopen_boost (path, "w");
829 throw OpenFileError (path, errno, OpenFileError::WRITE);
832 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
833 checked_fwrite (s.c_str(), s.length(), f, path);
841 SoundPage::GetName () const
849 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
850 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
854 _sound = new CheckBox (_panel, _("Play sound via"));
855 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
856 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
857 _sound_output = new wxChoice (_panel, wxID_ANY);
858 s->Add (_sound_output, 0);
859 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
860 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
861 table->Add (s, wxGBPosition(r, 1));
864 add_label_to_sizer (table, _panel, _("Mapping"), true, wxGBPosition(r, 0));
865 _map = new AudioMappingView (_panel, _("DCP"), _("DCP"), _("Output"), _("output"));
866 _map->SetSize (-1, 600);
867 table->Add (_map, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
870 _reset_to_default = new Button (_panel, _("Reset to default"));
871 table->Add (_reset_to_default, wxGBPosition(r, 1));
874 wxFont font = _sound_output_details->GetFont();
875 font.SetStyle (wxFONTSTYLE_ITALIC);
876 font.SetPointSize (font.GetPointSize() - 1);
877 _sound_output_details->SetFont (font);
879 RtAudio audio (DCPOMATIC_RTAUDIO_API);
880 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
881 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
882 if (dev.probed && dev.outputChannels > 0) {
883 _sound_output->Append (std_to_wx (dev.name));
887 _sound->Bind (wxEVT_CHECKBOX, bind(&SoundPage::sound_changed, this));
888 _sound_output->Bind (wxEVT_CHOICE, bind(&SoundPage::sound_output_changed, this));
889 _map->Changed.connect (bind(&SoundPage::map_changed, this, _1));
890 _reset_to_default->Bind (wxEVT_BUTTON, bind(&SoundPage::reset_to_default, this));
894 SoundPage::reset_to_default ()
896 Config::instance()->set_audio_mapping_to_default ();
900 SoundPage::map_changed (AudioMapping m)
902 Config::instance()->set_audio_mapping (m);
906 SoundPage::sound_changed ()
908 Config::instance()->set_sound (_sound->GetValue ());
912 SoundPage::sound_output_changed ()
914 RtAudio audio (DCPOMATIC_RTAUDIO_API);
915 optional<string> const so = get_sound_output();
916 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
917 Config::instance()->unset_sound_output ();
919 Config::instance()->set_sound_output (*so);
924 SoundPage::config_changed ()
926 Config* config = Config::instance ();
928 checked_set (_sound, config->sound ());
930 optional<string> const current_so = get_sound_output ();
931 optional<string> configured_so;
933 if (config->sound_output()) {
934 configured_so = config->sound_output().get();
936 /* No configured output means we should use the default */
937 RtAudio audio (DCPOMATIC_RTAUDIO_API);
939 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
940 } catch (RtAudioError& e) {
941 /* Probably no audio devices at all */
945 if (configured_so && current_so != configured_so) {
946 /* Update _sound_output with the configured value */
948 while (i < _sound_output->GetCount()) {
949 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
950 _sound_output->SetSelection (i);
957 RtAudio audio (DCPOMATIC_RTAUDIO_API);
959 map<int, wxString> apis;
960 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
961 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
962 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
963 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
964 apis[RtAudio::UNIX_JACK] = _("JACK");
965 apis[RtAudio::LINUX_ALSA] = _("ALSA");
966 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
967 apis[RtAudio::LINUX_OSS] = _("OSS");
968 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
972 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
973 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
974 if (info.name == *configured_so && info.outputChannels > 0) {
975 channels = info.outputChannels;
980 _sound_output_details->SetLabel (
981 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
984 _map->set (Config::instance()->audio_mapping(channels));
986 vector<string> input;
987 for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
988 input.push_back (short_audio_channel_name(i));
990 _map->set_input_channels (input);
992 vector<string> output;
993 for (int i = 0; i < channels; ++i) {
994 output.push_back (dcp::raw_convert<string>(i));
996 _map->set_output_channels (output);
998 setup_sensitivity ();
1002 SoundPage::setup_sensitivity ()
1004 _sound_output->Enable (_sound->GetValue());
1007 /** @return Currently-selected preview sound output in the dialogue */
1009 SoundPage::get_sound_output ()
1011 int const sel = _sound_output->GetSelection ();
1012 if (sel == wxNOT_FOUND) {
1013 return optional<string> ();
1016 return wx_to_std (_sound_output->GetString (sel));
1020 LocationsPage::LocationsPage (wxSize panel_size, int border)
1021 : Page (panel_size, border)
1027 LocationsPage::GetName () const
1029 return _("Locations");
1032 #ifdef DCPOMATIC_OSX
1034 LocationsPage::GetLargeIcon () const
1036 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
1041 LocationsPage::setup ()
1046 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1047 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1049 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
1050 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1051 table->Add (_content_directory, wxGBPosition (r, 1));
1054 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
1055 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1056 table->Add (_playlist_directory, wxGBPosition (r, 1));
1059 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
1060 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1061 table->Add (_kdm_directory, wxGBPosition (r, 1));
1064 #ifdef DCPOMATIC_VARIANT_SWAROOP
1065 add_label_to_sizer (table, _panel, _("Background image"), true, wxGBPosition (r, 0));
1066 _background_image = new FilePickerCtrl (_panel, _("Select image file"), "*.png;*.jpg;*.jpeg;*.tif;*.tiff", true, false);
1067 table->Add (_background_image, wxGBPosition (r, 1));
1071 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
1072 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
1073 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
1074 #ifdef DCPOMATIC_VARIANT_SWAROOP
1075 _background_image->Bind (wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::background_image_changed, this));
1080 LocationsPage::config_changed ()
1082 Config* config = Config::instance ();
1084 if (config->player_content_directory()) {
1085 checked_set (_content_directory, *config->player_content_directory());
1087 if (config->player_playlist_directory()) {
1088 checked_set (_playlist_directory, *config->player_playlist_directory());
1090 if (config->player_kdm_directory()) {
1091 checked_set (_kdm_directory, *config->player_kdm_directory());
1093 #ifdef DCPOMATIC_VARIANT_SWAROOP
1094 if (config->player_background_image()) {
1095 checked_set (_background_image, *config->player_background_image());
1101 LocationsPage::content_directory_changed ()
1103 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
1107 LocationsPage::playlist_directory_changed ()
1109 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
1113 LocationsPage::kdm_directory_changed ()
1115 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));
1118 #ifdef DCPOMATIC_VARIANT_SWAROOP
1120 LocationsPage::background_image_changed ()
1122 boost::filesystem::path const f = wx_to_std(_background_image->GetPath());
1123 if (!boost::filesystem::is_regular_file(f) || !wxImage::CanRead(std_to_wx(f.string()))) {
1124 error_dialog (0, _("Could not load image file."));
1125 if (Config::instance()->player_background_image()) {
1126 checked_set (_background_image, *Config::instance()->player_background_image());
1131 Config::instance()->set_player_background_image(f);