X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Fwx%2Fconfig_dialog.cc;h=e0effec53eafb90a25d2ff2e966f9d92f90406d7;hp=4195aa128880de01777cb28301ec612256b6d6d3;hb=01e979c79f7d0aa20fac1bb24c699e0636168294;hpb=4eaecb1841d3a80a4f06613ad4c0bd44d89285a9 diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index 4195aa128..e0effec53 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -19,17 +19,27 @@ */ #include "config_dialog.h" +#include "static_text.h" +#include "check_box.h" #include "nag_dialog.h" +#include "dcpomatic_button.h" +#include "audio_mapping_view.h" +#include +#include using std::string; using std::vector; using std::pair; using std::make_pair; using std::map; +using std::make_shared; using boost::bind; using boost::optional; -using boost::shared_ptr; -using boost::function; +using std::shared_ptr; +using std::function; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif static bool @@ -47,11 +57,19 @@ Page::Page (wxSize panel_size, int border) _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this)); } + +wxWindow* +Page::CreateWindow (wxWindow* parent) +{ + return create_window (parent); +} + + wxWindow* Page::create_window (wxWindow* parent) { _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size); - wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); + auto s = new wxBoxSizer (wxVERTICAL); _panel->SetSizer (s); setup (); @@ -78,65 +96,49 @@ Page::window_destroyed () } -StockPage::StockPage (Kind kind, wxSize panel_size, int border) - : wxStockPreferencesPage (kind) - , Page (panel_size, border) -{ - -} - -wxWindow* -StockPage::CreateWindow (wxWindow* parent) -{ - return create_window (parent); -} - -StandardPage::StandardPage (wxSize panel_size, int border) +GeneralPage::GeneralPage (wxSize panel_size, int border) : Page (panel_size, border) { } -wxWindow* -StandardPage::CreateWindow (wxWindow* parent) -{ - return create_window (parent); -} -GeneralPage::GeneralPage (wxSize panel_size, int border) - : StockPage (Kind_General, panel_size, border) +wxString +GeneralPage::GetName () const { - + return _("General"); } + void GeneralPage::add_language_controls (wxGridBagSizer* table, int& r) { - _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language")); + _set_language = new CheckBox (_panel, _("Set language")); table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); _language = new wxChoice (_panel, wxID_ANY); - vector > languages; - languages.push_back (make_pair ("Čeština", "cs_CZ")); - languages.push_back (make_pair ("汉语/漢語", "zh_CN")); - languages.push_back (make_pair ("Dansk", "da_DK")); - languages.push_back (make_pair ("Deutsch", "de_DE")); - languages.push_back (make_pair ("English", "en_GB")); - languages.push_back (make_pair ("Español", "es_ES")); - languages.push_back (make_pair ("Français", "fr_FR")); - languages.push_back (make_pair ("Italiano", "it_IT")); - languages.push_back (make_pair ("Nederlands", "nl_NL")); - languages.push_back (make_pair ("Русский", "ru_RU")); - languages.push_back (make_pair ("Polski", "pl_PL")); - languages.push_back (make_pair ("Português europeu", "pt_PT")); - languages.push_back (make_pair ("Português do Brasil", "pt_BR")); - languages.push_back (make_pair ("Svenska", "sv_SE")); - languages.push_back (make_pair ("Slovenský jazyk", "sk_SK")); - languages.push_back (make_pair ("українська мова", "uk_UA")); + vector> languages; + languages.push_back (make_pair("Čeština", "cs_CZ")); + languages.push_back (make_pair("汉语/漢語", "zh_CN")); + languages.push_back (make_pair("Dansk", "da_DK")); + languages.push_back (make_pair("Deutsch", "de_DE")); + languages.push_back (make_pair("English", "en_GB")); + languages.push_back (make_pair("Español", "es_ES")); + languages.push_back (make_pair("Français", "fr_FR")); + languages.push_back (make_pair("Italiano", "it_IT")); + languages.push_back (make_pair("Nederlands", "nl_NL")); + languages.push_back (make_pair("Русский", "ru_RU")); + languages.push_back (make_pair("Polski", "pl_PL")); + languages.push_back (make_pair("Português europeu", "pt_PT")); + languages.push_back (make_pair("Português do Brasil", "pt_BR")); + languages.push_back (make_pair("Svenska", "sv_SE")); + languages.push_back (make_pair("Slovenský jazyk", "sk_SK")); + languages.push_back (make_pair("Türkçe", "tr_TR")); + languages.push_back (make_pair("українська мова", "uk_UA")); checked_set (_language, languages); table->Add (_language, wxGBPosition (r, 1)); ++r; - wxStaticText* restart = add_label_to_sizer ( + auto restart = add_label_to_sizer ( table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2) ); wxFont font = restart->GetFont(); @@ -149,35 +151,14 @@ GeneralPage::add_language_controls (wxGridBagSizer* table, int& r) _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this)); } -void -GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r) -{ - _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via")); - table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - _sound_output = new wxChoice (_panel, wxID_ANY); - table->Add (_sound_output, wxGBPosition (r, 1)); - ++r; - - RtAudio audio (DCPOMATIC_RTAUDIO_API); - for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) { - RtAudio::DeviceInfo dev = audio.getDeviceInfo (i); - if (dev.probed && dev.outputChannels > 0) { - _sound_output->Append (std_to_wx (dev.name)); - } - } - - _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this)); - _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this)); -} - void GeneralPage::add_update_controls (wxGridBagSizer* table, int& r) { - _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup")); + _check_for_updates = new CheckBox (_panel, _("Check for updates on startup")); table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2)); ++r; - _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup")); + _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup")); table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2)); ++r; @@ -188,7 +169,7 @@ GeneralPage::add_update_controls (wxGridBagSizer* table, int& r) void GeneralPage::config_changed () { - Config* config = Config::instance (); + auto config = Config::instance (); checked_set (_set_language, static_cast(config->language())); @@ -209,8 +190,8 @@ GeneralPage::config_changed () compat_map["cs"] = "cs_CZ"; compat_map["uk"] = "uk_UA"; - string lang = config->language().get_value_or ("en_GB"); - if (compat_map.find (lang) != compat_map.end ()) { + auto lang = config->language().get_value_or("en_GB"); + if (compat_map.find(lang) != compat_map.end ()) { lang = compat_map[lang]; } @@ -219,35 +200,6 @@ GeneralPage::config_changed () checked_set (_check_for_updates, config->check_for_updates ()); checked_set (_check_for_test_updates, config->check_for_test_updates ()); - checked_set (_sound, config->sound ()); - - optional const current_so = get_sound_output (); - optional configured_so; - - if (config->sound_output()) { - configured_so = config->sound_output().get(); - } else { - /* No configured output means we should use the default */ - RtAudio audio (DCPOMATIC_RTAUDIO_API); - try { - configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name; - } catch (RtAudioError& e) { - /* Probably no audio devices at all */ - } - } - - if (configured_so && current_so != configured_so) { - /* Update _sound_output with the configured value */ - unsigned int i = 0; - while (i < _sound_output->GetCount()) { - if (_sound_output->GetString(i) == std_to_wx(*configured_so)) { - _sound_output->SetSelection (i); - break; - } - ++i; - } - } - setup_sensitivity (); } @@ -256,19 +208,6 @@ GeneralPage::setup_sensitivity () { _language->Enable (_set_language->GetValue ()); _check_for_test_updates->Enable (_check_for_updates->GetValue ()); - _sound_output->Enable (_sound->GetValue ()); -} - -/** @return Currently-selected preview sound output in the dialogue */ -optional -GeneralPage::get_sound_output () -{ - int const sel = _sound_output->GetSelection (); - if (sel == wxNOT_FOUND) { - return optional (); - } - - return wx_to_std (_sound_output->GetString (sel)); } void @@ -305,50 +244,23 @@ GeneralPage::check_for_test_updates_changed () Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ()); } -void -GeneralPage::sound_changed () -{ - Config::instance()->set_sound (_sound->GetValue ()); -} - -void -GeneralPage::sound_output_changed () -{ - RtAudio audio (DCPOMATIC_RTAUDIO_API); - optional const so = get_sound_output(); - if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) { - Config::instance()->unset_sound_output (); - } else { - Config::instance()->set_sound_output (*so); - } -} - CertificateChainEditor::CertificateChainEditor ( wxWindow* parent, wxString title, int border, function)> set, function (void)> get, - function nag_remake + function nag_alter ) : wxDialog (parent, wxID_ANY, title) , _set (set) , _get (get) - , _nag_remake (nag_remake) + , _nag_alter (nag_alter) { - wxFont subheading_font (*wxNORMAL_FONT); - subheading_font.SetWeight (wxFONTWEIGHT_BOLD); - _sizer = new wxBoxSizer (wxVERTICAL); - { - wxStaticText* m = new wxStaticText (this, wxID_ANY, title); - m->SetFont (subheading_font); - _sizer->Add (m, 0, wxALL, border); - } - - wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL); - _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border); + auto certificates_sizer = new wxBoxSizer (wxHORIZONTAL); + _sizer->Add (certificates_sizer, 0, wxALL, border); _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL); @@ -376,39 +288,41 @@ CertificateChainEditor::CertificateChainEditor ( certificates_sizer->Add (_certificates, 1, wxEXPAND); { - wxSizer* s = new wxBoxSizer (wxVERTICAL); - _add_certificate = new wxButton (this, wxID_ANY, _("Add...")); - s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - _remove_certificate = new wxButton (this, wxID_ANY, _("Remove")); - s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - _export_certificate = new wxButton (this, wxID_ANY, _("Export")); - s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto s = new wxBoxSizer (wxVERTICAL); + _add_certificate = new Button (this, _("Add...")); + s->Add (_add_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP); + _remove_certificate = new Button (this, _("Remove")); + s->Add (_remove_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP); + _export_certificate = new Button (this, _("Export certificate...")); + s->Add (_export_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP); + _export_chain = new Button (this, _("Export chain...")); + s->Add (_export_chain, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP); certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP); } - wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); _sizer->Add (table, 1, wxALL | wxEXPAND, border); int r = 0; add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0)); - _private_key = new wxStaticText (this, wxID_ANY, wxT ("")); + _private_key = new StaticText (this, wxT("")); wxFont font = _private_key->GetFont (); font.SetFamily (wxFONTFAMILY_TELETYPE); _private_key->SetFont (font); table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - _import_private_key = new wxButton (this, wxID_ANY, _("Import...")); + _import_private_key = new Button (this, _("Import...")); table->Add (_import_private_key, wxGBPosition (r, 2)); - _export_private_key = new wxButton (this, wxID_ANY, _("Export...")); + _export_private_key = new Button (this, _("Export...")); table->Add (_export_private_key, wxGBPosition (r, 3)); ++r; _button_sizer = new wxBoxSizer (wxHORIZONTAL); - _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key...")); + _remake_certificates = new Button (this, _("Re-make certificates and key...")); _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border); table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4)); ++r; - _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!")); + _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!")); font = *wxSMALL_FONT; font.SetWeight (wxFONTWEIGHT_BOLD); _private_key_bad->SetFont (font); @@ -421,10 +335,11 @@ CertificateChainEditor::CertificateChainEditor ( _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this)); _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this)); _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this)); + _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this)); _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this)); _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this)); - wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE); + auto buttons = CreateSeparatedButtonSizer (wxCLOSE); if (buttons) { _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); } @@ -446,7 +361,7 @@ CertificateChainEditor::add_button (wxWindow* button) void CertificateChainEditor::add_certificate () { - wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File")); + auto d = new wxFileDialog (this, _("Select Certificate File")); if (d->ShowModal() == wxID_OK) { try { @@ -467,7 +382,7 @@ CertificateChainEditor::add_certificate () "Only the first certificate will be used.") ); } - shared_ptr chain(new dcp::CertificateChain(*_get().get())); + auto chain = make_shared(*_get().get()); chain->add (c); if (!chain->chain_valid ()) { error_dialog ( @@ -493,17 +408,23 @@ CertificateChainEditor::add_certificate () void CertificateChainEditor::remove_certificate () { + if (_nag_alter()) { + /* Cancel was clicked */ + return; + } + int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); if (i == -1) { return; } _certificates->DeleteItem (i); - shared_ptr chain(new dcp::CertificateChain(*_get().get())); + auto chain = make_shared(*_get().get()); chain->remove (i); _set (chain); update_sensitivity (); + update_certificate_list (); } void @@ -514,27 +435,67 @@ CertificateChainEditor::export_certificate () return; } - wxFileDialog* d = new wxFileDialog ( - this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"), + auto all = _get()->root_to_leaf(); + + wxString default_name; + if (i == 0) { + default_name = "root.pem"; + } else if (i == static_cast(all.size() - 1)) { + default_name = "leaf.pem"; + } else { + default_name = "intermediate.pem"; + } + + auto d = new wxFileDialog( + this, _("Select Certificate File"), wxEmptyString, default_name, wxT ("PEM files (*.pem)|*.pem"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); - dcp::CertificateChain::List all = _get()->root_to_leaf (); - dcp::CertificateChain::List::iterator j = all.begin (); + auto j = all.begin (); for (int k = 0; k < i; ++k) { ++j; } if (d->ShowModal () == wxID_OK) { - FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w"); + boost::filesystem::path path (wx_to_std(d->GetPath())); + if (path.extension() != ".pem") { + path += ".pem"; + } + auto f = fopen_boost (path, "w"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } string const s = j->certificate (true); - fwrite (s.c_str(), 1, s.length(), f); + checked_fwrite (s.c_str(), s.length(), f, path); + fclose (f); + } + d->Destroy (); +} + +void +CertificateChainEditor::export_chain () +{ + auto d = new wxFileDialog ( + this, _("Select Chain File"), wxEmptyString, wxT("certificate_chain.pem"), wxT("PEM files (*.pem)|*.pem"), + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if (d->ShowModal () == wxID_OK) { + boost::filesystem::path path (wx_to_std(d->GetPath())); + if (path.extension() != ".pem") { + path += ".pem"; + } + auto f = fopen_boost (path, "w"); + if (!f) { + throw OpenFileError (path, errno, OpenFileError::WRITE); + } + + auto const s = _get()->chain(); + checked_fwrite (s.c_str(), s.length(), f, path); fclose (f); } + d->Destroy (); } @@ -543,8 +504,8 @@ CertificateChainEditor::update_certificate_list () { _certificates->DeleteAllItems (); size_t n = 0; - dcp::CertificateChain::List certs = _get()->root_to_leaf (); - BOOST_FOREACH (dcp::Certificate const & i, certs) { + auto certs = _get()->root_to_leaf(); + for (auto const& i: certs) { wxListItem item; item.SetId (n); _certificates->InsertItem (item); @@ -575,7 +536,7 @@ CertificateChainEditor::update_certificate_list () void CertificateChainEditor::remake_certificates () { - shared_ptr chain = _get(); + auto chain = _get(); string subject_organization_name; string subject_organizational_unit_name; @@ -583,7 +544,7 @@ CertificateChainEditor::remake_certificates () string intermediate_common_name; string leaf_common_name; - dcp::CertificateChain::List all = chain->root_to_leaf (); + auto all = chain->root_to_leaf (); if (all.size() >= 1) { /* Have a root */ @@ -604,12 +565,12 @@ CertificateChainEditor::remake_certificates () intermediate_common_name = i->subject_common_name (); } - if (_nag_remake()) { + if (_nag_alter()) { /* Cancel was clicked */ return; } - MakeChainDialog* d = new MakeChainDialog ( + auto d = new MakeChainDialog ( this, subject_organization_name, subject_organizational_unit_name, @@ -620,15 +581,13 @@ CertificateChainEditor::remake_certificates () if (d->ShowModal () == wxID_OK) { _set ( - shared_ptr ( - new dcp::CertificateChain ( - openssl_path (), - d->organisation (), - d->organisational_unit (), - d->root_common_name (), - d->intermediate_common_name (), - d->leaf_common_name () - ) + make_shared ( + openssl_path (), + d->organisation (), + d->organisational_unit (), + d->root_common_name (), + d->intermediate_common_name (), + d->leaf_common_name () ) ); @@ -643,8 +602,8 @@ void CertificateChainEditor::update_sensitivity () { /* We can only remove the leaf certificate */ - _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1)); - _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1); + _remove_certificate->Enable (_certificates->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1)); + _export_certificate->Enable (_certificates->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1); } void @@ -657,7 +616,7 @@ CertificateChainEditor::update_private_key () void CertificateChainEditor::import_private_key () { - wxFileDialog* d = new wxFileDialog (this, _("Select Key File")); + auto d = new wxFileDialog (this, _("Select Key File")); if (d->ShowModal() == wxID_OK) { try { @@ -670,11 +629,11 @@ CertificateChainEditor::import_private_key () return; } - shared_ptr chain(new dcp::CertificateChain(*_get().get())); + auto chain = make_shared(*_get().get()); chain->set_key (dcp::file_to_string (p)); _set (chain); update_private_key (); - } catch (dcp::MiscError& e) { + } catch (std::exception& e) { error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what())); } } @@ -687,24 +646,28 @@ CertificateChainEditor::import_private_key () void CertificateChainEditor::export_private_key () { - optional key = _get()->key(); + auto key = _get()->key(); if (!key) { return; } - wxFileDialog* d = new wxFileDialog ( - this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"), + auto d = new wxFileDialog ( + this, _("Select Key File"), wxEmptyString, wxT("private_key.pem"), wxT("PEM files (*.pem)|*.pem"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (d->ShowModal () == wxID_OK) { - FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w"); + boost::filesystem::path path (wx_to_std(d->GetPath())); + if (path.extension() != ".pem") { + path += ".pem"; + } + auto f = fopen_boost (path, "w"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const s = _get()->key().get (); - fwrite (s.c_str(), 1, s.length(), f); + auto const s = _get()->key().get (); + checked_fwrite (s.c_str(), s.length(), f, path); fclose (f); } d->Destroy (); @@ -722,50 +685,51 @@ KeysPage::setup () wxFont subheading_font (*wxNORMAL_FONT); subheading_font.SetWeight (wxFONTWEIGHT_BOLD); - wxSizer* sizer = _panel->GetSizer(); + auto sizer = _panel->GetSizer(); { - wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Decrypting KDMs")); + auto m = new StaticText (_panel, _("Decrypting KDMs")); m->SetFont (subheading_font); - sizer->Add (m, 0, wxALL, _border); + sizer->Add (m, 0, wxALL | wxEXPAND, _border); } - wxButton* export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export KDM decryption certificate...")); - sizer->Add (export_decryption_certificate, 0, wxLEFT, _border); - wxButton* export_decryption_chain = new wxButton (_panel, wxID_ANY, _("Export KDM decryption chain...")); - sizer->Add (export_decryption_chain, 0, wxLEFT, _border); - wxButton* export_settings = new wxButton (_panel, wxID_ANY, _("Export all KDM decryption settings...")); - sizer->Add (export_settings, 0, wxLEFT, _border); - wxButton* import_settings = new wxButton (_panel, wxID_ANY, _("Import all KDM decryption settings...")); - sizer->Add (import_settings, 0, wxLEFT, _border); - wxButton* decryption_advanced = new wxButton (_panel, wxID_ANY, _("Advanced...")); - sizer->Add (decryption_advanced, 0, wxALL, _border); + auto buttons = new wxBoxSizer (wxVERTICAL); + + auto export_decryption_certificate = new Button (_panel, _("Export KDM decryption leaf certificate...")); + buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto export_settings = new Button (_panel, _("Export all KDM decryption settings...")); + buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto import_settings = new Button (_panel, _("Import all KDM decryption settings...")); + buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto decryption_advanced = new Button (_panel, _("Advanced...")); + buttons->Add (decryption_advanced, 0); + + sizer->Add (buttons, 0, wxLEFT, _border); export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this)); - export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this)); export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this)); import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this)); decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this)); { - wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Signing DCPs and KDMs")); + auto m = new StaticText (_panel, _("Signing DCPs and KDMs")); m->SetFont (subheading_font); - sizer->Add (m, 0, wxALL, _border); + sizer->Add (m, 0, wxALL | wxEXPAND, _border); } - wxButton* signing_advanced = new wxButton (_panel, wxID_ANY, _("Advanced...")); - sizer->Add (signing_advanced, 0, wxLEFT, _border); + auto signing_advanced = new Button (_panel, _("Advanced...")); + sizer->Add (signing_advanced, 0, wxLEFT | wxBOTTOM, _border); signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this)); } void KeysPage::decryption_advanced () { - CertificateChainEditor* c = new CertificateChainEditor ( + auto c = new CertificateChainEditor ( _panel, _("Decrypting KDMs"), _border, - bind (&Config::set_decryption_chain, Config::instance (), _1), - bind (&Config::decryption_chain, Config::instance ()), - bind (&KeysPage::nag_remake_decryption_chain, this) + bind(&Config::set_decryption_chain, Config::instance(), _1), + bind(&Config::decryption_chain, Config::instance()), + bind(&KeysPage::nag_alter_decryption_chain, this) ); c->ShowModal(); @@ -774,11 +738,11 @@ KeysPage::decryption_advanced () void KeysPage::signing_advanced () { - CertificateChainEditor* c = new CertificateChainEditor ( + auto c = new CertificateChainEditor ( _panel, _("Signing DCPs and KDMs"), _border, - bind (&Config::set_signer_chain, Config::instance (), _1), - bind (&Config::signer_chain, Config::instance ()), - bind (&do_nothing) + bind(&Config::set_signer_chain, Config::instance(), _1), + bind(&Config::signer_chain, Config::instance()), + bind(&do_nothing) ); c->ShowModal(); @@ -787,22 +751,23 @@ KeysPage::signing_advanced () void KeysPage::export_decryption_chain_and_key () { - wxFileDialog* d = new wxFileDialog ( + auto d = new wxFileDialog ( _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (d->ShowModal () == wxID_OK) { - FILE* f = fopen_boost (path_from_file_dialog (d, "dom"), "w"); + boost::filesystem::path path (wx_to_std(d->GetPath())); + auto f = fopen_boost (path, "w"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const chain = Config::instance()->decryption_chain()->chain(); - fwrite (chain.c_str(), 1, chain.length(), f); + auto const chain = Config::instance()->decryption_chain()->chain(); + checked_fwrite (chain.c_str(), chain.length(), f, path); optional const key = Config::instance()->decryption_chain()->key(); DCPOMATIC_ASSERT (key); - fwrite (key->c_str(), 1, key->length(), f); + checked_fwrite (key->c_str(), key->length(), f, path); fclose (f); } d->Destroy (); @@ -812,16 +777,25 @@ KeysPage::export_decryption_chain_and_key () void KeysPage::import_decryption_chain_and_key () { - wxFileDialog* d = new wxFileDialog ( + if (NagDialog::maybe_nag ( + _panel, + Config::NAG_IMPORT_DECRYPTION_CHAIN, + _("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!"), + true + )) { + return; + } + + auto d = new wxFileDialog ( _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom") ); if (d->ShowModal () == wxID_OK) { - shared_ptr new_chain(new dcp::CertificateChain()); + auto new_chain = make_shared(); FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE); } string current; @@ -851,54 +825,324 @@ KeysPage::import_decryption_chain_and_key () } bool -KeysPage::nag_remake_decryption_chain () +KeysPage::nag_alter_decryption_chain () { return NagDialog::maybe_nag ( _panel, - Config::NAG_REMAKE_DECRYPTION_CHAIN, + Config::NAG_ALTER_DECRYPTION_CHAIN, _("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!"), true ); } void -KeysPage::export_decryption_chain () +KeysPage::export_decryption_certificate () { - wxFileDialog* d = new wxFileDialog ( - _panel, _("Select Chain File"), wxEmptyString, _("dcpomatic_kdm_decryption_chain.pem"), wxT ("PEM files (*.pem)|*.pem"), + auto config = Config::instance(); + wxString default_name = "dcpomatic"; + if (!config->dcp_creator().empty()) { + default_name += "_" + std_to_wx(careful_string_filter(config->dcp_creator())); + } + if (!config->dcp_issuer().empty()) { + default_name += "_" + std_to_wx(careful_string_filter(config->dcp_issuer())); + } + default_name += wxT("_kdm_decryption_cert.pem"); + + auto d = new wxFileDialog ( + _panel, _("Select Certificate File"), wxEmptyString, default_name, wxT("PEM files (*.pem)|*.pem"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (d->ShowModal () == wxID_OK) { - FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w"); + boost::filesystem::path path (wx_to_std(d->GetPath())); + if (path.extension() != ".pem") { + path += ".pem"; + } + auto f = fopen_boost (path, "w"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const s = Config::instance()->decryption_chain()->chain(); - fwrite (s.c_str(), 1, s.length(), f); + auto const s = Config::instance()->decryption_chain()->leaf().certificate (true); + checked_fwrite (s.c_str(), s.length(), f, path); fclose (f); } + d->Destroy (); } +wxString +SoundPage::GetName () const +{ + return _("Sound"); +} + void -KeysPage::export_decryption_certificate () +SoundPage::setup () { - wxFileDialog* d = new wxFileDialog ( - _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT - ); + auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border); - if (d->ShowModal () == wxID_OK) { - FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w"); - if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + int r = 0; + + _sound = new CheckBox (_panel, _("Play sound via")); + table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _sound_output = new wxChoice (_panel, wxID_ANY); + s->Add (_sound_output, 0); + _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT("")); + s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP); + table->Add (s, wxGBPosition(r, 1)); + ++r; + + add_label_to_sizer (table, _panel, _("Mapping"), true, wxGBPosition(r, 0)); + _map = new AudioMappingView (_panel, _("DCP"), _("DCP"), _("Output"), _("output")); + _map->SetSize (-1, 600); + table->Add (_map, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + _reset_to_default = new Button (_panel, _("Reset to default")); + table->Add (_reset_to_default, wxGBPosition(r, 1)); + ++r; + + wxFont font = _sound_output_details->GetFont(); + font.SetStyle (wxFONTSTYLE_ITALIC); + font.SetPointSize (font.GetPointSize() - 1); + _sound_output_details->SetFont (font); + + RtAudio audio (DCPOMATIC_RTAUDIO_API); + for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) { + try { + auto dev = audio.getDeviceInfo (i); + if (dev.probed && dev.outputChannels > 0) { + _sound_output->Append (std_to_wx (dev.name)); + } + } catch (RtAudioError&) { + /* Something went wrong so let's just ignore that device */ } + } - string const s = Config::instance()->decryption_chain()->leaf().certificate (true); - fwrite (s.c_str(), 1, s.length(), f); - fclose (f); + _sound->Bind (wxEVT_CHECKBOX, bind(&SoundPage::sound_changed, this)); + _sound_output->Bind (wxEVT_CHOICE, bind(&SoundPage::sound_output_changed, this)); + _map->Changed.connect (bind(&SoundPage::map_changed, this, _1)); + _reset_to_default->Bind (wxEVT_BUTTON, bind(&SoundPage::reset_to_default, this)); +} + +void +SoundPage::reset_to_default () +{ + Config::instance()->set_audio_mapping_to_default (); +} + +void +SoundPage::map_changed (AudioMapping m) +{ + Config::instance()->set_audio_mapping (m); +} + +void +SoundPage::sound_changed () +{ + Config::instance()->set_sound (_sound->GetValue ()); +} + +void +SoundPage::sound_output_changed () +{ + RtAudio audio (DCPOMATIC_RTAUDIO_API); + auto const so = get_sound_output(); + string default_device; + try { + default_device = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name; + } catch (RtAudioError&) { + /* Never mind */ } - d->Destroy (); + if (!so || *so == default_device) { + Config::instance()->unset_sound_output (); + } else { + Config::instance()->set_sound_output (*so); + } +} + +void +SoundPage::config_changed () +{ + auto config = Config::instance (); + + checked_set (_sound, config->sound ()); + + auto const current_so = get_sound_output (); + optional configured_so; + + if (config->sound_output()) { + configured_so = config->sound_output().get(); + } else { + /* No configured output means we should use the default */ + RtAudio audio (DCPOMATIC_RTAUDIO_API); + try { + configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name; + } catch (RtAudioError&) { + /* Probably no audio devices at all */ + } + } + + if (configured_so && current_so != configured_so) { + /* Update _sound_output with the configured value */ + unsigned int i = 0; + while (i < _sound_output->GetCount()) { + if (_sound_output->GetString(i) == std_to_wx(*configured_so)) { + _sound_output->SetSelection (i); + break; + } + ++i; + } + } + + RtAudio audio (DCPOMATIC_RTAUDIO_API); + + map apis; + apis[RtAudio::MACOSX_CORE] = _("CoreAudio"); + apis[RtAudio::WINDOWS_ASIO] = _("ASIO"); + apis[RtAudio::WINDOWS_DS] = _("Direct Sound"); + apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI"); + apis[RtAudio::UNIX_JACK] = _("JACK"); + apis[RtAudio::LINUX_ALSA] = _("ALSA"); + apis[RtAudio::LINUX_PULSE] = _("PulseAudio"); + apis[RtAudio::LINUX_OSS] = _("OSS"); + apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy"); + + int channels = 0; + if (configured_so) { + for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) { + try { + auto info = audio.getDeviceInfo(i); + if (info.name == *configured_so && info.outputChannels > 0) { + channels = info.outputChannels; + } + } catch (RtAudioError&) { + /* Never mind */ + } + } + } + + _sound_output_details->SetLabel ( + wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()]) + ); + + _map->set (Config::instance()->audio_mapping(channels)); + + vector input; + for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) { + input.push_back (NamedChannel(short_audio_channel_name(i), i)); + } + _map->set_input_channels (input); + + vector output; + for (int i = 0; i < channels; ++i) { + output.push_back (NamedChannel(dcp::raw_convert(i), i)); + } + _map->set_output_channels (output); + + setup_sensitivity (); +} + +void +SoundPage::setup_sensitivity () +{ + _sound_output->Enable (_sound->GetValue()); +} + +/** @return Currently-selected preview sound output in the dialogue */ +optional +SoundPage::get_sound_output () +{ + int const sel = _sound_output->GetSelection (); + if (sel == wxNOT_FOUND) { + return optional (); + } + + return wx_to_std (_sound_output->GetString (sel)); +} + + +LocationsPage::LocationsPage (wxSize panel_size, int border) + : Page (panel_size, border) +{ + +} + +wxString +LocationsPage::GetName () const +{ + return _("Locations"); +} + +#ifdef DCPOMATIC_OSX +wxBitmap +LocationsPage::GetLargeIcon () const +{ + return wxBitmap(bitmap_path("locations"), wxBITMAP_TYPE_PNG); +} +#endif + +void +LocationsPage::setup () +{ + int r = 0; + + auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border); + + add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0)); + _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1)); + table->Add (_content_directory, wxGBPosition (r, 1)); + ++r; + + add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0)); + _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1)); + table->Add (_playlist_directory, wxGBPosition (r, 1)); + ++r; + + add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0)); + _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1)); + table->Add (_kdm_directory, wxGBPosition (r, 1)); + ++r; + + _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this)); + _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this)); + _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this)); +} + +void +LocationsPage::config_changed () +{ + auto config = Config::instance (); + + if (config->player_content_directory()) { + checked_set (_content_directory, *config->player_content_directory()); + } + if (config->player_playlist_directory()) { + checked_set (_playlist_directory, *config->player_playlist_directory()); + } + if (config->player_kdm_directory()) { + checked_set (_kdm_directory, *config->player_kdm_directory()); + } +} + +void +LocationsPage::content_directory_changed () +{ + Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath())); +} + +void +LocationsPage::playlist_directory_changed () +{ + Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath())); +} + +void +LocationsPage::kdm_directory_changed () +{ + Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath())); }