Fix bugs with default container configuration.
[dcpomatic.git] / src / wx / config_dialog.cc
index e44ed879bbcd6895b527abc11c813d6a1c7f2e74..233766cbaddd27395ea77033f5c872441a01fb3c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include "server_dialog.h"
 #include "make_chain_dialog.h"
 #include "email_dialog.h"
+#include "name_format_editor.h"
+#include "nag_dialog.h"
 #include "lib/config.h"
 #include "lib/ratio.h"
 #include "lib/filter.h"
 #include "lib/dcp_content_type.h"
 #include "lib/log.h"
 #include "lib/util.h"
-#include "lib/raw_convert.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 <wx/preferences.h>
 #include <wx/spinctrl.h>
 #include <wx/filepicker.h>
+#include <RtAudio.h>
 #include <boost/filesystem.hpp>
 #include <boost/foreach.hpp>
 #include <iostream>
@@ -62,6 +65,14 @@ using boost::bind;
 using boost::shared_ptr;
 using boost::function;
 using boost::optional;
+using dcp::locale_convert;
+
+static
+void
+do_nothing ()
+{
+
+}
 
 class Page
 {
@@ -191,16 +202,27 @@ private:
                restart->SetFont (font);
                ++r;
 
-               add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
-               _num_local_encoding_threads = new wxSpinCtrl (_panel);
-               table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
+               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));
+               ++r;
+
+               add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
+               _server_encoding_threads = new wxSpinCtrl (_panel);
+               table->Add (_server_encoding_threads, 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");
+               _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
                table->Add (_cinemas_file, wxGBPosition (r, 1));
                ++r;
 
+               _preview_sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound in the preview via"));
+               table->Add (_preview_sound, wxGBPosition (r, 0));
+                _preview_sound_output = new wxChoice (_panel, wxID_ANY);
+                table->Add (_preview_sound_output, wxGBPosition (r, 1));
+                ++r;
+
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
                _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
                table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
@@ -233,22 +255,34 @@ private:
                table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
                ++r;
 
-               _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED,   boost::bind (&GeneralPage::set_language_changed, this));
-               _language->Bind     (wxEVT_COMMAND_CHOICE_SELECTED,    boost::bind (&GeneralPage::language_changed,     this));
-               _cinemas_file->Bind (wxEVT_COMMAND_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
-
-               _num_local_encoding_threads->SetRange (1, 128);
-               _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
+                RtAudio audio (DCPOMATIC_RTAUDIO_API);
+                for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
+                        RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
+                        if (dev.probed && dev.outputChannels > 0) {
+                                _preview_sound_output->Append (std_to_wx (dev.name));
+                        }
+                }
+
+               _set_language->Bind         (wxEVT_CHECKBOX,           boost::bind (&GeneralPage::set_language_changed,  this));
+               _language->Bind             (wxEVT_CHOICE,             boost::bind (&GeneralPage::language_changed,      this));
+               _cinemas_file->Bind         (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed,  this));
+               _preview_sound->Bind        (wxEVT_CHECKBOX,           boost::bind (&GeneralPage::preview_sound_changed, this));
+               _preview_sound_output->Bind (wxEVT_CHOICE,             boost::bind (&GeneralPage::preview_sound_output_changed, this));
+
+               _master_encoding_threads->SetRange (1, 128);
+               _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
+               _server_encoding_threads->SetRange (1, 128);
+               _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::server_encoding_threads_changed, this));
 
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
-               _analyse_ebur128->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
+               _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
 #endif
-               _automatic_audio_analysis->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
-               _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
-               _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
+               _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
+               _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
+               _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
 
-               _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::issuer_changed, this));
-               _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::creator_changed, this));
+               _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
+               _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
        }
 
        void config_changed ()
@@ -281,7 +315,8 @@ private:
 
                checked_set (_language, lang);
 
-               checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
+               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
@@ -291,14 +326,54 @@ private:
                checked_set (_issuer, config->dcp_issuer ());
                checked_set (_creator, config->dcp_creator ());
                checked_set (_cinemas_file, config->cinemas_file());
+               checked_set (_preview_sound, config->preview_sound());
+
+                optional<string> const current_so = get_preview_sound_output ();
+                optional<string> configured_so;
+
+                if (config->preview_sound_output()) {
+                        configured_so = config->preview_sound_output().get();
+                } else {
+                        /* No configured output means we should use the default */
+                        RtAudio audio (DCPOMATIC_RTAUDIO_API);
+                       try {
+                               configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
+                       } catch (RtAudioError& e) {
+                               /* Probably no audio devices at all */
+                       }
+                }
+
+                if (configured_so && current_so != configured_so) {
+                        /* Update _preview_sound_output with the configured value */
+                        unsigned int i = 0;
+                        while (i < _preview_sound_output->GetCount()) {
+                                if (_preview_sound_output->GetString(i) == std_to_wx(*configured_so)) {
+                                        _preview_sound_output->SetSelection (i);
+                                        break;
+                                }
+                                ++i;
+                        }
+                }
 
                setup_sensitivity ();
        }
 
+        /** @return Currently-selected preview sound output in the dialogue */
+        optional<string> get_preview_sound_output ()
+        {
+                int const sel = _preview_sound_output->GetSelection ();
+                if (sel == wxNOT_FOUND) {
+                        return optional<string> ();
+                }
+
+                return wx_to_std (_preview_sound_output->GetString (sel));
+        }
+
        void setup_sensitivity ()
        {
                _language->Enable (_set_language->GetValue ());
                _check_for_test_updates->Enable (_check_for_updates->GetValue ());
+               _preview_sound_output->Enable (_preview_sound->GetValue ());
        }
 
        void set_language_changed ()
@@ -343,9 +418,14 @@ private:
                Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
        }
 
-       void num_local_encoding_threads_changed ()
+       void master_encoding_threads_changed ()
        {
-               Config::instance()->set_num_local_encoding_threads (_num_local_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 ()
@@ -363,10 +443,29 @@ private:
                Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
        }
 
+       void preview_sound_changed ()
+       {
+               Config::instance()->set_preview_sound (_preview_sound->GetValue ());
+       }
+
+        void preview_sound_output_changed ()
+        {
+                RtAudio audio (DCPOMATIC_RTAUDIO_API);
+                optional<string> const so = get_preview_sound_output();
+                if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
+                        Config::instance()->unset_preview_sound_output ();
+                } else {
+                        Config::instance()->set_preview_sound_output (*so);
+                }
+        }
+
        wxCheckBox* _set_language;
        wxChoice* _language;
-       wxSpinCtrl* _num_local_encoding_threads;
+       wxSpinCtrl* _master_encoding_threads;
+       wxSpinCtrl* _server_encoding_threads;
        FilePickerCtrl* _cinemas_file;
+       wxCheckBox* _preview_sound;
+       wxChoice* _preview_sound_output;
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
        wxCheckBox* _analyse_ebur128;
 #endif
@@ -428,6 +527,10 @@ private:
                _container = new wxChoice (_panel, wxID_ANY);
                table->Add (_container);
 
+               add_label_to_sizer (table, _panel, _("Default scale-to"), true);
+               _scale_to = new wxChoice (_panel, wxID_ANY);
+               table->Add (_scale_to);
+
                add_label_to_sizer (table, _panel, _("Default content type"), true);
                _dcp_content_type = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_content_type);
@@ -458,57 +561,78 @@ private:
                _standard = new wxChoice (_panel, wxID_ANY);
                table->Add (_standard);
 
+               add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
+#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);
+
                _still_length->SetRange (1, 3600);
-               _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
+               _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
 
-               _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_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));
 
-               _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
+               _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
 
-               vector<Ratio const *> ratios = Ratio::all ();
-               for (size_t i = 0; i < ratios.size(); ++i) {
-                       _container->Append (std_to_wx (ratios[i]->nickname ()));
+               BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
+                       _container->Append (std_to_wx(i->container_nickname()));
                }
 
-               _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
+               _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
 
-               vector<DCPContentType const *> const ct = DCPContentType::all ();
-               for (size_t i = 0; i < ct.size(); ++i) {
-                       _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
+               _scale_to->Append (_("Guess from content"));
+
+               BOOST_FOREACH (Ratio const * i, Ratio::all()) {
+                       _scale_to->Append (std_to_wx(i->image_nickname()));
                }
 
-               vector<pair<string, string> > items;
-               for (int i = 0; i <= 16; i += 2) {
-                       items.push_back (make_pair (raw_convert<string> (i), raw_convert<string> (i)));
+               _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
+
+               BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
+                       _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
                }
 
-               checked_set (_dcp_audio_channels, items);
+               setup_audio_channels_choice (_dcp_audio_channels, 2);
 
-               _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
-               _dcp_audio_channels->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
+               _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
+               _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
 
                _j2k_bandwidth->SetRange (50, 250);
-               _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
+               _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
 
                _audio_delay->SetRange (-1000, 1000);
-               _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
+               _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
 
                _standard->Append (_("SMPTE"));
                _standard->Append (_("Interop"));
-               _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::standard_changed, this));
+               _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
        }
 
        void config_changed ()
        {
                Config* config = Config::instance ();
 
+               vector<Ratio const *> containers = Ratio::containers ();
+               for (size_t i = 0; i < containers.size(); ++i) {
+                       if (containers[i] == config->default_container ()) {
+                               _container->SetSelection (i);
+                       }
+               }
+
                vector<Ratio const *> ratios = Ratio::all ();
                for (size_t i = 0; i < ratios.size(); ++i) {
-                       if (ratios[i] == config->default_container ()) {
-                               _container->SetSelection (i);
+                       if (ratios[i] == config->default_scale_to ()) {
+                               _scale_to->SetSelection (i + 1);
                        }
                }
 
+               if (!config->default_scale_to()) {
+                       _scale_to->SetSelection (0);
+               }
+
                vector<DCPContentType const *> const ct = DCPContentType::all ();
                for (size_t i = 0; i < ct.size(); ++i) {
                        if (ct[i] == config->default_dcp_content_type ()) {
@@ -518,9 +642,10 @@ 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 ()));
                checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
                _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
-               checked_set (_dcp_audio_channels, raw_convert<string> (config->default_dcp_audio_channels()));
+               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);
        }
@@ -539,7 +664,9 @@ private:
        {
                int const s = _dcp_audio_channels->GetSelection ();
                if (s != wxNOT_FOUND) {
-                       Config::instance()->set_default_dcp_audio_channels (s * 2);
+                       Config::instance()->set_default_dcp_audio_channels (
+                               locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
+                               );
                }
        }
 
@@ -548,6 +675,11 @@ private:
                Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
        }
 
+       void kdm_directory_changed ()
+       {
+               Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
+       }
+
        void edit_isdcf_metadata_clicked ()
        {
                ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
@@ -563,10 +695,21 @@ private:
 
        void container_changed ()
        {
-               vector<Ratio const *> ratio = Ratio::all ();
+               vector<Ratio const *> ratio = Ratio::containers ();
                Config::instance()->set_default_container (ratio[_container->GetSelection()]);
        }
 
+       void scale_to_changed ()
+       {
+               int const s = _scale_to->GetSelection ();
+               if (s == 0) {
+                       Config::instance()->set_default_scale_to (0);
+               } else {
+                       vector<Ratio const *> ratio = Ratio::all ();
+                       Config::instance()->set_default_scale_to (ratio[s - 1]);
+               }
+       }
+
        void dcp_content_type_changed ()
        {
                vector<DCPContentType const *> ct = DCPContentType::all ();
@@ -584,10 +727,13 @@ private:
        wxSpinCtrl* _still_length;
 #ifdef DCPOMATIC_USE_OWN_PICKER
        DirPickerCtrl* _directory;
+       DirPickerCtrl* _kdm_directory;
 #else
        wxDirPickerCtrl* _directory;
+       wxDirPickerCtrl* _kdm_directory;
 #endif
        wxChoice* _container;
+       wxChoice* _scale_to;
        wxChoice* _dcp_content_type;
        wxChoice* _dcp_audio_channels;
        wxChoice* _standard;
@@ -625,13 +771,12 @@ private:
                        columns,
                        boost::bind (&Config::servers, Config::instance()),
                        boost::bind (&Config::set_servers, Config::instance(), _1),
-                       boost::bind (&always_valid),
                        boost::bind (&EncodingServersPage::server_column, this, _1)
                        );
 
                _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
 
-               _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, 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 ()
@@ -662,11 +807,13 @@ public:
                wxString title,
                int border,
                function<void (shared_ptr<dcp::CertificateChain>)> set,
-               function<shared_ptr<const dcp::CertificateChain> (void)> get
+               function<shared_ptr<const dcp::CertificateChain> (void)> get,
+               function<void (void)> nag_remake
                )
                : wxPanel (parent)
                , _set (set)
                , _get (get)
+               , _nag_remake (nag_remake)
        {
                wxFont subheading_font (*wxNORMAL_FONT);
                subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
@@ -735,19 +882,26 @@ public:
                ++r;
 
                _button_sizer = new wxBoxSizer (wxHORIZONTAL);
-               _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
+               _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
                _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
                table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
                ++r;
 
-               _add_certificate->Bind     (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::add_certificate, this));
-               _remove_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remove_certificate, this));
-               _export_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::export_certificate, this));
-               _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
-               _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
-               _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remake_certificates, this));
-               _load_private_key->Bind    (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::load_private_key, this));
-               _export_private_key->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::export_private_key, this));
+               _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
+               font = *wxSMALL_FONT;
+               font.SetWeight (wxFONTWEIGHT_BOLD);
+               _private_key_bad->SetFont (font);
+               table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
+               ++r;
+
+               _add_certificate->Bind     (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::add_certificate, this));
+               _remove_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remove_certificate, this));
+               _export_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_certificate, this));
+               _certificates->Bind        (wxEVT_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
+               _certificates->Bind        (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
+               _remake_certificates->Bind (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remake_certificates, this));
+               _load_private_key->Bind    (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::load_private_key, this));
+               _export_private_key->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_private_key, this));
 
                SetSizerAndFit (_sizer);
        }
@@ -763,7 +917,7 @@ public:
 
        void add_button (wxWindow* button)
        {
-               _button_sizer->Add (button);
+               _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
                _sizer->Layout ();
        }
 
@@ -774,8 +928,17 @@ private:
 
                if (d->ShowModal() == wxID_OK) {
                        try {
-                               dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
-                               if (c.extra_data ()) {
+                               dcp::Certificate c;
+                               string extra;
+                               try {
+                                       extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
+                               } catch (boost::filesystem::filesystem_error& e) {
+                                       error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
+                                       d->Destroy ();
+                                       return;
+                               }
+
+                               if (!extra.empty ()) {
                                        message_dialog (
                                                this,
                                                _("This file contains other certificates (or other data) after its first certificate. "
@@ -783,8 +946,17 @@ private:
                                                );
                                }
                                _chain->add (c);
-                               _set (_chain);
-                               update_certificate_list ();
+                               if (!_chain->chain_valid ()) {
+                                       error_dialog (
+                                               this,
+                                               _("Adding this certificate would make the chain inconsistent, so it will not be added. "
+                                                 "Add certificates in order from root to intermediate to leaf.")
+                                               );
+                                       _chain->remove (c);
+                               } else {
+                                       _set (_chain);
+                                       update_certificate_list ();
+                               }
                        } catch (dcp::MiscError& e) {
                                error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
                        }
@@ -830,7 +1002,7 @@ private:
                if (d->ShowModal () == wxID_OK) {
                        FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
                        if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()));
+                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
                        }
 
                        string const s = j->certificate (true);
@@ -861,6 +1033,16 @@ private:
 
                        ++n;
                }
+
+               static wxColour normal = _private_key_bad->GetForegroundColour ();
+
+               if (_chain->private_key_valid ()) {
+                       _private_key_bad->Hide ();
+                       _private_key_bad->SetForegroundColour (normal);
+               } else {
+                       _private_key_bad->Show ();
+                       _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
+               }
        }
 
        void remake_certificates ()
@@ -894,6 +1076,8 @@ private:
                        intermediate_common_name = i->subject_common_name ();
                }
 
+               _nag_remake ();
+
                MakeChainDialog* d = new MakeChainDialog (
                        this,
                        subject_organization_name,
@@ -925,7 +1109,8 @@ private:
 
        void update_sensitivity ()
        {
-               _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
+               /* We can only remove the leaf certificate */
+               _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
                _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
        }
 
@@ -978,7 +1163,7 @@ private:
                if (d->ShowModal () == wxID_OK) {
                        FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
                        if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()));
+                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
                        }
 
                        string const s = _chain->key().get ();
@@ -996,11 +1181,13 @@ private:
        wxStaticText* _private_key;
        wxButton* _load_private_key;
        wxButton* _export_private_key;
+       wxStaticText* _private_key_bad;
        wxSizer* _sizer;
        wxBoxSizer* _button_sizer;
        shared_ptr<dcp::CertificateChain> _chain;
        boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
        boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
+       boost::function<void (void)> _nag_remake;
 };
 
 class KeysPage : public StandardPage
@@ -1029,23 +1216,28 @@ private:
                _signer = new CertificateChainEditor (
                        _panel, _("Signing DCPs and KDMs"), _border,
                        boost::bind (&Config::set_signer_chain, Config::instance (), _1),
-                       boost::bind (&Config::signer_chain, Config::instance ())
+                       boost::bind (&Config::signer_chain, Config::instance ()),
+                       boost::bind (&do_nothing)
                        );
 
                _panel->GetSizer()->Add (_signer);
 
                _decryption = new CertificateChainEditor (
-                       _panel, _("Decrypting DCPs"), _border,
+                       _panel, _("Decrypting KDMs"), _border,
                        boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
-                       boost::bind (&Config::decryption_chain, Config::instance ())
+                       boost::bind (&Config::decryption_chain, Config::instance ()),
+                       boost::bind (&KeysPage::nag_remake_decryption_chain, this)
                        );
 
                _panel->GetSizer()->Add (_decryption);
 
-               _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
+               _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
                _decryption->add_button (_export_decryption_certificate);
+               _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
+               _decryption->add_button (_export_decryption_chain);
 
-               _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
+               _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
+               _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
        }
 
        void export_decryption_certificate ()
@@ -1058,7 +1250,7 @@ private:
                if (d->ShowModal () == wxID_OK) {
                        FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
                        if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()));
+                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
                        }
 
                        string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
@@ -1068,15 +1260,45 @@ private:
                d->Destroy ();
        }
 
+       void export_decryption_chain ()
+       {
+               wxFileDialog* d = new wxFileDialog (
+                       _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+                       );
+
+               if (d->ShowModal () == wxID_OK) {
+                       FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+                       if (!f) {
+                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+                       }
+
+                       string const s = Config::instance()->decryption_chain()->chain();
+                       fwrite (s.c_str(), 1, s.length(), f);
+                       fclose (f);
+               }
+               d->Destroy ();
+       }
+
        void config_changed ()
        {
                _signer->config_changed ();
                _decryption->config_changed ();
        }
 
+       void nag_remake_decryption_chain ()
+       {
+               NagDialog::maybe_nag (
+                       _panel,
+                       Config::NAG_REMAKE_DECRYPTION_CHAIN,
+                       _("If you continue with this operation you will no longer be able to use any DKDMs that you have created.  Also, any KDMs that have been sent to you will become useless.  Proceed with caution!")
+                       );
+       }
+
        CertificateChainEditor* _signer;
        CertificateChainEditor* _decryption;
        wxButton* _export_decryption_certificate;
+       wxButton* _export_decryption_chain;
 };
 
 class TMSPage : public StandardPage
@@ -1128,11 +1350,11 @@ private:
                _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
                _tms_protocol->Append (_("FTP (for Dolby)"));
 
-               _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
-               _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
-               _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
-               _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
-               _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
+               _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
+               _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
+               _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
+               _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
+               _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
        }
 
        void config_changed ()
@@ -1252,7 +1474,6 @@ private:
                        columns,
                        bind (&Config::kdm_cc, Config::instance()),
                        bind (&Config::set_kdm_cc, Config::instance(), _1),
-                       bind (&string_not_empty, _1),
                        bind (&column, _1)
                        );
                table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
@@ -1269,15 +1490,15 @@ private:
 
                _kdm_cc->layout ();
 
-               _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
-               _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
-               _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
-               _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
-               _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
-               _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
-               _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
-               _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
-               _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
+               _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
+               _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
+               _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
+               _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
+               _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
+               _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
+               _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
+               _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
+               _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
        }
 
        void config_changed ()
@@ -1358,6 +1579,71 @@ private:
        wxButton* _reset_kdm_email;
 };
 
+class CoverSheetPage : public StandardPage
+{
+public:
+
+       CoverSheetPage (wxSize panel_size, int border)
+#ifdef DCPOMATIC_OSX
+               /* We have to force both width and height of this one */
+               : StandardPage (wxSize (480, 128), border)
+#else
+               : StandardPage (panel_size, border)
+#endif
+       {}
+
+       wxString GetName () const
+       {
+               return _("Cover Sheet");
+       }
+
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
+       {
+               return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
+
+private:
+       void setup ()
+       {
+               _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
+               _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
+
+               _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
+               _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
+
+               _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
+               _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
+       }
+
+       void config_changed ()
+       {
+               checked_set (_cover_sheet, Config::instance()->cover_sheet ());
+       }
+
+       void cover_sheet_changed ()
+       {
+               if (_cover_sheet->GetValue().IsEmpty ()) {
+                       /* Sometimes we get sent an erroneous notification that the cover sheet
+                          is empty; I don't know why.
+                       */
+                       return;
+               }
+               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 ());
+       }
+
+       wxTextCtrl* _cover_sheet;
+       wxButton* _reset_cover_sheet;
+};
+
+
 /** @class AdvancedPage
  *  @brief Advanced page of the preferences dialog.
  */
@@ -1379,6 +1665,17 @@ public:
        {}
 
 private:
+       void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
+       {
+               int flags = wxALIGN_TOP | wxTOP | wxLEFT;
+#ifdef __WXOSX__
+               flags |= wxALIGN_RIGHT;
+               text += wxT (":");
+#endif
+               wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
+               table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
+       }
+
        void setup ()
        {
                wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
@@ -1402,15 +1699,38 @@ private:
                table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
-#ifdef __WXOSX__
-               wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
-               table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
-#else
-               wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
-               table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
-#endif
+               {
+                       add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
+                       dcp::NameFormat::Map titles;
+                       titles['t'] = "type (cpl/pkl)";
+                       dcp::NameFormat::Map examples;
+                       examples['t'] = "cpl";
+                       _dcp_metadata_filename_format = new NameFormatEditor (
+                               _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
+                               );
+                       table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
+               }
+
+               {
+                       add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
+                       dcp::NameFormat::Map titles;
+                       titles['t'] = "type (j2c/pcm/sub)";
+                       titles['r'] = "reel number";
+                       titles['n'] = "number of reels";
+                       titles['c'] = "content filename";
+                       dcp::NameFormat::Map examples;
+                       examples['t'] = "j2c";
+                       examples['r'] = "1";
+                       examples['n'] = "4";
+                       examples['c'] = "myfile.mp4";
+                       _dcp_asset_filename_format = new NameFormatEditor (
+                               _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
+                               );
+                       table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
+               }
 
                {
+                       add_top_aligned_label_to_sizer (table, _panel, _("Log"));
                        wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
                        _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
                        t->Add (_log_general, 1, wxEXPAND | wxALL);
@@ -1437,18 +1757,20 @@ private:
 #endif
 
                _maximum_j2k_bandwidth->SetRange (1, 1000);
-               _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
-               _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
-               _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
-               _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
-               _log_debug_email->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+               _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
+               _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
+               _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
+               _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
+               _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
+               _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_decode->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));
 #ifdef DCPOMATIC_WINDOWS
-               _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
+               _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
 #endif
        }
 
@@ -1486,6 +1808,16 @@ private:
                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 ());
+       }
+
+       void dcp_asset_filename_format_changed ()
+       {
+               Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
+       }
+
        void log_changed ()
        {
                int types = 0;
@@ -1523,6 +1855,8 @@ private:
        wxSpinCtrl* _maximum_j2k_bandwidth;
        wxCheckBox* _allow_any_dcp_frame_rate;
        wxCheckBox* _only_servers_encode;
+       NameFormatEditor* _dcp_metadata_filename_format;
+       NameFormatEditor* _dcp_asset_filename_format;
        wxCheckBox* _log_general;
        wxCheckBox* _log_warning;
        wxCheckBox* _log_error;
@@ -1558,6 +1892,7 @@ create_config_dialog ()
        e->AddPage (new KeysPage (ps, border));
        e->AddPage (new TMSPage (ps, border));
        e->AddPage (new KDMEmailPage (ps, border));
+       e->AddPage (new CoverSheetPage (ps, border));
        e->AddPage (new AdvancedPage (ps, border));
        return e;
 }