Support buttons.
[dcpomatic.git] / src / wx / full_config_dialog.cc
index 56a765af297c9203e9bdb6fd3ab57112c44ad126..2f1db11c2600d3c16f8207c58ae01e31d64a1f5e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -36,6 +36,9 @@
 #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 "lib/config.h"
 #include "lib/ratio.h"
 #include "lib/filter.h"
@@ -69,13 +72,6 @@ using boost::function;
 using boost::optional;
 using dcp::locale_convert;
 
-static
-void
-do_nothing ()
-{
-
-}
-
 class FullGeneralPage : public GeneralPage
 {
 public:
@@ -92,6 +88,11 @@ private:
                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));
@@ -110,17 +111,19 @@ private:
                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);
                table->Add (_cinemas_file, wxGBPosition (r, 1));
+               Button* export_cinemas = new Button (_panel, _("Export..."));
+               table->Add (export_cinemas, wxGBPosition (r, 2));
                ++r;
 
                add_play_sound_controls (table, 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"));
+               _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
                table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
                ++r;
 #endif
 
-               _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
+               _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
                table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
                ++r;
 
@@ -143,10 +146,15 @@ private:
                _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));
+
                _master_encoding_threads->SetRange (1, 128);
                _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
                _server_encoding_threads->SetRange (1, 128);
                _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
+               export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
 
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
                _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
@@ -161,6 +169,14 @@ private:
        {
                Config* 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
@@ -175,6 +191,28 @@ private:
                GeneralPage::config_changed ();
        }
 
+       void export_cinemas_file ()
+       {
+               wxFileDialog* d = new wxFileDialog (
+                       _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT ("XML files (*.xml)|*.xml"),
+                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+                );
+
+               if (d->ShowModal () == wxID_OK) {
+                       boost::filesystem::copy_file (Config::instance()->cinemas_file(), wx_to_std(d->GetPath()));
+               }
+               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 ()
@@ -239,6 +277,7 @@ 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;
@@ -295,7 +334,7 @@ private:
                table->Add (_directory, 1, wxEXPAND);
 
                add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
-               _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
+               _isdcf_metadata_button = new Button (_panel, _("Edit..."));
                table->Add (_isdcf_metadata_button);
 
                add_label_to_sizer (table, _panel, _("Default container"), true);
@@ -342,8 +381,12 @@ private:
 #else
                _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
 #endif
+
                table->Add (_kdm_directory, 1, wxEXPAND);
 
+               _upload_after_make_dcp = new CheckBox (_panel, _("Default to enabling upload of DCP to TMS"));
+               table->Add (_upload_after_make_dcp, 1, wxEXPAND);
+
                _still_length->SetRange (1, 3600);
                _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
 
@@ -384,6 +427,8 @@ private:
                _standard->Append (_("SMPTE"));
                _standard->Append (_("Interop"));
                _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
+
+               _upload_after_make_dcp->Bind (wxEVT_CHECKBOX, boost::bind (&DefaultsPage::upload_after_make_dcp_changed, this));
        }
 
        void config_changed ()
@@ -423,6 +468,7 @@ private:
                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);
+               checked_set (_upload_after_make_dcp, config->default_upload_after_make_dcp());
        }
 
        void j2k_bandwidth_changed ()
@@ -496,6 +542,11 @@ private:
                Config::instance()->set_default_interop (_standard->GetSelection() == 1);
        }
 
+       void upload_after_make_dcp_changed ()
+       {
+               Config::instance()->set_default_upload_after_make_dcp (_upload_after_make_dcp->GetValue ());
+       }
+
        wxSpinCtrl* _j2k_bandwidth;
        wxSpinCtrl* _audio_delay;
        wxButton* _isdcf_metadata_button;
@@ -512,6 +563,7 @@ private:
        wxChoice* _dcp_content_type;
        wxChoice* _dcp_audio_channels;
        wxChoice* _standard;
+       wxCheckBox* _upload_after_make_dcp;
 };
 
 class EncodingServersPage : public StandardPage
@@ -536,7 +588,7 @@ public:
 private:
        void setup ()
        {
-               _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
+               _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
                _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
 
                vector<string> columns;
@@ -574,621 +626,432 @@ private:
        EditableList<string, ServerDialog>* _servers_list;
 };
 
-class CertificateChainEditor : public wxPanel
+class TMSPage : public StandardPage
 {
 public:
-       CertificateChainEditor (
-               wxWindow* parent,
-               wxString title,
-               int border,
-               function<void (shared_ptr<dcp::CertificateChain>)> set,
-               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);
-
-               _sizer = new wxBoxSizer (wxVERTICAL);
-
-               {
-                       wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
-                       m->SetFont (subheading_font);
-                       _sizer->Add (m, 0, wxALL, border);
-               }
-
-               wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
-               _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
-
-               _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
-
-               {
-                       wxListItem ip;
-                       ip.SetId (0);
-                       ip.SetText (_("Type"));
-                       ip.SetWidth (100);
-                       _certificates->InsertColumn (0, ip);
-               }
-
-               {
-                       wxListItem ip;
-                       ip.SetId (1);
-                       ip.SetText (_("Thumbprint"));
-                       ip.SetWidth (340);
+       TMSPage (wxSize panel_size, int border)
+               : StandardPage (panel_size, border)
+       {}
 
-                       wxFont font = ip.GetFont ();
-                       font.SetFamily (wxFONTFAMILY_TELETYPE);
-                       ip.SetFont (font);
+       wxString GetName () const
+       {
+               return _("TMS");
+       }
 
-                       _certificates->InsertColumn (1, ip);
-               }
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
+       {
+               return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
 
-               certificates_sizer->Add (_certificates, 1, wxEXPAND);
+private:
+       void setup ()
+       {
+               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               table->AddGrowableCol (1, 1);
+               _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
-               {
-                       wxSizer* s = new wxBoxSizer (wxVERTICAL);
-                       _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
-                       s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
-                       s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
-                       s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
-               }
+               add_label_to_sizer (table, _panel, _("Protocol"), true);
+               _tms_protocol = new wxChoice (_panel, wxID_ANY);
+               table->Add (_tms_protocol, 1, wxEXPAND);
 
-               wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-               _sizer->Add (table, 1, wxALL | wxEXPAND, border);
-               int r = 0;
+               add_label_to_sizer (table, _panel, _("IP address"), true);
+               _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_tms_ip, 1, wxEXPAND);
 
-               add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
-               _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
-               wxFont font = _private_key->GetFont ();
-               font.SetFamily (wxFONTFAMILY_TELETYPE);
-               _private_key->SetFont (font);
-               table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
-               _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
-               table->Add (_load_private_key, wxGBPosition (r, 2));
-               _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
-               table->Add (_export_private_key, wxGBPosition (r, 3));
-               ++r;
+               add_label_to_sizer (table, _panel, _("Target path"), true);
+               _tms_path = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_tms_path, 1, wxEXPAND);
 
-               _button_sizer = new wxBoxSizer (wxHORIZONTAL);
-               _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_label_to_sizer (table, _panel, _("User name"), true);
+               _tms_user = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_tms_user, 1, wxEXPAND);
 
-               _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_label_to_sizer (table, _panel, _("Password"), true);
+               _tms_password = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_tms_password, 1, wxEXPAND);
 
-               _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));
+               _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
+               _tms_protocol->Append (_("FTP (for Dolby)"));
 
-               SetSizerAndFit (_sizer);
+               _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 ()
        {
-               _chain.reset (new dcp::CertificateChain (*_get().get ()));
+               Config* config = Config::instance ();
 
-               update_certificate_list ();
-               update_private_key ();
-               update_sensitivity ();
+               checked_set (_tms_protocol, config->tms_protocol ());
+               checked_set (_tms_ip, config->tms_ip ());
+               checked_set (_tms_path, config->tms_path ());
+               checked_set (_tms_user, config->tms_user ());
+               checked_set (_tms_password, config->tms_password ());
        }
 
-       void add_button (wxWindow* button)
+       void tms_protocol_changed ()
        {
-               _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
-               _sizer->Layout ();
+               Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
        }
 
-private:
-       void add_certificate ()
-       {
-               wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
-
-               if (d->ShowModal() == wxID_OK) {
-                       try {
-                               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. "
-                                                 "Only the first certificate will be used.")
-                                               );
-                               }
-                               _chain->add (c);
-                               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 ()));
-                       }
-               }
-
-               d->Destroy ();
-
-               update_sensitivity ();
+       void tms_ip_changed ()
+       {
+               Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
        }
 
-       void remove_certificate ()
+       void tms_path_changed ()
        {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
-
-               _certificates->DeleteItem (i);
-               _chain->remove (i);
-               _set (_chain);
-
-               update_sensitivity ();
+               Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
        }
 
-       void export_certificate ()
+       void tms_user_changed ()
        {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
-
-               wxFileDialog* d = new wxFileDialog (
-                       this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
-
-               dcp::CertificateChain::List all = _chain->root_to_leaf ();
-               dcp::CertificateChain::List::iterator j = all.begin ();
-               for (int k = 0; k < i; ++k) {
-                       ++j;
-               }
-
-               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 = j->certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+               Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
        }
 
-       void update_certificate_list ()
+       void tms_password_changed ()
        {
-               _certificates->DeleteAllItems ();
-               size_t n = 0;
-               dcp::CertificateChain::List certs = _chain->root_to_leaf ();
-               BOOST_FOREACH (dcp::Certificate const & i, certs) {
-                       wxListItem item;
-                       item.SetId (n);
-                       _certificates->InsertItem (item);
-                       _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
+               Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
+       }
 
-                       if (n == 0) {
-                               _certificates->SetItem (n, 0, _("Root"));
-                       } else if (n == (certs.size() - 1)) {
-                               _certificates->SetItem (n, 0, _("Leaf"));
-                       } else {
-                               _certificates->SetItem (n, 0, _("Intermediate"));
-                       }
+       wxChoice* _tms_protocol;
+       wxTextCtrl* _tms_ip;
+       wxTextCtrl* _tms_path;
+       wxTextCtrl* _tms_user;
+       wxTextCtrl* _tms_password;
+};
 
-                       ++n;
-               }
+static string
+column (string s)
+{
+       return s;
+}
 
-               static wxColour normal = _private_key_bad->GetForegroundColour ();
+class EmailPage : public StandardPage
+{
+public:
+       EmailPage (wxSize panel_size, int border)
+               : StandardPage (panel_size, border)
+       {}
 
-               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));
-               }
+       wxString GetName () const
+       {
+               return _("Email");
        }
 
-       void remake_certificates ()
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
        {
-               shared_ptr<const dcp::CertificateChain> chain = _get ();
-
-               string subject_organization_name;
-               string subject_organizational_unit_name;
-               string root_common_name;
-               string intermediate_common_name;
-               string leaf_common_name;
-
-               dcp::CertificateChain::List all = chain->root_to_leaf ();
-
-               if (all.size() >= 1) {
-                       /* Have a root */
-                       subject_organization_name = chain->root().subject_organization_name ();
-                       subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
-                       root_common_name = chain->root().subject_common_name ();
-               }
+               return wxBitmap ("email", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
 
-               if (all.size() >= 2) {
-                       /* Have a leaf */
-                       leaf_common_name = chain->leaf().subject_common_name ();
-               }
+private:
+       void setup ()
+       {
+               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               table->AddGrowableCol (1, 1);
+               _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
 
-               if (all.size() >= 3) {
-                       /* Have an intermediate */
-                       dcp::CertificateChain::List::iterator i = all.begin ();
-                       ++i;
-                       intermediate_common_name = i->subject_common_name ();
+               add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
+               {
+                       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);
+                       _port = new wxSpinCtrl (_panel, wxID_ANY);
+                       _port->SetRange (0, 65535);
+                       s->Add (_port);
+                       table->Add (s, 1, wxEXPAND | wxALL);
                }
 
-               _nag_remake ();
+               add_label_to_sizer (table, _panel, _("Mail user name"), true);
+               _user = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_user, 1, wxEXPAND | wxALL);
 
-               MakeChainDialog* d = new MakeChainDialog (
-                       this,
-                       subject_organization_name,
-                       subject_organizational_unit_name,
-                       root_common_name,
-                       intermediate_common_name,
-                       leaf_common_name
-                       );
+               add_label_to_sizer (table, _panel, _("Mail password"), true);
+               _password = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_password, 1, wxEXPAND | wxALL);
 
-               if (d->ShowModal () == wxID_OK) {
-                       _chain.reset (
-                               new dcp::CertificateChain (
-                                       openssl_path (),
-                                       d->organisation (),
-                                       d->organisational_unit (),
-                                       d->root_common_name (),
-                                       d->intermediate_common_name (),
-                                       d->leaf_common_name ()
-                                       )
-                               );
+               _server->Bind (wxEVT_TEXT, boost::bind (&EmailPage::server_changed, this));
+               _port->Bind (wxEVT_SPINCTRL, boost::bind (&EmailPage::port_changed, this));
+               _user->Bind (wxEVT_TEXT, boost::bind (&EmailPage::user_changed, this));
+               _password->Bind (wxEVT_TEXT, boost::bind (&EmailPage::password_changed, this));
+       }
 
-                       _set (_chain);
-                       update_certificate_list ();
-                       update_private_key ();
-               }
+       void config_changed ()
+       {
+               Config* config = Config::instance ();
 
-               d->Destroy ();
+               checked_set (_server, config->mail_server ());
+               checked_set (_port, config->mail_port ());
+               checked_set (_user, config->mail_user ());
+               checked_set (_password, config->mail_password ());
        }
 
-       void update_sensitivity ()
+       void server_changed ()
        {
-               /* 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);
+               Config::instance()->set_mail_server (wx_to_std (_server->GetValue ()));
        }
 
-       void update_private_key ()
+       void port_changed ()
        {
-               checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
-               _sizer->Layout ();
+               Config::instance()->set_mail_port (_port->GetValue ());
        }
 
-       void load_private_key ()
+       void user_changed ()
        {
-               wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
-
-               if (d->ShowModal() == wxID_OK) {
-                       try {
-                               boost::filesystem::path p (wx_to_std (d->GetPath ()));
-                               if (boost::filesystem::file_size (p) > 8192) {
-                                       error_dialog (
-                                               this,
-                                               wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
-                                               );
-                                       return;
-                               }
-
-                               _chain->set_key (dcp::file_to_string (p));
-                               _set (_chain);
-                               update_private_key ();
-                       } catch (dcp::MiscError& e) {
-                               error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
-                       }
-               }
-
-               d->Destroy ();
-
-               update_sensitivity ();
+               Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
        }
 
-       void export_private_key ()
+       void password_changed ()
        {
-               optional<string> key = _chain->key ();
-               if (!key) {
-                       return;
-               }
-
-               wxFileDialog* d = new wxFileDialog (
-                       this, _("Select Key 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 = _chain->key().get ();
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+               Config::instance()->set_mail_password (wx_to_std (_password->GetValue ()));
        }
 
-       wxListCtrl* _certificates;
-       wxButton* _add_certificate;
-       wxButton* _export_certificate;
-       wxButton* _remove_certificate;
-       wxButton* _remake_certificates;
-       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;
+       wxTextCtrl* _server;
+       wxSpinCtrl* _port;
+       wxTextCtrl* _user;
+       wxTextCtrl* _password;
 };
 
-class KeysPage : public StandardPage
+class KDMEmailPage : public StandardPage
 {
 public:
-       KeysPage (wxSize panel_size, int border)
+
+       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)
+#else
                : StandardPage (panel_size, border)
+#endif
        {}
 
        wxString GetName () const
        {
-               return _("Keys");
+               return _("KDM Email");
        }
 
 #ifdef DCPOMATIC_OSX
        wxBitmap GetLargeIcon () const
        {
-               return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
        }
 #endif
 
 private:
-
        void setup ()
        {
-               _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 (&do_nothing)
-                       );
+               wxFlexGridSizer* 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, _("Subject"), true);
+               _subject = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_subject, 1, wxEXPAND | wxALL);
 
-               _panel->GetSizer()->Add (_signer);
+               add_label_to_sizer (table, _panel, _("From address"), true);
+               _from = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_from, 1, wxEXPAND | wxALL);
 
-               _decryption = new CertificateChainEditor (
-                       _panel, _("Decrypting KDMs"), _border,
-                       boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
-                       boost::bind (&Config::decryption_chain, Config::instance ()),
-                       boost::bind (&KeysPage::nag_remake_decryption_chain, this)
+               vector<string> columns;
+               columns.push_back (wx_to_std (_("Address")));
+               add_label_to_sizer (table, _panel, _("CC addresses"), true);
+               _cc = new EditableList<string, EmailDialog> (
+                       _panel,
+                       columns,
+                       bind (&Config::kdm_cc, Config::instance()),
+                       bind (&Config::set_kdm_cc, Config::instance(), _1),
+                       bind (&column, _1)
                        );
+               table->Add (_cc, 1, wxEXPAND | wxALL);
+
+               add_label_to_sizer (table, _panel, _("BCC address"), true);
+               _bcc = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_bcc, 1, wxEXPAND | wxALL);
+
+               _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
+               _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
 
-               _panel->GetSizer()->Add (_decryption);
+               _reset_email = new Button (_panel, _("Reset to default subject and text"));
+               _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
 
-               _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);
+               _cc->layout ();
 
-               _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));
+               _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
+               _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
+               _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
+               _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
+               _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
        }
 
-       void export_decryption_certificate ()
+       void config_changed ()
        {
-               wxFileDialog* d = new wxFileDialog (
-                       _panel, _("Select Certificate 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);
-                       }
+               Config* config = Config::instance ();
 
-                       string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+               checked_set (_subject, config->kdm_subject ());
+               checked_set (_from, config->kdm_from ());
+               checked_set (_bcc, config->kdm_bcc ());
+               checked_set (_email, Config::instance()->kdm_email ());
        }
 
-       void export_decryption_chain ()
+       void kdm_subject_changed ()
        {
-               wxFileDialog* d = new wxFileDialog (
-                       _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
+               Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
+       }
 
-               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);
-                       }
+       void kdm_from_changed ()
+       {
+               Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
+       }
 
-                       string const s = Config::instance()->decryption_chain()->chain();
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+       void kdm_bcc_changed ()
+       {
+               Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
        }
 
-       void config_changed ()
+       void kdm_email_changed ()
        {
-               _signer->config_changed ();
-               _decryption->config_changed ();
+               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_kdm_email (wx_to_std (_email->GetValue ()));
        }
 
-       void nag_remake_decryption_chain ()
+       void reset_email ()
        {
-               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!")
-                       );
+               Config::instance()->reset_kdm_email ();
+               checked_set (_email, Config::instance()->kdm_email ());
        }
 
-       CertificateChainEditor* _signer;
-       CertificateChainEditor* _decryption;
-       wxButton* _export_decryption_certificate;
-       wxButton* _export_decryption_chain;
+       wxTextCtrl* _subject;
+       wxTextCtrl* _from;
+       EditableList<string, EmailDialog>* _cc;
+       wxTextCtrl* _bcc;
+       wxTextCtrl* _email;
+       wxButton* _reset_email;
 };
 
-class TMSPage : public StandardPage
+class AccountsPage : public StandardPage
 {
 public:
-       TMSPage (wxSize panel_size, int border)
+       AccountsPage (wxSize panel_size, int border)
                : StandardPage (panel_size, border)
        {}
 
        wxString GetName () const
        {
-               return _("TMS");
+               return _("Accounts");
        }
 
 #ifdef DCPOMATIC_OSX
        wxBitmap GetLargeIcon () const
        {
-               return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap ("accounts", wxBITMAP_TYPE_PNG_RESOURCE);
        }
 #endif
 
-private:
        void setup ()
        {
                wxFlexGridSizer* 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, _("Protocol"), true);
-               _tms_protocol = new wxChoice (_panel, wxID_ANY);
-               table->Add (_tms_protocol, 1, wxEXPAND);
-
-               add_label_to_sizer (table, _panel, _("IP address"), true);
-               _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_ip, 1, wxEXPAND);
+               _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
 
-               add_label_to_sizer (table, _panel, _("Target path"), true);
-               _tms_path = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_path, 1, wxEXPAND);
+               add_label_to_sizer (table, _panel, _("certificates.barco.com username"), true);
+               _barco_username = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_barco_username, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("User name"), true);
-               _tms_user = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_user, 1, wxEXPAND);
+               add_label_to_sizer (table, _panel, _("certificates.barco.com password"), true);
+               _barco_password = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_barco_password, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("Password"), true);
-               _tms_password = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_password, 1, wxEXPAND);
+               add_label_to_sizer (table, _panel, _("certificates.christiedigital.com username"), true);
+               _christie_username = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_christie_username, 1, wxEXPAND | wxALL);
 
-               _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
-               _tms_protocol->Append (_("FTP (for Dolby)"));
+               add_label_to_sizer (table, _panel, _("certificates.christiedigital.com password"), true);
+               _christie_password = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_christie_password, 1, wxEXPAND | wxALL);
 
-               _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));
+               _barco_username->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::barco_username_changed, this));
+               _barco_password->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::barco_password_changed, this));
+               _christie_username->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::christie_username_changed, this));
+               _christie_password->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::christie_password_changed, this));
        }
 
        void config_changed ()
        {
                Config* config = Config::instance ();
 
-               checked_set (_tms_protocol, config->tms_protocol ());
-               checked_set (_tms_ip, config->tms_ip ());
-               checked_set (_tms_path, config->tms_path ());
-               checked_set (_tms_user, config->tms_user ());
-               checked_set (_tms_password, config->tms_password ());
+               checked_set (_barco_username, config->barco_username().get_value_or(""));
+               checked_set (_barco_password, config->barco_password().get_value_or(""));
+               checked_set (_christie_username, config->christie_username().get_value_or(""));
+               checked_set (_christie_password, config->christie_password().get_value_or(""));
        }
 
-       void tms_protocol_changed ()
+       void barco_username_changed ()
        {
-               Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
+               wxString const s = _barco_username->GetValue();
+               if (!s.IsEmpty()) {
+                       Config::instance()->set_barco_username (wx_to_std(s));
+               } else {
+                       Config::instance()->unset_barco_username ();
+               }
        }
 
-       void tms_ip_changed ()
+       void barco_password_changed ()
        {
-               Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
+               wxString const s = _barco_password->GetValue();
+               if (!s.IsEmpty()) {
+                       Config::instance()->set_barco_password (wx_to_std(s));
+               } else {
+                       Config::instance()->unset_barco_password ();
+               }
        }
 
-       void tms_path_changed ()
+       void christie_username_changed ()
        {
-               Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
+               wxString const s = _christie_username->GetValue();
+               if (!s.IsEmpty()) {
+                       Config::instance()->set_christie_username (wx_to_std(s));
+               } else {
+                       Config::instance()->unset_christie_username ();
+               }
        }
 
-       void tms_user_changed ()
+       void christie_password_changed ()
        {
-               Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
-       }
-
-       void tms_password_changed ()
-       {
-               Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
+               wxString const s = _christie_password->GetValue();
+               if (!s.IsEmpty()) {
+                       Config::instance()->set_christie_password (wx_to_std(s));
+               } else {
+                       Config::instance()->unset_christie_password ();
+               }
        }
 
-       wxChoice* _tms_protocol;
-       wxTextCtrl* _tms_ip;
-       wxTextCtrl* _tms_path;
-       wxTextCtrl* _tms_user;
-       wxTextCtrl* _tms_password;
+private:
+       wxTextCtrl* _barco_username;
+       wxTextCtrl* _barco_password;
+       wxTextCtrl* _christie_username;
+       wxTextCtrl* _christie_password;
 };
 
-static string
-column (string s)
-{
-       return s;
-}
 
-class KDMEmailPage : public StandardPage
+class NotificationsPage : public StandardPage
 {
 public:
-
-       KDMEmailPage (wxSize panel_size, int border)
+       NotificationsPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
                /* We have to force both width and height of this one */
-               : StandardPage (wxSize (480, 128), border)
+               : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
 #else
                : StandardPage (panel_size, border)
 #endif
@@ -1196,13 +1059,13 @@ public:
 
        wxString GetName () const
        {
-               return _("KDM Email");
+               return _("Notifications");
        }
 
 #ifdef DCPOMATIC_OSX
        wxBitmap GetLargeIcon () const
        {
-               return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap ("notifications", wxBITMAP_TYPE_PNG_RESOURCE);
        }
 #endif
 
@@ -1213,145 +1076,143 @@ private:
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
 
-               add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
-               {
-                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-                       _mail_server = new wxTextCtrl (_panel, wxID_ANY);
-                       s->Add (_mail_server, 1, wxEXPAND | wxALL);
-                       add_label_to_sizer (s, _panel, _("port"), false);
-                       _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
-                       _mail_port->SetRange (0, 65535);
-                       s->Add (_mail_port);
-                       table->Add (s, 1, wxEXPAND | wxALL);
-               }
-
-               add_label_to_sizer (table, _panel, _("Mail user name"), true);
-               _mail_user = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_mail_user, 1, wxEXPAND | wxALL);
+               _enable_message_box = new CheckBox (_panel, _("Message box"));
+               table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
+               table->AddSpacer (0);
 
-               add_label_to_sizer (table, _panel, _("Mail password"), true);
-               _mail_password = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_mail_password, 1, wxEXPAND | wxALL);
+               _enable_email = new CheckBox (_panel, _("Email"));
+               table->Add (_enable_email, 1, wxEXPAND | wxALL);
+               table->AddSpacer (0);
 
                add_label_to_sizer (table, _panel, _("Subject"), true);
-               _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
+               _subject = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_subject, 1, wxEXPAND | wxALL);
 
                add_label_to_sizer (table, _panel, _("From address"), true);
-               _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_kdm_from, 1, wxEXPAND | wxALL);
+               _from = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_from, 1, wxEXPAND | wxALL);
+
+               add_label_to_sizer (table, _panel, _("To address"), true);
+               _to = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_to, 1, wxEXPAND | wxALL);
 
                vector<string> columns;
                columns.push_back (wx_to_std (_("Address")));
                add_label_to_sizer (table, _panel, _("CC addresses"), true);
-               _kdm_cc = new EditableList<string, EmailDialog> (
+               _cc = new EditableList<string, EmailDialog> (
                        _panel,
                        columns,
-                       bind (&Config::kdm_cc, Config::instance()),
-                       bind (&Config::set_kdm_cc, Config::instance(), _1),
+                       bind (&Config::notification_cc, Config::instance()),
+                       bind (&Config::set_notification_cc, Config::instance(), _1),
                        bind (&column, _1)
                        );
-               table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
+               table->Add (_cc, 1, wxEXPAND | wxALL);
 
                add_label_to_sizer (table, _panel, _("BCC address"), true);
-               _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
+               _bcc = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_bcc, 1, wxEXPAND | wxALL);
 
-               _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
-               _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
+               _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
+               _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
 
-               _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
-               _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
+               _reset_email = new Button (_panel, _("Reset to default subject and text"));
+               _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
 
-               _kdm_cc->layout ();
+               _cc->layout ();
 
-               _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));
-       }
+               _enable_message_box->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX));
+               _enable_email->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL));
 
-       void config_changed ()
-       {
-               Config* config = Config::instance ();
+               _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
+               _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
+               _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
+               _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
+               _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
+               _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
 
-               checked_set (_mail_server, config->mail_server ());
-               checked_set (_mail_port, config->mail_port ());
-               checked_set (_mail_user, config->mail_user ());
-               checked_set (_mail_password, config->mail_password ());
-               checked_set (_kdm_subject, config->kdm_subject ());
-               checked_set (_kdm_from, config->kdm_from ());
-               checked_set (_kdm_bcc, config->kdm_bcc ());
-               checked_set (_kdm_email, Config::instance()->kdm_email ());
+               update_sensitivity ();
        }
 
-       void mail_server_changed ()
+       void update_sensitivity ()
        {
-               Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
+               bool const s = _enable_email->GetValue();
+               _subject->Enable(s);
+               _from->Enable(s);
+               _to->Enable(s);
+               _cc->Enable(s);
+               _bcc->Enable(s);
+               _email->Enable(s);
+               _reset_email->Enable(s);
        }
 
-       void mail_port_changed ()
+       void config_changed ()
        {
-               Config::instance()->set_mail_port (_mail_port->GetValue ());
-       }
+               Config* config = Config::instance ();
 
-       void mail_user_changed ()
-       {
-               Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
+               checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
+               checked_set (_enable_email, config->notification(Config::EMAIL));
+               checked_set (_subject, config->notification_subject ());
+               checked_set (_from, config->notification_from ());
+               checked_set (_to, config->notification_to ());
+               checked_set (_bcc, config->notification_bcc ());
+               checked_set (_email, Config::instance()->notification_email ());
+
+               update_sensitivity ();
        }
 
-       void mail_password_changed ()
+       void notification_subject_changed ()
        {
-               Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
+               Config::instance()->set_notification_subject (wx_to_std (_subject->GetValue ()));
        }
 
-       void kdm_subject_changed ()
+       void notification_from_changed ()
        {
-               Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
+               Config::instance()->set_notification_from (wx_to_std (_from->GetValue ()));
        }
 
-       void kdm_from_changed ()
+       void notification_to_changed ()
        {
-               Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
+               Config::instance()->set_notification_to (wx_to_std (_to->GetValue ()));
        }
 
-       void kdm_bcc_changed ()
+       void notification_bcc_changed ()
        {
-               Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
+               Config::instance()->set_notification_bcc (wx_to_std (_bcc->GetValue ()));
        }
 
-       void kdm_email_changed ()
+       void notification_email_changed ()
        {
-               if (_kdm_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_kdm_email (wx_to_std (_kdm_email->GetValue ()));
+               Config::instance()->set_notification_email (wx_to_std (_email->GetValue ()));
        }
 
-       void reset_kdm_email ()
+       void reset_email ()
        {
-               Config::instance()->reset_kdm_email ();
-               checked_set (_kdm_email, Config::instance()->kdm_email ());
-       }
-
-       wxTextCtrl* _mail_server;
-       wxSpinCtrl* _mail_port;
-       wxTextCtrl* _mail_user;
-       wxTextCtrl* _mail_password;
-       wxTextCtrl* _kdm_subject;
-       wxTextCtrl* _kdm_from;
-       EditableList<string, EmailDialog>* _kdm_cc;
-       wxTextCtrl* _kdm_bcc;
-       wxTextCtrl* _kdm_email;
-       wxButton* _reset_kdm_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 ();
+       }
+
+       wxCheckBox* _enable_message_box;
+       wxCheckBox* _enable_email;
+
+       wxTextCtrl* _subject;
+       wxTextCtrl* _from;
+       wxTextCtrl* _to;
+       EditableList<string, EmailDialog>* _cc;
+       wxTextCtrl* _bcc;
+       wxTextCtrl* _email;
+       wxButton* _reset_email;
 };
 
 class CoverSheetPage : public StandardPage
@@ -1361,7 +1222,7 @@ 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)
+               : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
 #else
                : StandardPage (panel_size, border)
 #endif
@@ -1385,7 +1246,7 @@ private:
                _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"));
+               _reset_cover_sheet = new Button (_panel, _("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));
@@ -1429,6 +1290,7 @@ public:
                : StockPage (Kind_Advanced, panel_size, border)
                , _maximum_j2k_bandwidth (0)
                , _allow_any_dcp_frame_rate (0)
+               , _allow_any_container (0)
                , _only_servers_encode (0)
                , _log_general (0)
                , _log_warning (0)
@@ -1447,7 +1309,7 @@ private:
                flags |= wxALIGN_RIGHT;
                text += wxT (":");
 #endif
-               wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
+               wxStaticText* m = new StaticText (parent, text);
                table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
        }
 
@@ -1466,14 +1328,33 @@ private:
                        table->Add (s, 1);
                }
 
-               _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
+               _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
                table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
-               _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
+               _allow_any_container = new CheckBox (_panel, _("Allow non-standard container ratios"));
+               table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
+               table->AddSpacer (0);
+
+               wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
+               wxFont font = restart->GetFont();
+               font.SetStyle (wxFONTSTYLE_ITALIC);
+               font.SetPointSize (font.GetPointSize() - 1);
+               restart->SetFont (font);
+               table->AddSpacer (0);
+
+               _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
                table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
+               {
+                       add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true);
+                       wxBoxSizer* 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"));
                        dcp::NameFormat::Map titles;
@@ -1507,26 +1388,26 @@ private:
                {
                        add_top_aligned_label_to_sizer (table, _panel, _("Log"));
                        wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
-                       _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
+                       _log_general = new CheckBox (_panel, _("General"));
                        t->Add (_log_general, 1, wxEXPAND | wxALL);
-                       _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
+                       _log_warning = new CheckBox (_panel, _("Warnings"));
                        t->Add (_log_warning, 1, wxEXPAND | wxALL);
-                       _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
+                       _log_error = new CheckBox (_panel, _("Errors"));
                        t->Add (_log_error, 1, wxEXPAND | wxALL);
                        /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
-                       _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
+                       _log_timing = new CheckBox (_panel, S_("Config|Timing"));
                        t->Add (_log_timing, 1, wxEXPAND | wxALL);
-                       _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
+                       _log_debug_decode = new CheckBox (_panel, _("Debug: decode"));
                        t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
-                       _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
+                       _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
                        t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
-                       _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
+                       _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
                        t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
                        table->Add (t, 0, wxALL, 6);
                }
 
 #ifdef DCPOMATIC_WINDOWS
-               _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
+               _win32_console = new CheckBox (_panel, _("Open console window"));
                table->Add (_win32_console, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 #endif
@@ -1534,7 +1415,9 @@ private:
                _maximum_j2k_bandwidth->SetRange (1, 1000);
                _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));
+               _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_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));
                _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));
@@ -1555,6 +1438,7 @@ private:
 
                checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
                checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
+               checked_set (_allow_any_container, config->allow_any_container ());
                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);
@@ -1563,6 +1447,7 @@ private:
                checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
                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 (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
 #ifdef DCPOMATIC_WINDOWS
                checked_set (_win32_console, config->win32_console());
 #endif
@@ -1573,11 +1458,21 @@ private:
                Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
        }
 
+       void frames_in_memory_multiplier_changed ()
+       {
+               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 ());
        }
 
+       void allow_any_container_changed ()
+       {
+               Config::instance()->set_allow_any_container (_allow_any_container->GetValue ());
+       }
+
        void only_servers_encode_changed ()
        {
                Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
@@ -1628,7 +1523,9 @@ private:
 #endif
 
        wxSpinCtrl* _maximum_j2k_bandwidth;
+       wxSpinCtrl* _frames_in_memory_multiplier;
        wxCheckBox* _allow_any_dcp_frame_rate;
+       wxCheckBox* _allow_any_container;
        wxCheckBox* _only_servers_encode;
        NameFormatEditor* _dcp_metadata_filename_format;
        NameFormatEditor* _dcp_asset_filename_format;
@@ -1654,7 +1551,7 @@ create_full_config_dialog ()
           the containing window doesn't shrink too much when we select those panels.
           This is obviously an unpleasant hack.
        */
-       wxSize ps = wxSize (520, -1);
+       wxSize ps = wxSize (600, -1);
        int const border = 16;
 #else
        wxSize ps = wxSize (-1, -1);
@@ -1666,7 +1563,10 @@ create_full_config_dialog ()
        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 AccountsPage (ps, border));
+       e->AddPage (new NotificationsPage (ps, border));
        e->AddPage (new CoverSheetPage (ps, border));
        e->AddPage (new AdvancedPage (ps, border));
        return e;