Change bitmap_path to take a full name with extension.
[dcpomatic.git] / src / wx / full_config_dialog.cc
index ab3dc08003e93ce4fc46f162f4b7557dca39706d..bb804cae876c837951e387db26de934118c5a9be 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 /** @file src/full_config_dialog.cc
  *  @brief A dialogue to edit all DCP-o-matic configuration.
  */
 
-#include "full_config_dialog.h"
-#include "wx_util.h"
-#include "editable_list.h"
-#include "filter_dialog.h"
+
+#include "check_box.h"
+#include "config_dialog.h"
+#include "config_move_dialog.h"
+#include "dcpomatic_button.h"
 #include "dir_picker_ctrl.h"
+#include "editable_list.h"
+#include "email_dialog.h"
 #include "file_picker_ctrl.h"
-#include "isdcf_metadata_dialog.h"
-#include "server_dialog.h"
+#include "filter_dialog.h"
+#include "full_config_dialog.h"
+#include "kdm_choice.h"
 #include "make_chain_dialog.h"
-#include "email_dialog.h"
-#include "name_format_editor.h"
 #include "nag_dialog.h"
-#include "config_move_dialog.h"
-#include "config_dialog.h"
-#include "static_text.h"
-#include "check_box.h"
-#include "dcpomatic_button.h"
+#include "name_format_editor.h"
 #include "password_entry.h"
+#include "send_test_email_dialog.h"
+#include "server_dialog.h"
+#include "static_text.h"
+#include "wx_util.h"
 #include "lib/config.h"
-#include "lib/ratio.h"
-#include "lib/filter.h"
+#include "lib/cross.h"
 #include "lib/dcp_content_type.h"
+#include "lib/emailer.h"
+#include "lib/exceptions.h"
+#include "lib/filter.h"
 #include "lib/log.h"
+#include "lib/ratio.h"
 #include "lib/util.h"
-#include "lib/cross.h"
-#include "lib/exceptions.h"
-#include <dcp/locale_convert.h>
-#include <dcp/exceptions.h>
 #include <dcp/certificate_chain.h>
-#include <wx/stdpaths.h>
+#include <dcp/exceptions.h>
+#include <dcp/locale_convert.h>
+#include <dcp/warnings.h>
+LIBDCP_DISABLE_WARNINGS
+#include <wx/filepicker.h>
 #include <wx/preferences.h>
 #include <wx/spinctrl.h>
-#include <wx/filepicker.h>
+#include <wx/stdpaths.h>
+LIBDCP_ENABLE_WARNINGS
 #include <RtAudio.h>
 #include <boost/filesystem.hpp>
-#include <boost/foreach.hpp>
 #include <iostream>
 
-using std::vector;
-using std::string;
-using std::list;
+
 using std::cout;
-using std::pair;
+using std::function;
+using std::list;
 using std::make_pair;
 using std::map;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
 using boost::bind;
-using boost::shared_ptr;
-using boost::function;
 using boost::optional;
+#if BOOST_VERSION >= 106100
+using namespace boost::placeholders;
+#endif
 using dcp::locale_convert;
 
+
 class FullGeneralPage : public GeneralPage
 {
 public:
@@ -81,19 +92,14 @@ public:
        {}
 
 private:
-       void setup ()
+       void setup () override
        {
-               wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                int r = 0;
                add_language_controls (table, r);
 
-               add_label_to_sizer (table, _panel, _("Interface complexity"), true, wxGBPosition(r, 0));
-               _interface_complexity = new wxChoice (_panel, wxID_ANY);
-               table->Add (_interface_complexity, wxGBPosition (r, 1));
-               ++r;
-
                add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
                _master_encoding_threads = new wxSpinCtrl (_panel);
                table->Add (_master_encoding_threads, wxGBPosition (r, 1));
@@ -105,12 +111,12 @@ private:
                ++r;
 
                add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
-               _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, true);
+               _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
                table->Add (_config_file, wxGBPosition (r, 1));
                ++r;
 
                add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
-               _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, true);
+               _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
                table->Add (_cinemas_file, wxGBPosition (r, 1));
                Button* export_cinemas = new Button (_panel, _("Export..."));
                table->Add (export_cinemas, wxGBPosition (r, 2));
@@ -128,26 +134,8 @@ private:
 
                add_update_controls (table, r);
 
-               wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-               bottom_table->AddGrowableCol (1, 1);
-
-               add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
-               _issuer = new wxTextCtrl (_panel, wxID_ANY);
-               bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
-
-               add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
-               _creator = new wxTextCtrl (_panel, wxID_ANY);
-               bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
-
-               table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
-               ++r;
-
-               _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::config_file_changed,   this));
-               _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::cinemas_file_changed,  this));
-
-               _interface_complexity->Append (_("Simple"));
-               _interface_complexity->Append (_("Full"));
-               _interface_complexity->Bind (wxEVT_CHOICE, boost::bind(&FullGeneralPage::interface_complexity_changed, this));
+               _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed,  this));
+               _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
 
                _master_encoding_threads->SetRange (1, 128);
                _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
@@ -159,32 +147,19 @@ private:
                _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
 #endif
                _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
-
-               _issuer->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::issuer_changed, this));
-               _creator->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::creator_changed, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
-               switch (config->interface_complexity()) {
-               case Config::INTERFACE_SIMPLE:
-                       checked_set (_interface_complexity, 0);
-                       break;
-               case Config::INTERFACE_FULL:
-                       checked_set (_interface_complexity, 1);
-                       break;
-               }
                checked_set (_master_encoding_threads, config->master_encoding_threads ());
                checked_set (_server_encoding_threads, config->server_encoding_threads ());
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
                checked_set (_analyse_ebur128, config->analyse_ebur128 ());
 #endif
                checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
-               checked_set (_issuer, config->dcp_issuer ());
-               checked_set (_creator, config->dcp_creator ());
-               checked_set (_config_file, config->config_file());
+               checked_set (_config_file, config->config_read_file());
                checked_set (_cinemas_file, config->cinemas_file());
 
                GeneralPage::config_changed ();
@@ -192,8 +167,8 @@ private:
 
        void export_cinemas_file ()
        {
-               wxFileDialog* d = new wxFileDialog (
-                       _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT ("XML files (*.xml)|*.xml"),
+               auto d = new wxFileDialog (
+                       _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
                        wxFD_SAVE | wxFD_OVERWRITE_PROMPT
                 );
 
@@ -203,58 +178,38 @@ private:
                d->Destroy ();
        }
 
-       void interface_complexity_changed ()
-       {
-               if (_interface_complexity->GetSelection() == 0) {
-                       Config::instance()->set_interface_complexity (Config::INTERFACE_SIMPLE);
-               } else {
-                       Config::instance()->set_interface_complexity (Config::INTERFACE_FULL);
-               }
-       }
-
-
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
        void analyse_ebur128_changed ()
        {
-               Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
+               Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
        }
 #endif
 
        void automatic_audio_analysis_changed ()
        {
-               Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
+               Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
        }
 
        void master_encoding_threads_changed ()
        {
-               Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
+               Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
        }
 
        void server_encoding_threads_changed ()
        {
-               Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
-       }
-
-       void issuer_changed ()
-       {
-               Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
-       }
-
-       void creator_changed ()
-       {
-               Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
+               Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
        }
 
        void config_file_changed ()
        {
-               Config* config = Config::instance();
+               auto config = Config::instance();
                boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
-               if (new_file == config->config_file()) {
+               if (new_file == config->config_read_file()) {
                        return;
                }
                bool copy_and_link = true;
                if (boost::filesystem::exists(new_file)) {
-                       ConfigMoveDialog* d = new ConfigMoveDialog (_panel, new_file);
+                       auto d = new ConfigMoveDialog (_panel, new_file);
                        if (d->ShowModal() == wxID_OK) {
                                copy_and_link = false;
                        }
@@ -263,7 +218,7 @@ private:
 
                if (copy_and_link) {
                        config->write ();
-                       if (new_file != config->config_file()) {
+                       if (new_file != config->config_read_file()) {
                                config->copy_and_link (new_file);
                        }
                } else {
@@ -276,7 +231,6 @@ private:
                Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
        }
 
-       wxChoice* _interface_complexity;
        wxSpinCtrl* _master_encoding_threads;
        wxSpinCtrl* _server_encoding_threads;
        FilePickerCtrl* _config_file;
@@ -285,46 +239,45 @@ private:
        wxCheckBox* _analyse_ebur128;
 #endif
        wxCheckBox* _automatic_audio_analysis;
-       wxTextCtrl* _issuer;
-       wxTextCtrl* _creator;
 };
 
-class DefaultsPage : public StandardPage
+
+class DefaultsPage : public Page
 {
 public:
        DefaultsPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("Defaults");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("defaults.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                {
-                       add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
-                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+                       add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       auto s = new wxBoxSizer (wxHORIZONTAL);
                        _still_length = new wxSpinCtrl (_panel);
                        s->Add (_still_length);
-                       add_label_to_sizer (s, _panel, _("s"), false);
+                       add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
-               add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
+               add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
 #ifdef DCPOMATIC_USE_OWN_PICKER
                _directory = new DirPickerCtrl (_panel);
 #else
@@ -332,68 +285,97 @@ private:
 #endif
                table->Add (_directory, 1, wxEXPAND);
 
-               add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
-               _isdcf_metadata_button = new Button (_panel, _("Edit..."));
-               table->Add (_isdcf_metadata_button);
-
-               add_label_to_sizer (table, _panel, _("Default container"), true);
+               add_label_to_sizer (table, _panel, _("Default container"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _container = new wxChoice (_panel, wxID_ANY);
                table->Add (_container);
 
-               add_label_to_sizer (table, _panel, _("Default content type"), true);
+               add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _dcp_content_type = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_content_type);
 
-               add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
+               add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_audio_channels);
 
                {
-                       add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
-                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+                       add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       auto s = new wxBoxSizer (wxHORIZONTAL);
                        _j2k_bandwidth = new wxSpinCtrl (_panel);
                        s->Add (_j2k_bandwidth);
-                       add_label_to_sizer (s, _panel, _("Mbit/s"), false);
+                       add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
                {
-                       add_label_to_sizer (table, _panel, _("Default audio delay"), true);
-                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+                       add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       auto s = new wxBoxSizer (wxHORIZONTAL);
                        _audio_delay = new wxSpinCtrl (_panel);
                        s->Add (_audio_delay);
-                       add_label_to_sizer (s, _panel, _("ms"), false);
+                       add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
-               add_label_to_sizer (table, _panel, _("Default standard"), true);
+               add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _standard = new wxChoice (_panel, wxID_ANY);
                table->Add (_standard);
 
-               add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
+               table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
+               table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
+
+               table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
+               table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
+
+               table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
+               table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
+
+               table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
+               table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
 #ifdef DCPOMATIC_USE_OWN_PICKER
                _kdm_directory = new DirPickerCtrl (_panel);
 #else
                _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
 #endif
-
                table->Add (_kdm_directory, 1, wxEXPAND);
 
+               add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _kdm_type = new KDMChoice (_panel);
+               table->Add (_kdm_type, 1, wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _kdm_duration = new wxSpinCtrl (_panel);
+               _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
+               auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
+               kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
+               kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
+               table->Add (kdm_duration_sizer, 1, wxEXPAND);
+
+               table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
+
                _still_length->SetRange (1, 3600);
                _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
 
                _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
                _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
+               _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
+               _kdm_duration_unit->Append (_("days"));
+               _kdm_duration_unit->Append (_("weeks"));
+               _kdm_duration_unit->Append (_("months"));
+               _kdm_duration_unit->Append (_("years"));
 
-               _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
+               _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
+               _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
 
-               BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
+               _use_isdcf_name_by_default->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::use_isdcf_name_by_default_changed, this));
+
+               for (auto i: Ratio::containers()) {
                        _container->Append (std_to_wx(i->container_nickname()));
                }
 
                _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
 
-               BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
+               for (auto i: DCPContentType::all()) {
                        _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
                }
 
@@ -411,22 +393,30 @@ private:
                _standard->Append (_("SMPTE"));
                _standard->Append (_("Interop"));
                _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
+
+               for (auto const& i: _enable_metadata) {
+                       i.second->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::metadata_changed, this));
+               }
+
+               for (auto const& i: _metadata) {
+                       i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
+               }
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
-               vector<Ratio const *> containers = Ratio::containers ();
+               auto containers = Ratio::containers ();
                for (size_t i = 0; i < containers.size(); ++i) {
-                       if (containers[i] == config->default_container ()) {
+                       if (containers[i] == config->default_container()) {
                                _container->SetSelection (i);
                        }
                }
 
-               vector<DCPContentType const *> const ct = DCPContentType::all ();
+               auto const ct = DCPContentType::all ();
                for (size_t i = 0; i < ct.size(); ++i) {
-                       if (ct[i] == config->default_dcp_content_type ()) {
+                       if (ct[i] == config->default_dcp_content_type()) {
                                _dcp_content_type->SetSelection (i);
                        }
                }
@@ -434,11 +424,76 @@ private:
                checked_set (_still_length, config->default_still_length ());
                _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
                _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
+               _kdm_type->set (config->default_kdm_type());
+               checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
                checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
                _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
                checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
                checked_set (_audio_delay, config->default_audio_delay ());
                checked_set (_standard, config->default_interop() ? 1 : 0);
+
+               auto metadata = config->default_metadata();
+
+               for (auto const& i: metadata) {
+                       _enable_metadata[i.first]->SetValue(true);
+                       checked_set (_metadata[i.first], i.second);
+               }
+
+               for (auto const& i: _enable_metadata) {
+                       if (metadata.find(i.first) == metadata.end()) {
+                               checked_set (i.second, false);
+                       }
+               }
+
+               for (auto const& i: _metadata) {
+                       if (metadata.find(i.first) == metadata.end()) {
+                               checked_set (i.second, wxT(""));
+                       }
+               }
+
+               checked_set (_kdm_duration, config->default_kdm_duration().duration);
+               switch (config->default_kdm_duration().unit) {
+                       case RoughDuration::Unit::DAYS:
+                               _kdm_duration->SetRange(1, 365);
+                               checked_set (_kdm_duration_unit, 0);
+                               break;
+                       case RoughDuration::Unit::WEEKS:
+                               _kdm_duration->SetRange(1, 52);
+                               checked_set (_kdm_duration_unit, 1);
+                               break;
+                       case RoughDuration::Unit::MONTHS:
+                               _kdm_duration->SetRange(1, 12);
+                               checked_set (_kdm_duration_unit, 2);
+                               break;
+                       case RoughDuration::Unit::YEARS:
+                               _kdm_duration->SetRange(1, 40);
+                               checked_set (_kdm_duration_unit, 3);
+                               break;
+               }
+
+               setup_sensitivity ();
+       }
+
+       void kdm_duration_changed ()
+       {
+               auto config = Config::instance();
+               auto duration = _kdm_duration->GetValue();
+               RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
+               switch (_kdm_duration_unit->GetSelection()) {
+               case 0:
+                       unit = RoughDuration::Unit::DAYS;
+                       break;
+               case 1:
+                       unit = RoughDuration::Unit::WEEKS;
+                       break;
+               case 2:
+                       unit = RoughDuration::Unit::MONTHS;
+                       break;
+               case 3:
+                       unit = RoughDuration::Unit::YEARS;
+                       break;
+               }
+               config->set_default_kdm_duration (RoughDuration(duration, unit));
        }
 
        void j2k_bandwidth_changed ()
@@ -456,7 +511,7 @@ private:
                int const s = _dcp_audio_channels->GetSelection ();
                if (s != wxNOT_FOUND) {
                        Config::instance()->set_default_dcp_audio_channels (
-                               locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
+                               locale_convert<int>(string_client_data(_dcp_audio_channels->GetClientObject(s)))
                                );
                }
        }
@@ -471,12 +526,14 @@ private:
                Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
        }
 
-       void edit_isdcf_metadata_clicked ()
+       void kdm_type_changed ()
        {
-               ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
-               d->ShowModal ();
-               Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
-               d->Destroy ();
+               Config::instance()->set_default_kdm_type(_kdm_type->get());
+       }
+
+       void use_isdcf_name_by_default_changed ()
+       {
+               Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
        }
 
        void still_length_changed ()
@@ -486,13 +543,13 @@ private:
 
        void container_changed ()
        {
-               vector<Ratio const *> ratio = Ratio::containers ();
+               auto ratio = Ratio::containers ();
                Config::instance()->set_default_container (ratio[_container->GetSelection()]);
        }
 
        void dcp_content_type_changed ()
        {
-               vector<DCPContentType const *> ct = DCPContentType::all ();
+               auto ct = DCPContentType::all ();
                Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
        }
 
@@ -501,9 +558,27 @@ private:
                Config::instance()->set_default_interop (_standard->GetSelection() == 1);
        }
 
+       void metadata_changed ()
+       {
+               map<string, string> metadata;
+               for (auto const& i: _enable_metadata) {
+                       if (i.second->GetValue()) {
+                               metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
+                       }
+               }
+               Config::instance()->set_default_metadata (metadata);
+               setup_sensitivity ();
+       }
+
+       void setup_sensitivity ()
+       {
+               for (auto const& i: _enable_metadata) {
+                       _metadata[i.first]->Enable(i.second->GetValue());
+               }
+       }
+
        wxSpinCtrl* _j2k_bandwidth;
        wxSpinCtrl* _audio_delay;
-       wxButton* _isdcf_metadata_button;
        wxSpinCtrl* _still_length;
 #ifdef DCPOMATIC_USE_OWN_PICKER
        DirPickerCtrl* _directory;
@@ -512,53 +587,62 @@ private:
        wxDirPickerCtrl* _directory;
        wxDirPickerCtrl* _kdm_directory;
 #endif
+       KDMChoice* _kdm_type;
+       wxSpinCtrl* _kdm_duration;
+       wxChoice* _kdm_duration_unit;
+       wxCheckBox* _use_isdcf_name_by_default;
        wxChoice* _container;
        wxChoice* _dcp_content_type;
        wxChoice* _dcp_audio_channels;
        wxChoice* _standard;
+       map<string, CheckBox*> _enable_metadata;
+       map<string, wxTextCtrl*> _metadata;
 };
 
-class EncodingServersPage : public StandardPage
+
+class EncodingServersPage : public Page
 {
 public:
        EncodingServersPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("Servers");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("servers.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
                _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
                _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
 
                vector<EditableListColumn> columns;
-               columns.push_back (EditableListColumn(wx_to_std(_("IP address / host name"))));
+               columns.push_back (EditableListColumn(_("IP address / host name")));
                _servers_list = new EditableList<string, ServerDialog> (
                        _panel,
                        columns,
                        boost::bind (&Config::servers, Config::instance()),
                        boost::bind (&Config::set_servers, Config::instance(), _1),
-                       boost::bind (&EncodingServersPage::server_column, this, _1)
+                       boost::bind (&EncodingServersPage::server_column, this, _1),
+                       false,
+                       EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
                        );
 
                _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
 
-               _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
+               _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind(&EncodingServersPage::use_any_servers_changed, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
                checked_set (_use_any_servers, Config::instance()->use_any_servers ());
                _servers_list->refresh ();
@@ -578,27 +662,28 @@ private:
        EditableList<string, ServerDialog>* _servers_list;
 };
 
-class TMSPage : public StandardPage
+
+class TMSPage : public Page
 {
 public:
        TMSPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("TMS");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("tms.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
                _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
                _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
@@ -607,23 +692,23 @@ private:
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
-               add_label_to_sizer (table, _panel, _("Protocol"), true);
+               add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _tms_protocol = new wxChoice (_panel, wxID_ANY);
                table->Add (_tms_protocol, 1, wxEXPAND);
 
-               add_label_to_sizer (table, _panel, _("IP address"), true);
+               add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_tms_ip, 1, wxEXPAND);
 
-               add_label_to_sizer (table, _panel, _("Target path"), true);
+               add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _tms_path = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_tms_path, 1, wxEXPAND);
 
-               add_label_to_sizer (table, _panel, _("User name"), true);
+               add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _tms_user = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_tms_user, 1, wxEXPAND);
 
-               add_label_to_sizer (table, _panel, _("Password"), true);
+               add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _tms_password = new PasswordEntry (_panel);
                table->Add (_tms_password->get_panel(), 1, wxEXPAND);
 
@@ -638,12 +723,12 @@ private:
                _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
                checked_set (_upload, config->upload_after_make_dcp());
-               checked_set (_tms_protocol, config->tms_protocol ());
+               checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
                checked_set (_tms_ip, config->tms_ip ());
                checked_set (_tms_path, config->tms_path ());
                checked_set (_tms_user, config->tms_user ());
@@ -688,121 +773,121 @@ private:
        PasswordEntry* _tms_password;
 };
 
-static string
-column (string s)
-{
-       return s;
-}
 
-class EmailPage : public StandardPage
+class EmailPage : public Page
 {
 public:
        EmailPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("Email");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("email", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("email.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
 
-               add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
+               add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                {
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _server = new wxTextCtrl (_panel, wxID_ANY);
                        s->Add (_server, 1, wxEXPAND | wxALL);
-                       add_label_to_sizer (s, _panel, _("port"), false);
+                       add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        _port = new wxSpinCtrl (_panel, wxID_ANY);
                        _port->SetRange (0, 65535);
                        s->Add (_port);
-                       add_label_to_sizer (s, _panel, _("protocol"), false);
+                       add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        _protocol = new wxChoice (_panel, wxID_ANY);
                        /* Make sure this matches the switches in config_changed and port_changed below */
                        _protocol->Append (_("Auto"));
                        _protocol->Append (_("Plain"));
                        _protocol->Append (_("STARTTLS"));
                        _protocol->Append (_("SSL"));
-                       s->Add (_protocol);
+                       s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
                        table->Add (s, 1, wxEXPAND | wxALL);
                }
 
-               add_label_to_sizer (table, _panel, _("User name"), true);
+               add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _user = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_user, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("Password"), true);
+               add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _password = new PasswordEntry (_panel);
                table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
 
-               _server->Bind (wxEVT_TEXT, boost::bind (&EmailPage::server_changed, this));
-               _port->Bind (wxEVT_SPINCTRL, boost::bind (&EmailPage::port_changed, this));
-               _protocol->Bind (wxEVT_CHOICE, boost::bind (&EmailPage::protocol_changed, this));
-               _user->Bind (wxEVT_TEXT, boost::bind (&EmailPage::user_changed, this));
-               _password->Changed.connect (boost::bind (&EmailPage::password_changed, this));
+               table->AddSpacer (0);
+               _send_test_email = new Button (_panel, _("Send test email..."));
+               table->Add (_send_test_email);
+
+               _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
+               _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
+               _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
+               _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
+               _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
+               _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
-               checked_set (_server, config->mail_server ());
-               checked_set (_port, config->mail_port ());
+               checked_set (_server, config->mail_server());
+               checked_set (_port, config->mail_port());
                switch (config->mail_protocol()) {
-               case EMAIL_PROTOCOL_AUTO:
+               case EmailProtocol::AUTO:
                        checked_set (_protocol, 0);
                        break;
-               case EMAIL_PROTOCOL_PLAIN:
+               case EmailProtocol::PLAIN:
                        checked_set (_protocol, 1);
                        break;
-               case EMAIL_PROTOCOL_STARTTLS:
+               case EmailProtocol::STARTTLS:
                        checked_set (_protocol, 2);
                        break;
-               case EMAIL_PROTOCOL_SSL:
+               case EmailProtocol::SSL:
                        checked_set (_protocol, 3);
                        break;
                }
-               checked_set (_user, config->mail_user ());
+               checked_set (_user, config->mail_user());
                checked_set (_password, config->mail_password());
        }
 
        void server_changed ()
        {
-               Config::instance()->set_mail_server (wx_to_std (_server->GetValue ()));
+               Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
        }
 
        void port_changed ()
        {
-               Config::instance()->set_mail_port (_port->GetValue ());
+               Config::instance()->set_mail_port(_port->GetValue());
        }
 
        void protocol_changed ()
        {
                switch (_protocol->GetSelection()) {
                case 0:
-                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_AUTO);
+                       Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
                        break;
                case 1:
-                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_PLAIN);
+                       Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
                        break;
                case 2:
-                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_STARTTLS);
+                       Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
                        break;
                case 3:
-                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_SSL);
+                       Config::instance()->set_mail_protocol(EmailProtocol::SSL);
                        break;
                }
        }
@@ -817,66 +902,101 @@ private:
                Config::instance()->set_mail_password(_password->get());
        }
 
+       void send_test_email_clicked ()
+       {
+               auto dialog = new SendTestEmailDialog(_panel);
+               auto result = dialog->ShowModal();
+               dialog->Destroy();
+               if (result == wxID_OK) {
+                       Emailer emailer(
+                               wx_to_std(dialog->from()),
+                               { wx_to_std(dialog->to()) },
+                               wx_to_std(_("DCP-o-matic test email")),
+                               wx_to_std(_("This is a test email from DCP-o-matic."))
+                               );
+                       auto config = Config::instance();
+                       try {
+                               emailer.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
+                       } catch (NetworkError& e) {
+                               error_dialog (_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
+                               return;
+                       } catch (std::exception& e) {
+                               error_dialog (_panel, _("Test email sending failed."), std_to_wx(e.what()));
+                               return;
+                       } catch (...) {
+                               error_dialog (_panel, _("Test email sending failed."));
+                               return;
+                       }
+                       message_dialog (_panel, _("Test email sent."));
+               }
+       }
+
        wxTextCtrl* _server;
        wxSpinCtrl* _port;
        wxChoice* _protocol;
        wxTextCtrl* _user;
        PasswordEntry* _password;
+       Button* _send_test_email;
 };
 
-class KDMEmailPage : public StandardPage
+
+class KDMEmailPage : public Page
 {
 public:
 
        KDMEmailPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
                /* We have to force both width and height of this one */
-               : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
 #else
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
 #endif
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("KDM Email");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("kdm_email.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                table->AddGrowableCol (1, 1);
-               _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
+               _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
 
-               add_label_to_sizer (table, _panel, _("Subject"), true);
+               add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _subject = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_subject, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("From address"), true);
+               add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _from = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_from, 1, wxEXPAND | wxALL);
 
                vector<EditableListColumn> columns;
-               columns.push_back (EditableListColumn(wx_to_std(_("Address"))));
-               add_label_to_sizer (table, _panel, _("CC addresses"), true);
+               columns.push_back (EditableListColumn(_("Address")));
+               add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _cc = new EditableList<string, EmailDialog> (
                        _panel,
                        columns,
                        bind (&Config::kdm_cc, Config::instance()),
                        bind (&Config::set_kdm_cc, Config::instance(), _1),
-                       bind (&column, _1)
+                       [] (string s, int) {
+                               return s;
+                       },
+                       true,
+                       EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
                        );
                table->Add (_cc, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("BCC address"), true);
+               add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _bcc = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_bcc, 1, wxEXPAND | wxALL);
 
@@ -895,9 +1015,9 @@ private:
                _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
                checked_set (_subject, config->kdm_subject ());
                checked_set (_from, config->kdm_from ());
@@ -945,36 +1065,37 @@ private:
        wxButton* _reset_email;
 };
 
-class NotificationsPage : public StandardPage
+
+class NotificationsPage : public Page
 {
 public:
        NotificationsPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
                /* We have to force both width and height of this one */
-               : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
 #else
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
 #endif
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("Notifications");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("notifications", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("notifications.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                table->AddGrowableCol (1, 1);
-               _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
+               _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
 
                _enable_message_box = new CheckBox (_panel, _("Message box"));
                table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
@@ -984,31 +1105,35 @@ private:
                table->Add (_enable_email, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
-               add_label_to_sizer (table, _panel, _("Subject"), true);
+               add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _subject = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_subject, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("From address"), true);
+               add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _from = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_from, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("To address"), true);
+               add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _to = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_to, 1, wxEXPAND | wxALL);
 
                vector<EditableListColumn> columns;
-               columns.push_back (EditableListColumn(wx_to_std(_("Address"))));
-               add_label_to_sizer (table, _panel, _("CC addresses"), true);
+               columns.push_back (EditableListColumn(_("Address")));
+               add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _cc = new EditableList<string, EmailDialog> (
                        _panel,
                        columns,
                        bind (&Config::notification_cc, Config::instance()),
                        bind (&Config::set_notification_cc, Config::instance(), _1),
-                       bind (&column, _1)
+                       [] (string s, int) {
+                               return s;
+                       },
+                       true,
+                       EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
                        );
                table->Add (_cc, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("BCC address"), true);
+               add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _bcc = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_bcc, 1, wxEXPAND | wxALL);
 
@@ -1030,10 +1155,10 @@ private:
                _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
                _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
 
-               update_sensitivity ();
+               setup_sensitivity ();
        }
 
-       void update_sensitivity ()
+       void setup_sensitivity ()
        {
                bool const s = _enable_email->GetValue();
                _subject->Enable(s);
@@ -1045,9 +1170,9 @@ private:
                _reset_email->Enable(s);
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
                checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
                checked_set (_enable_email, config->notification(Config::EMAIL));
@@ -1057,50 +1182,50 @@ private:
                checked_set (_bcc, config->notification_bcc ());
                checked_set (_email, Config::instance()->notification_email ());
 
-               update_sensitivity ();
+               setup_sensitivity ();
        }
 
        void notification_subject_changed ()
        {
-               Config::instance()->set_notification_subject (wx_to_std (_subject->GetValue ()));
+               Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
        }
 
        void notification_from_changed ()
        {
-               Config::instance()->set_notification_from (wx_to_std (_from->GetValue ()));
+               Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
        }
 
        void notification_to_changed ()
        {
-               Config::instance()->set_notification_to (wx_to_std (_to->GetValue ()));
+               Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
        }
 
        void notification_bcc_changed ()
        {
-               Config::instance()->set_notification_bcc (wx_to_std (_bcc->GetValue ()));
+               Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
        }
 
        void notification_email_changed ()
        {
-               if (_email->GetValue().IsEmpty ()) {
+               if (_email->GetValue().IsEmpty()) {
                        /* Sometimes we get sent an erroneous notification that the email
                           is empty; I don't know why.
                        */
                        return;
                }
-               Config::instance()->set_notification_email (wx_to_std (_email->GetValue ()));
+               Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
        }
 
        void reset_email ()
        {
-               Config::instance()->reset_notification_email ();
-               checked_set (_email, Config::instance()->notification_email ());
+               Config::instance()->reset_notification_email();
+               checked_set (_email, Config::instance()->notification_email());
        }
 
        void type_changed (wxCheckBox* b, Config::Notification n)
        {
                Config::instance()->set_notification(n, b->GetValue());
-               update_sensitivity ();
+               setup_sensitivity ();
        }
 
        wxCheckBox* _enable_message_box;
@@ -1115,33 +1240,34 @@ private:
        wxButton* _reset_email;
 };
 
-class CoverSheetPage : public StandardPage
+
+class CoverSheetPage : public Page
 {
 public:
 
        CoverSheetPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
                /* We have to force both width and height of this one */
-               : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
 #else
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
 #endif
        {}
 
-       wxString GetName () const
+       wxString GetName () const override
        {
                return _("Cover Sheet");
        }
 
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
+       wxBitmap GetLargeIcon () const override
        {
-               return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap(bitmap_path("cover_sheet.png"), wxBITMAP_TYPE_PNG);
        }
 #endif
 
 private:
-       void setup ()
+       void setup () override
        {
                _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
                _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
@@ -1153,9 +1279,9 @@ private:
                _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               checked_set (_cover_sheet, Config::instance()->cover_sheet ());
+               checked_set (_cover_sheet, Config::instance()->cover_sheet());
        }
 
        void cover_sheet_changed ()
@@ -1166,13 +1292,13 @@ private:
                        */
                        return;
                }
-               Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
+               Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
        }
 
        void reset_cover_sheet ()
        {
-               Config::instance()->reset_cover_sheet ();
-               checked_set (_cover_sheet, Config::instance()->cover_sheet ());
+               Config::instance()->reset_cover_sheet();
+               checked_set (_cover_sheet, Config::instance()->cover_sheet());
        }
 
        wxTextCtrl* _cover_sheet;
@@ -1180,28 +1306,143 @@ private:
 };
 
 
+class IdentifiersPage : public Page
+{
+public:
+       IdentifiersPage (wxSize panel_size, int border)
+               : Page (panel_size, border)
+       {}
+
+       wxString GetName () const override
+       {
+               return _("Identifiers");
+       }
+
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const override
+       {
+               return wxBitmap(bitmap_path("identifiers.png"), wxBITMAP_TYPE_PNG);
+       }
+#endif
+
+private:
+       void setup () override
+       {
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               table->AddGrowableCol (1, 1);
+
+               add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _issuer = new wxTextCtrl (_panel, wxID_ANY);
+               _issuer->SetToolTip (_("This will be written to the DCP's XML files as the <Issuer>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
+               table->Add (_issuer, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _creator = new wxTextCtrl (_panel, wxID_ANY);
+               _creator->SetToolTip (_("This will be written to the DCP's XML files as the <Creator>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
+               table->Add (_creator, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _company_name = new wxTextCtrl (_panel, wxID_ANY);
+               _company_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'company name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_company_name, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _product_name = new wxTextCtrl (_panel, wxID_ANY);
+               _product_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'product name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_product_name, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _product_version = new wxTextCtrl (_panel, wxID_ANY);
+               _product_version->SetToolTip (_("This will be written to the DCP's MXF files as the 'product version'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_product_version, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
+               _j2k_comment->SetToolTip (_("This will be written to the DCP's JPEG2000 data as a comment.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
+
+               _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
+
+               _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
+               _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
+               _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
+               _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
+               _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
+               _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
+       }
+
+       void config_changed () override
+       {
+               auto config = Config::instance ();
+               checked_set (_issuer, config->dcp_issuer ());
+               checked_set (_creator, config->dcp_creator ());
+               checked_set (_company_name, config->dcp_company_name ());
+               checked_set (_product_name, config->dcp_product_name ());
+               checked_set (_product_version, config->dcp_product_version ());
+               checked_set (_j2k_comment, config->dcp_j2k_comment ());
+       }
+
+       void issuer_changed ()
+       {
+               Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
+       }
+
+       void creator_changed ()
+       {
+               Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
+       }
+
+       void company_name_changed ()
+       {
+               Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
+       }
+
+       void product_name_changed ()
+       {
+               Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
+       }
+
+       void product_version_changed ()
+       {
+               Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
+       }
+
+       void j2k_comment_changed ()
+       {
+               Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
+       }
+
+       wxTextCtrl* _issuer;
+       wxTextCtrl* _creator;
+       wxTextCtrl* _company_name;
+       wxTextCtrl* _product_name;
+       wxTextCtrl* _product_version;
+       wxTextCtrl* _j2k_comment;
+};
+
+
 /** @class AdvancedPage
  *  @brief Advanced page of the preferences dialog.
  */
-class AdvancedPage : public StockPage
+class AdvancedPage : public Page
 {
 public:
        AdvancedPage (wxSize panel_size, int border)
-               : StockPage (Kind_Advanced, panel_size, border)
-               , _maximum_j2k_bandwidth (0)
-               , _allow_any_dcp_frame_rate (0)
-               , _allow_any_container (0)
-               , _show_experimental_audio_processors (0)
-               , _only_servers_encode (0)
-               , _log_general (0)
-               , _log_warning (0)
-               , _log_error (0)
-               , _log_timing (0)
-               , _log_debug_threed (0)
-               , _log_debug_encode (0)
-               , _log_debug_email (0)
+               : Page (panel_size, border)
        {}
 
+       wxString GetName () const override
+       {
+               return _("Advanced");
+       }
+
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const override
+       {
+               return wxBitmap(bitmap_path("advanced.png"), wxBITMAP_TYPE_PNG);
+       }
+#endif
+
 private:
        void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
        {
@@ -1214,62 +1455,75 @@ private:
                table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
        }
 
-       void setup ()
+       void setup () override
        {
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                {
-                       add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
+                       add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
                        s->Add (_maximum_j2k_bandwidth, 1);
-                       add_label_to_sizer (s, _panel, _("Mbit/s"), false);
+                       add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
-               add_label_to_sizer (table, _panel, _("Video display mode"), true);
+               add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _video_display_mode = new wxChoice (_panel, wxID_ANY);
                table->Add (_video_display_mode);
 
-               wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
-               wxFont font = restart->GetFont();
+               auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
+               auto font = restart->GetFont();
                font.SetStyle (wxFONTSTYLE_ITALIC);
                font.SetPointSize (font.GetPointSize() - 1);
                restart->SetFont (font);
                table->AddSpacer (0);
 
                _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
-               table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
+               table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
                table->AddSpacer (0);
 
                _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
-               table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
-               table->AddSpacer (0);
-
-               restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
+               table->Add (_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
+               restart = new StaticText (_panel, _("(restart DCP-o-matic to see all ratios)"));
+               table->Add (restart, 1, wxEXPAND | wxALL | wxALIGN_CENTRE_VERTICAL);
                restart->SetFont (font);
+
+               _allow_96khz_audio = new CheckBox (_panel, _("Allow creation of DCPs with 96kHz audio"));
+               table->Add (_allow_96khz_audio, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
                table->AddSpacer (0);
 
+               _use_all_audio_channels = new CheckBox(_panel, _("Allow mapping to all audio channels"));
+               table->Add(_use_all_audio_channels, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
+               table->AddSpacer(0);
+
                _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
-               table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
+               table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
                table->AddSpacer (0);
 
                _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
-               table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
+               table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
                table->AddSpacer (0);
 
                {
-                       add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true);
-                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+                       add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       auto s = new wxBoxSizer (wxHORIZONTAL);
                        _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
                        s->Add (_frames_in_memory_multiplier, 1);
                        table->Add (s, 1);
                }
 
                {
-                       add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
+                       auto format = create_label (_panel, _("DCP metadata filename format"), true);
+#ifdef DCPOMATIC_OSX
+                       auto align = new wxBoxSizer (wxHORIZONTAL);
+                       align->Add (format, 0, wxTOP, 2);
+                       table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
+#else
+                       table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
+#endif
                        dcp::NameFormat::Map titles;
                        titles['t'] = wx_to_std (_("type (cpl/pkl)"));
                        dcp::NameFormat::Map examples;
@@ -1281,7 +1535,14 @@ private:
                }
 
                {
-                       add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
+                       auto format = create_label (_panel, _("DCP asset filename format"), true);
+#ifdef DCPOMATIC_OSX
+                       auto align = new wxBoxSizer (wxHORIZONTAL);
+                       align->Add (format, 0, wxTOP, 2);
+                       table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
+#else
+                       table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
+#endif
                        dcp::NameFormat::Map titles;
                        titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
                        titles['r'] = wx_to_std (_("reel number"));
@@ -1300,7 +1561,7 @@ private:
 
                {
                        add_top_aligned_label_to_sizer (table, _panel, _("Log"));
-                       wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
+                       auto t = new wxFlexGridSizer (2);
                        _log_general = new CheckBox (_panel, _("General"));
                        t->Add (_log_general, 1, wxEXPAND | wxALL);
                        _log_warning = new CheckBox (_panel, _("Warnings"));
@@ -1316,6 +1577,12 @@ private:
                        t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
                        _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
                        t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
+                       _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
+                       t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
+                       _log_debug_player = new CheckBox (_panel, _("Debug: player"));
+                       t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
+                       _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
+                       t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
                        table->Add (t, 0, wxALL, 6);
                }
 
@@ -1328,10 +1595,14 @@ private:
                _maximum_j2k_bandwidth->SetRange (1, 1000);
                _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
                _video_display_mode->Append (_("Simple (safer)"));
+#if wxCHECK_VERSION(3, 1, 0)
                _video_display_mode->Append (_("OpenGL (faster)"));
+#endif
                _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
                _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
                _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
+               _allow_96khz_audio->Bind (wxEVT_CHECKBOX, boost::bind(&AdvancedPage::allow_96khz_audio_changed, this));
+               _use_all_audio_channels->Bind(wxEVT_CHECKBOX, boost::bind(&AdvancedPage::use_all_channels_changed, this));
                _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
                _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
                _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
@@ -1344,14 +1615,17 @@ private:
                _log_debug_threed->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
                _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
                _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_video_view->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_player->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
 #ifdef DCPOMATIC_WINDOWS
                _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
 #endif
        }
 
-       void config_changed ()
+       void config_changed () override
        {
-               Config* config = Config::instance ();
+               auto config = Config::instance ();
 
                checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
                switch (config->video_view_type()) {
@@ -1364,15 +1638,20 @@ private:
                }
                checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
                checked_set (_allow_any_container, config->allow_any_container ());
+               checked_set (_allow_96khz_audio, config->allow_96khz_audio());
+               checked_set (_use_all_audio_channels, config->use_all_audio_channels());
                checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
                checked_set (_only_servers_encode, config->only_servers_encode ());
                checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
                checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
                checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
                checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
-               checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREED);
+               checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
                checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
                checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
+               checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
+               checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
+               checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
                checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
 #ifdef DCPOMATIC_WINDOWS
                checked_set (_win32_console, config->win32_console());
@@ -1381,51 +1660,61 @@ private:
 
        void maximum_j2k_bandwidth_changed ()
        {
-               Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
+               Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
        }
 
        void video_display_mode_changed ()
        {
                if (_video_display_mode->GetSelection() == 0) {
-                       Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
+                       Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
                } else {
-                       Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
+                       Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
                }
        }
 
        void frames_in_memory_multiplier_changed ()
        {
-               Config::instance()->set_frames_in_memory_multiplier (_frames_in_memory_multiplier->GetValue());
+               Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
        }
 
        void allow_any_dcp_frame_rate_changed ()
        {
-               Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
+               Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
        }
 
        void allow_any_container_changed ()
        {
-               Config::instance()->set_allow_any_container (_allow_any_container->GetValue ());
+               Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
+       }
+
+       void allow_96khz_audio_changed ()
+       {
+               Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
+       }
+
+       void use_all_channels_changed ()
+       {
+               Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
        }
 
        void show_experimental_audio_processors_changed ()
        {
-               Config::instance()->set_show_experimental_audio_processors (_show_experimental_audio_processors->GetValue ());
+               Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
        }
 
        void only_servers_encode_changed ()
        {
-               Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
+               Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
        }
 
        void dcp_metadata_filename_format_changed ()
        {
-               Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
+               Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
        }
 
        void dcp_asset_filename_format_changed ()
        {
-               Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
+               Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
        }
 
        void log_changed ()
@@ -1444,7 +1733,7 @@ private:
                        types |= LogEntry::TYPE_TIMING;
                }
                if (_log_debug_threed->GetValue ()) {
-                       types |= LogEntry::TYPE_DEBUG_THREED;
+                       types |= LogEntry::TYPE_DEBUG_THREE_D;
                }
                if (_log_debug_encode->GetValue ()) {
                        types |= LogEntry::TYPE_DEBUG_ENCODE;
@@ -1452,64 +1741,80 @@ private:
                if (_log_debug_email->GetValue ()) {
                        types |= LogEntry::TYPE_DEBUG_EMAIL;
                }
+               if (_log_debug_video_view->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
+               }
+               if (_log_debug_player->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_PLAYER;
+               }
+               if (_log_debug_audio_analysis->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
+               }
                Config::instance()->set_log_types (types);
        }
 
 #ifdef DCPOMATIC_WINDOWS
        void win32_console_changed ()
        {
-               Config::instance()->set_win32_console (_win32_console->GetValue ());
+               Config::instance()->set_win32_console(_win32_console->GetValue());
        }
 #endif
 
-       wxSpinCtrl* _maximum_j2k_bandwidth;
-       wxChoice* _video_display_mode;
-       wxSpinCtrl* _frames_in_memory_multiplier;
-       wxCheckBox* _allow_any_dcp_frame_rate;
-       wxCheckBox* _allow_any_container;
-       wxCheckBox* _show_experimental_audio_processors;
-       wxCheckBox* _only_servers_encode;
-       NameFormatEditor* _dcp_metadata_filename_format;
-       NameFormatEditor* _dcp_asset_filename_format;
-       wxCheckBox* _log_general;
-       wxCheckBox* _log_warning;
-       wxCheckBox* _log_error;
-       wxCheckBox* _log_timing;
-       wxCheckBox* _log_debug_threed;
-       wxCheckBox* _log_debug_encode;
-       wxCheckBox* _log_debug_email;
+       wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
+       wxChoice* _video_display_mode = nullptr;
+       wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
+       wxCheckBox* _allow_any_dcp_frame_rate = nullptr;
+       wxCheckBox* _allow_any_container = nullptr;
+       wxCheckBox* _allow_96khz_audio = nullptr;
+       wxCheckBox* _use_all_audio_channels = nullptr;
+       wxCheckBox* _show_experimental_audio_processors = nullptr;
+       wxCheckBox* _only_servers_encode = nullptr;
+       NameFormatEditor* _dcp_metadata_filename_format = nullptr;
+       NameFormatEditor* _dcp_asset_filename_format = nullptr;
+       wxCheckBox* _log_general = nullptr;
+       wxCheckBox* _log_warning = nullptr;
+       wxCheckBox* _log_error = nullptr;
+       wxCheckBox* _log_timing = nullptr;
+       wxCheckBox* _log_debug_threed = nullptr;
+       wxCheckBox* _log_debug_encode = nullptr;
+       wxCheckBox* _log_debug_email = nullptr;
+       wxCheckBox* _log_debug_video_view = nullptr;
+       wxCheckBox* _log_debug_player = nullptr;
+       wxCheckBox* _log_debug_audio_analysis = nullptr;
 #ifdef DCPOMATIC_WINDOWS
-       wxCheckBox* _win32_console;
+       wxCheckBox* _win32_console = nullptr;
 #endif
 };
 
+
 wxPreferencesEditor*
 create_full_config_dialog ()
 {
-       wxPreferencesEditor* e = new wxPreferencesEditor ();
+       auto e = new wxPreferencesEditor ();
 
 #ifdef DCPOMATIC_OSX
        /* Width that we force some of the config panels to be on OSX so that
           the containing window doesn't shrink too much when we select those panels.
           This is obviously an unpleasant hack.
        */
-       wxSize ps = wxSize (700, -1);
+       wxSize ps = wxSize (750, -1);
        int const border = 16;
 #else
        wxSize ps = wxSize (-1, -1);
        int const border = 8;
 #endif
 
-       e->AddPage (new FullGeneralPage (ps, border));
-       e->AddPage (new SoundPage (ps, border));
-       e->AddPage (new DefaultsPage (ps, border));
-       e->AddPage (new EncodingServersPage (ps, border));
-       e->AddPage (new KeysPage (ps, border));
-       e->AddPage (new TMSPage (ps, border));
-       e->AddPage (new EmailPage (ps, border));
-       e->AddPage (new KDMEmailPage (ps, border));
-       e->AddPage (new NotificationsPage (ps, border));
-       e->AddPage (new CoverSheetPage (ps, border));
-       e->AddPage (new AdvancedPage (ps, border));
+       e->AddPage (new FullGeneralPage    (ps, border));
+       e->AddPage (new SoundPage          (ps, border));
+       e->AddPage (new DefaultsPage       (ps, border));
+       e->AddPage (new EncodingServersPage(ps, border));
+       e->AddPage (new KeysPage           (ps, border));
+       e->AddPage (new TMSPage            (ps, border));
+       e->AddPage (new EmailPage          (ps, border));
+       e->AddPage (new KDMEmailPage       (ps, border));
+       e->AddPage (new NotificationsPage  (ps, border));
+       e->AddPage (new CoverSheetPage     (ps, border));
+       e->AddPage (new IdentifiersPage    (ps, border));
+       e->AddPage (new AdvancedPage       (ps, border));
        return e;
 }