X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Fwx%2Fconfig_dialog.cc;h=ddf565180697185a6076ceab11899c12ab949565;hp=b5490f05a663a1b30feb64443df7a57dac2008fc;hb=439b5d7a315daf2422cb6c995110d628a91d9389;hpb=b8cd038b2f5e6d521b3593c4f6024bf1cd287670 diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index b5490f05a..ddf565180 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington + Copyright (C) 2012-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,22 +18,31 @@ */ -#include "config_dialog.h" -#include "static_text.h" + +#include "audio_mapping_view.h" #include "check_box.h" -#include "nag_dialog.h" +#include "config_dialog.h" #include "dcpomatic_button.h" -#include +#include "nag_dialog.h" +#include "static_text.h" +#include +#include -using std::string; -using std::vector; -using std::pair; + +using std::function; using std::make_pair; +using std::make_shared; using std::map; +using std::pair; +using std::shared_ptr; +using std::string; +using std::vector; using boost::bind; using boost::optional; -using boost::shared_ptr; -using boost::function; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + static bool @@ -51,11 +60,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 (); @@ -82,66 +99,50 @@ 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 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 ("Türkçe", "tr_TR")); - 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("Slovenščina", "sl_SI")); + 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(); @@ -154,36 +155,6 @@ 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 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; - - 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) { - 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) { @@ -202,7 +173,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())); @@ -223,8 +194,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]; } @@ -233,62 +204,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; - } - } - - 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) { - RtAudio::DeviceInfo info = audio.getDeviceInfo(i); - if (info.name == *configured_so && info.outputChannels > 0) { - channels = info.outputChannels; - } - } - } - - _sound_output_details->SetLabel ( - wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()]) - ); - setup_sensitivity (); } @@ -297,19 +212,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 @@ -346,24 +248,6 @@ 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, @@ -377,19 +261,10 @@ CertificateChainEditor::CertificateChainEditor ( , _get (get) , _nag_alter (nag_alter) { - wxFont subheading_font (*wxNORMAL_FONT); - subheading_font.SetWeight (wxFONTWEIGHT_BOLD); - _sizer = new wxBoxSizer (wxVERTICAL); - { - wxStaticText* m = new StaticText (this, 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); @@ -417,17 +292,19 @@ CertificateChainEditor::CertificateChainEditor ( certificates_sizer->Add (_certificates, 1, wxEXPAND); { - wxSizer* s = new wxBoxSizer (wxVERTICAL); + auto s = new wxBoxSizer (wxVERTICAL); _add_certificate = new Button (this, _("Add...")); - s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + s->Add (_add_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP); _remove_certificate = new Button (this, _("Remove")); - s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - _export_certificate = new Button (this, _("Export")); - s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + 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; @@ -446,8 +323,6 @@ CertificateChainEditor::CertificateChainEditor ( _button_sizer = new wxBoxSizer (wxHORIZONTAL); _remake_certificates = new Button (this, _("Re-make certificates and key...")); _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border); - _export_chain = new Button (this, _("Export chain...")); - _button_sizer->Add (_export_chain, 1, wxRIGHT, border); table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4)); ++r; @@ -468,7 +343,7 @@ CertificateChainEditor::CertificateChainEditor ( _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()); } @@ -490,7 +365,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 { @@ -511,7 +386,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 ( @@ -548,11 +423,12 @@ CertificateChainEditor::remove_certificate () } _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 @@ -563,27 +439,39 @@ 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) { boost::filesystem::path path (wx_to_std(d->GetPath())); - FILE* f = fopen_boost (path, "w"); + if (path.extension() != ".pem") { + path += ".pem"; + } + dcp::File f(path, "w"); if (!f) { - throw OpenFileError (path, errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } string const s = j->certificate (true); - checked_fwrite (s.c_str(), s.length(), f, path); - fclose (f); + f.checked_write(s.c_str(), s.length()); } d->Destroy (); } @@ -591,21 +479,23 @@ CertificateChainEditor::export_certificate () void CertificateChainEditor::export_chain () { - wxFileDialog* d = new wxFileDialog ( - this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"), + 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())); - FILE* f = fopen_boost (path, "w"); + if (path.extension() != ".pem") { + path += ".pem"; + } + dcp::File f(path, "w"); if (!f) { - throw OpenFileError (path, errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const s = _get()->chain(); - checked_fwrite (s.c_str(), s.length(), f, path); - fclose (f); + auto const s = _get()->chain(); + f.checked_write (s.c_str(), s.length()); } d->Destroy (); @@ -616,8 +506,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); @@ -648,63 +538,15 @@ CertificateChainEditor::update_certificate_list () void CertificateChainEditor::remake_certificates () { - shared_ptr chain = _get(); - - string subject_organization_name; - string subject_organizational_unit_name; - string root_common_name; - string intermediate_common_name; - string leaf_common_name; - - dcp::CertificateChain::List all = chain->root_to_leaf (); - - if (all.size() >= 1) { - /* Have a root */ - subject_organization_name = chain->root().subject_organization_name (); - subject_organizational_unit_name = chain->root().subject_organizational_unit_name (); - root_common_name = chain->root().subject_common_name (); - } - - if (all.size() >= 2) { - /* Have a leaf */ - leaf_common_name = chain->leaf().subject_common_name (); - } - - if (all.size() >= 3) { - /* Have an intermediate */ - dcp::CertificateChain::List::iterator i = all.begin (); - ++i; - intermediate_common_name = i->subject_common_name (); - } - if (_nag_alter()) { /* Cancel was clicked */ return; } - MakeChainDialog* d = new MakeChainDialog ( - this, - subject_organization_name, - subject_organizational_unit_name, - root_common_name, - intermediate_common_name, - leaf_common_name - ); + auto d = new MakeChainDialog (this, _get()); 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 () - ) - ) - ); - + _set (d->get()); update_certificate_list (); update_private_key (); } @@ -716,8 +558,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 @@ -730,7 +572,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 { @@ -743,11 +585,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())); } } @@ -760,26 +602,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) { boost::filesystem::path path (wx_to_std(d->GetPath())); - FILE* f = fopen_boost (path, "w"); + if (path.extension() != ".pem") { + path += ".pem"; + } + dcp::File f(path, "w"); if (!f) { - throw OpenFileError (path, errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const s = _get()->key().get (); - checked_fwrite (s.c_str(), s.length(), f, path); - fclose (f); + auto const s = _get()->key().get (); + f.checked_write(s.c_str(), s.length()); } d->Destroy (); } @@ -796,22 +640,26 @@ KeysPage::setup () wxFont subheading_font (*wxNORMAL_FONT); subheading_font.SetWeight (wxFONTWEIGHT_BOLD); - wxSizer* sizer = _panel->GetSizer(); + auto sizer = _panel->GetSizer(); { - wxStaticText* m = new StaticText (_panel, _("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 Button (_panel, _("Export KDM decryption certificate...")); - sizer->Add (export_decryption_certificate, 0, wxLEFT, _border); - wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings...")); - sizer->Add (export_settings, 0, wxLEFT, _border); - wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings...")); - sizer->Add (import_settings, 0, wxLEFT, _border); - wxButton* decryption_advanced = new Button (_panel, _("Advanced...")); - sizer->Add (decryption_advanced, 0, wxALL, _border); + auto kdm_buttons = new wxBoxSizer (wxVERTICAL); + + auto export_decryption_certificate = new Button (_panel, _("Export KDM decryption leaf certificate...")); + kdm_buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto export_settings = new Button (_panel, _("Export all KDM decryption settings...")); + kdm_buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto import_settings = new Button (_panel, _("Import all KDM decryption settings...")); + kdm_buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto decryption_advanced = new Button (_panel, _("Advanced...")); + kdm_buttons->Add (decryption_advanced, 0); + + sizer->Add (kdm_buttons, 0, wxLEFT, _border); export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this)); export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this)); @@ -819,24 +667,44 @@ KeysPage::setup () decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this)); { - wxStaticText* m = new StaticText (_panel, _("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 Button (_panel, _("Advanced...")); - sizer->Add (signing_advanced, 0, wxLEFT, _border); + auto signing_buttons = new wxBoxSizer (wxVERTICAL); + + auto signing_advanced = new Button (_panel, _("Advanced...")); + signing_buttons->Add (signing_advanced, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + auto remake_signing = new Button (_panel, _("Re-make certificates and key...")); + signing_buttons->Add (remake_signing, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + + sizer->Add (signing_buttons, 0, wxLEFT, _border); + signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this)); + remake_signing->Bind (wxEVT_BUTTON, bind(&KeysPage::remake_signing, this)); } + +void +KeysPage::remake_signing () +{ + auto d = new MakeChainDialog (_panel, Config::instance()->signer_chain()); + + if (d->ShowModal () == wxID_OK) { + Config::instance()->set_signer_chain(d->get()); + } +} + + 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_alter_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(); @@ -845,11 +713,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(); @@ -858,24 +726,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) { boost::filesystem::path path (wx_to_std(d->GetPath())); - FILE* f = fopen_boost (path, "w"); + dcp::File f(path, "w"); if (!f) { - throw OpenFileError (path, errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const chain = Config::instance()->decryption_chain()->chain(); - checked_fwrite (chain.c_str(), chain.length(), f, path); - optional const key = Config::instance()->decryption_chain()->key(); + auto const chain = Config::instance()->decryption_chain()->chain(); + f.checked_write (chain.c_str(), chain.length()); + auto const key = Config::instance()->decryption_chain()->key(); DCPOMATIC_ASSERT (key); - checked_fwrite (key->c_str(), key->length(), f, path); - fclose (f); + f.checked_write(key->c_str(), key->length()); } d->Destroy (); @@ -893,22 +760,22 @@ KeysPage::import_decryption_chain_and_key () return; } - wxFileDialog* d = new wxFileDialog ( + 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"); + dcp::File f(wx_to_std(d->GetPath()), "r"); if (!f) { - throw OpenFileError (wx_to_std (d->GetPath ()), errno, false); + throw OpenFileError (f.path(), errno, OpenFileError::WRITE); } string current; - while (!feof (f)) { + while (!f.eof()) { char buffer[128]; - if (fgets (buffer, 128, f) == 0) { + if (f.gets(buffer, 128) == 0) { break; } current += buffer; @@ -920,7 +787,6 @@ KeysPage::import_decryption_chain_and_key () current = ""; } } - fclose (f); if (new_chain->chain_valid() && new_chain->private_key_valid()) { Config::instance()->set_decryption_chain (new_chain); @@ -945,22 +811,310 @@ KeysPage::nag_alter_decryption_chain () void KeysPage::export_decryption_certificate () { - wxFileDialog* d = new wxFileDialog ( - _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.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) { boost::filesystem::path path (wx_to_std(d->GetPath())); - FILE* f = fopen_boost (path, "w"); + if (path.extension() != ".pem") { + path += ".pem"; + } + dcp::File f(path, "w"); if (!f) { - throw OpenFileError (path, errno, false); + throw OpenFileError (path, errno, OpenFileError::WRITE); } - string const s = Config::instance()->decryption_chain()->leaf().certificate (true); - checked_fwrite (s.c_str(), s.length(), f, path); - fclose (f); + auto const s = Config::instance()->decryption_chain()->leaf().certificate (true); + f.checked_write(s.c_str(), s.length()); } d->Destroy (); } + +wxString +SoundPage::GetName () const +{ + return _("Sound"); +} + +void +SoundPage::setup () +{ + auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border); + + 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, 400); + 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 */ + } + } + + _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 */ + } + 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.png"), 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())); +}