Stop invalid dates causing boost::posix_time to raise exceptions.
[dcpomatic.git] / src / wx / config_dialog.cc
index a6b299bcb6dd6c976baf59bdfda754fdef475d1f..55d387528e9d42c55a0974d99fe094b2428bbf86 100644 (file)
@@ -23,6 +23,8 @@
 #include "check_box.h"
 #include "nag_dialog.h"
 #include "dcpomatic_button.h"
+#include "audio_mapping_view.h"
+#include <dcp/raw_convert.h>
 #include <iostream>
 
 using std::string;
@@ -154,36 +156,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)
 {
@@ -233,62 +205,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<string> const current_so = get_sound_output ();
-       optional<string> 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<int, wxString> 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 +213,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<string>
-GeneralPage::get_sound_output ()
-{
-       int const sel = _sound_output->GetSelection ();
-       if (sel == wxNOT_FOUND) {
-               return optional<string> ();
-       }
-
-       return wx_to_std (_sound_output->GetString (sel));
 }
 
 void
@@ -346,24 +249,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<string> 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,
@@ -419,11 +304,13 @@ CertificateChainEditor::CertificateChainEditor (
        {
                wxSizer* 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);
        }
 
@@ -446,8 +333,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;
 
@@ -579,7 +464,7 @@ CertificateChainEditor::export_certificate ()
                boost::filesystem::path path (wx_to_std(d->GetPath()));
                FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (path, errno, false);
+                       throw OpenFileError (path, errno, OpenFileError::WRITE);
                }
 
                string const s = j->certificate (true);
@@ -601,7 +486,7 @@ CertificateChainEditor::export_chain ()
                boost::filesystem::path path (wx_to_std(d->GetPath()));
                FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (path, errno, false);
+                       throw OpenFileError (path, errno, OpenFileError::WRITE);
                }
 
                string const s = _get()->chain();
@@ -775,7 +660,7 @@ CertificateChainEditor::export_private_key ()
                boost::filesystem::path path (wx_to_std(d->GetPath()));
                FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (path, errno, false);
+                       throw OpenFileError (path, errno, OpenFileError::WRITE);
                }
 
                string const s = _get()->key().get ();
@@ -868,7 +753,7 @@ KeysPage::export_decryption_chain_and_key ()
                boost::filesystem::path path (wx_to_std(d->GetPath()));
                FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (path, errno, false);
+                       throw OpenFileError (path, errno, OpenFileError::WRITE);
                }
 
                string const chain = Config::instance()->decryption_chain()->chain();
@@ -903,7 +788,7 @@ KeysPage::import_decryption_chain_and_key ()
 
                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;
@@ -955,7 +840,7 @@ KeysPage::export_decryption_certificate ()
                boost::filesystem::path path (wx_to_std(d->GetPath()));
                FILE* f = fopen_boost (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);
@@ -965,3 +850,182 @@ KeysPage::export_decryption_certificate ()
 
        d->Destroy ();
 }
+
+wxString
+SoundPage::GetName () const
+{
+       return _("Sound");
+}
+
+void
+SoundPage::setup ()
+{
+       wxGridBagSizer* 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, 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) {
+               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(&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);
+       optional<string> 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);
+       }
+}
+
+void
+SoundPage::config_changed ()
+{
+       Config* config = Config::instance ();
+
+       checked_set (_sound, config->sound ());
+
+       optional<string> const current_so = get_sound_output ();
+       optional<string> 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<int, wxString> 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()])
+               );
+
+       _map->set (Config::instance()->audio_mapping(channels));
+
+       vector<string> input;
+       for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
+               input.push_back (short_audio_channel_name(i));
+       }
+       _map->set_input_channels (input);
+
+       vector<string> output;
+       for (int i = 0; i < channels; ++i) {
+               output.push_back (dcp::raw_convert<string>(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<string>
+SoundPage::get_sound_output ()
+{
+       int const sel = _sound_output->GetSelection ();
+       if (sel == wxNOT_FOUND) {
+               return optional<string> ();
+       }
+
+       return wx_to_std (_sound_output->GetString (sel));
+}