+ 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<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&) {
+ /* 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) {
+ 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<NamedChannel> 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<NamedChannel> output;
+ for (int i = 0; i < channels; ++i) {
+ output.push_back (NamedChannel(dcp::raw_convert<string>(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<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));
+}
+
+
+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()));