Remove the "simple" UI (#1868).
[dcpomatic.git] / src / wx / full_config_dialog.cc
index 8007e6bdaaadee21aa8aa169baced9787d494a64..c41236096c9466d2896b23cbe0e202e58297bca3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include "name_format_editor.h"
 #include "nag_dialog.h"
 #include "config_move_dialog.h"
+#include "config_dialog.h"
+#include "static_text.h"
+#include "check_box.h"
+#include "dcpomatic_button.h"
+#include "password_entry.h"
 #include "lib/config.h"
 #include "lib/ratio.h"
 #include "lib/filter.h"
@@ -66,101 +71,16 @@ using boost::bind;
 using boost::shared_ptr;
 using boost::function;
 using boost::optional;
+#if BOOST_VERSION >= 106100
+using namespace boost::placeholders;
+#endif
 using dcp::locale_convert;
 
-static
-void
-do_nothing ()
-{
-
-}
-
-class Page
-{
-public:
-       Page (wxSize panel_size, int border)
-               : _border (border)
-               , _panel (0)
-               , _panel_size (panel_size)
-               , _window_exists (false)
-       {
-               _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
-       }
-
-       virtual ~Page () {}
-
-protected:
-       wxWindow* create_window (wxWindow* parent)
-       {
-               _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
-               wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
-               _panel->SetSizer (s);
-
-               setup ();
-               _window_exists = true;
-               config_changed ();
-
-               _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
-
-               return _panel;
-       }
-
-       int _border;
-       wxPanel* _panel;
-
-private:
-       virtual void config_changed () = 0;
-       virtual void setup () = 0;
-
-       void config_changed_wrapper ()
-       {
-               if (_window_exists) {
-                       config_changed ();
-               }
-       }
-
-       void window_destroyed ()
-       {
-               _window_exists = false;
-       }
-
-       wxSize _panel_size;
-       boost::signals2::scoped_connection _config_connection;
-       bool _window_exists;
-};
-
-class StockPage : public wxStockPreferencesPage, public Page
-{
-public:
-       StockPage (Kind kind, wxSize panel_size, int border)
-               : wxStockPreferencesPage (kind)
-               , Page (panel_size, border)
-       {}
-
-       wxWindow* CreateWindow (wxWindow* parent)
-       {
-               return create_window (parent);
-       }
-};
-
-class StandardPage : public wxPreferencesPage, public Page
-{
-public:
-       StandardPage (wxSize panel_size, int border)
-               : Page (panel_size, border)
-       {}
-
-       wxWindow* CreateWindow (wxWindow* parent)
-       {
-               return create_window (parent);
-       }
-};
-
-class GeneralPage : public StockPage
+class FullGeneralPage : public GeneralPage
 {
 public:
-       GeneralPage (wxSize panel_size, int border)
-               : StockPage (Kind_General, panel_size, border)
+       FullGeneralPage (wxSize panel_size, int border)
+               : GeneralPage (panel_size, border)
        {}
 
 private:
@@ -170,38 +90,7 @@ private:
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                int r = 0;
-               _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
-               table->Add (_set_language, wxGBPosition (r, 0));
-               _language = new wxChoice (_panel, wxID_ANY);
-               vector<pair<string, string> > languages;
-               languages.push_back (make_pair ("Čeština", "cs_CZ"));
-               languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
-               languages.push_back (make_pair ("Dansk", "da_DK"));
-               languages.push_back (make_pair ("Deutsch", "de_DE"));
-               languages.push_back (make_pair ("English", "en_GB"));
-               languages.push_back (make_pair ("Español", "es_ES"));
-               languages.push_back (make_pair ("Français", "fr_FR"));
-               languages.push_back (make_pair ("Italiano", "it_IT"));
-               languages.push_back (make_pair ("Nederlands", "nl_NL"));
-               languages.push_back (make_pair ("Русский", "ru_RU"));
-               languages.push_back (make_pair ("Polski", "pl_PL"));
-               languages.push_back (make_pair ("Português europeu", "pt_PT"));
-               languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
-               languages.push_back (make_pair ("Svenska", "sv_SE"));
-               languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
-               languages.push_back (make_pair ("українська мова", "uk_UA"));
-               checked_set (_language, languages);
-               table->Add (_language, wxGBPosition (r, 1));
-               ++r;
-
-               wxStaticText* restart = add_label_to_sizer (
-                       table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
-                       );
-               wxFont font = restart->GetFont();
-               font.SetStyle (wxFONTSTYLE_ITALIC);
-               font.SetPointSize (font.GetPointSize() - 1);
-               restart->SetFont (font);
-               ++r;
+               add_language_controls (table, 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);
@@ -214,194 +103,71 @@ private:
                ++r;
 
                add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
-               _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true);
+               _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, true);
                table->Add (_config_file, wxGBPosition (r, 1));
                ++r;
 
                add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
-               _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
+               _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, true);
                table->Add (_cinemas_file, wxGBPosition (r, 1));
+               Button* export_cinemas = new Button (_panel, _("Export..."));
+               table->Add (export_cinemas, wxGBPosition (r, 2));
                ++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"));
+               _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;
 
-               _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
-               table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
-               ++r;
-
-               _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
-               table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
-               ++r;
-
-               wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-               bottom_table->AddGrowableCol (1, 1);
-
-               add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
-               _issuer = new wxTextCtrl (_panel, wxID_ANY);
-               bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
-
-               add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
-               _creator = new wxTextCtrl (_panel, wxID_ANY);
-               bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
-
-               table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
-               ++r;
+               add_update_controls (table, r);
 
-                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));
-               _config_file->Bind          (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::config_file_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));
+               _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::config_file_changed,   this));
+               _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::cinemas_file_changed,  this));
 
                _master_encoding_threads->SetRange (1, 128);
-               _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
+               _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 (&GeneralPage::server_encoding_threads_changed, this));
+               _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 (&GeneralPage::analyse_ebur128_changed, this));
+               _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
 #endif
-               _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_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
-               _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
+               _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
        }
 
        void config_changed ()
        {
                Config* config = Config::instance ();
 
-               checked_set (_set_language, static_cast<bool>(config->language()));
-
-               /* Backwards compatibility of config file */
-
-               map<string, string> compat_map;
-               compat_map["fr"] = "fr_FR";
-               compat_map["it"] = "it_IT";
-               compat_map["es"] = "es_ES";
-               compat_map["sv"] = "sv_SE";
-               compat_map["de"] = "de_DE";
-               compat_map["nl"] = "nl_NL";
-               compat_map["ru"] = "ru_RU";
-               compat_map["pl"] = "pl_PL";
-               compat_map["da"] = "da_DK";
-               compat_map["pt"] = "pt_PT";
-               compat_map["sk"] = "sk_SK";
-               compat_map["cs"] = "cs_CZ";
-               compat_map["uk"] = "uk_UA";
-
-               string lang = config->language().get_value_or ("en_GB");
-               if (compat_map.find (lang) != compat_map.end ()) {
-                       lang = compat_map[lang];
-               }
-
-               checked_set (_language, lang);
-
                checked_set (_master_encoding_threads, config->master_encoding_threads ());
                checked_set (_server_encoding_threads, config->server_encoding_threads ());
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
                checked_set (_analyse_ebur128, config->analyse_ebur128 ());
 #endif
                checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
-               checked_set (_check_for_updates, config->check_for_updates ());
-               checked_set (_check_for_test_updates, config->check_for_test_updates ());
-               checked_set (_issuer, config->dcp_issuer ());
-               checked_set (_creator, config->dcp_creator ());
                checked_set (_config_file, config->config_file());
                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 ());
+               GeneralPage::config_changed ();
        }
 
-       void set_language_changed ()
+       void export_cinemas_file ()
        {
-               setup_sensitivity ();
-               if (_set_language->GetValue ()) {
-                       language_changed ();
-               } else {
-                       Config::instance()->unset_language ();
-               }
-       }
+               wxFileDialog* d = new wxFileDialog (
+                       _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT ("XML files (*.xml)|*.xml"),
+                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+                );
 
-       void language_changed ()
-       {
-               int const sel = _language->GetSelection ();
-               if (sel != -1) {
-                       Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
-               } else {
-                       Config::instance()->unset_language ();
+               if (d->ShowModal () == wxID_OK) {
+                       boost::filesystem::copy_file (Config::instance()->cinemas_file(), wx_to_std(d->GetPath()));
                }
+               d->Destroy ();
        }
 
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
@@ -416,16 +182,6 @@ private:
                Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
        }
 
-       void check_for_updates_changed ()
-       {
-               Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
-       }
-
-       void check_for_test_updates_changed ()
-       {
-               Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
-       }
-
        void master_encoding_threads_changed ()
        {
                Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
@@ -436,16 +192,6 @@ private:
                Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
        }
 
-       void issuer_changed ()
-       {
-               Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
-       }
-
-       void creator_changed ()
-       {
-               Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
-       }
-
        void config_file_changed ()
        {
                Config* config = Config::instance();
@@ -477,45 +223,21 @@ 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* _master_encoding_threads;
        wxSpinCtrl* _server_encoding_threads;
        FilePickerCtrl* _config_file;
        FilePickerCtrl* _cinemas_file;
-       wxCheckBox* _preview_sound;
-       wxChoice* _preview_sound_output;
 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
        wxCheckBox* _analyse_ebur128;
 #endif
        wxCheckBox* _automatic_audio_analysis;
-       wxCheckBox* _check_for_updates;
-       wxCheckBox* _check_for_test_updates;
-       wxTextCtrl* _issuer;
-       wxTextCtrl* _creator;
 };
 
-class DefaultsPage : public StandardPage
+class DefaultsPage : public Page
 {
 public:
        DefaultsPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
        wxString GetName () const
@@ -538,15 +260,15 @@ private:
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                {
-                       add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
+                       add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _still_length = new wxSpinCtrl (_panel);
                        s->Add (_still_length);
-                       add_label_to_sizer (s, _panel, _("s"), false);
+                       add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
-               add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
+               add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
 #ifdef DCPOMATIC_USE_OWN_PICKER
                _directory = new DirPickerCtrl (_panel);
 #else
@@ -554,54 +276,51 @@ private:
 #endif
                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..."));
+               add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _isdcf_metadata_button = new Button (_panel, _("Edit..."));
                table->Add (_isdcf_metadata_button);
 
-               add_label_to_sizer (table, _panel, _("Default container"), true);
+               add_label_to_sizer (table, _panel, _("Default container"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _container = new wxChoice (_panel, wxID_ANY);
                table->Add (_container);
 
-               add_label_to_sizer (table, _panel, _("Default scale-to"), true);
-               _scale_to = new wxChoice (_panel, wxID_ANY);
-               table->Add (_scale_to);
-
-               add_label_to_sizer (table, _panel, _("Default content type"), true);
+               add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _dcp_content_type = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_content_type);
 
-               add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
+               add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_audio_channels);
 
                {
-                       add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
+                       add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _j2k_bandwidth = new wxSpinCtrl (_panel);
                        s->Add (_j2k_bandwidth);
-                       add_label_to_sizer (s, _panel, _("Mbit/s"), false);
+                       add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
                {
-                       add_label_to_sizer (table, _panel, _("Default audio delay"), true);
+                       add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _audio_delay = new wxSpinCtrl (_panel);
                        s->Add (_audio_delay);
-                       add_label_to_sizer (s, _panel, _("ms"), false);
+                       add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        table->Add (s, 1);
                }
 
-               add_label_to_sizer (table, _panel, _("Default standard"), true);
+               add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                _standard = new wxChoice (_panel, wxID_ANY);
                table->Add (_standard);
 
-               add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
+               add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
 #ifdef DCPOMATIC_USE_OWN_PICKER
                _kdm_directory = new DirPickerCtrl (_panel);
 #else
                _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
 #endif
+
                table->Add (_kdm_directory, 1, wxEXPAND);
 
                _still_length->SetRange (1, 3600);
@@ -618,14 +337,6 @@ private:
 
                _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
 
-               _scale_to->Append (_("Guess from content"));
-
-               BOOST_FOREACH (Ratio const * i, Ratio::all()) {
-                       _scale_to->Append (std_to_wx(i->image_nickname()));
-               }
-
-               _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 ()));
                }
@@ -657,17 +368,6 @@ private:
                        }
                }
 
-               vector<Ratio const *> ratios = Ratio::all ();
-               for (size_t i = 0; i < ratios.size(); ++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 ()) {
@@ -734,17 +434,6 @@ private:
                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 ();
@@ -768,17 +457,16 @@ private:
        wxDirPickerCtrl* _kdm_directory;
 #endif
        wxChoice* _container;
-       wxChoice* _scale_to;
        wxChoice* _dcp_content_type;
        wxChoice* _dcp_audio_channels;
        wxChoice* _standard;
 };
 
-class EncodingServersPage : public StandardPage
+class EncodingServersPage : public Page
 {
 public:
        EncodingServersPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
        {}
 
        wxString GetName () const
@@ -796,11 +484,11 @@ 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;
-               columns.push_back (wx_to_std (_("IP address / host name")));
+               vector<EditableListColumn> columns;
+               columns.push_back (EditableListColumn(_("IP address / host name")));
                _servers_list = new EditableList<string, ServerDialog> (
                        _panel,
                        columns,
@@ -834,524 +522,274 @@ private:
        EditableList<string, ServerDialog>* _servers_list;
 };
 
-class CertificateChainEditor : public wxPanel
+class TMSPage : public Page
 {
 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);
-               }
+       TMSPage (wxSize panel_size, int border)
+               : Page (panel_size, border)
+       {}
 
-               {
-                       wxListItem ip;
-                       ip.SetId (1);
-                       ip.SetText (_("Thumbprint"));
-                       ip.SetWidth (340);
+       wxString GetName () const
+       {
+               return _("TMS");
+       }
 
-                       wxFont font = ip.GetFont ();
-                       font.SetFamily (wxFONTFAMILY_TELETYPE);
-                       ip.SetFont (font);
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
+       {
+               return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
 
-                       _certificates->InsertColumn (1, ip);
-               }
+private:
+       void setup ()
+       {
+               _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
+               _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
 
-               certificates_sizer->Add (_certificates, 1, wxEXPAND);
+               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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _tms_password = new PasswordEntry (_panel);
+               table->Add (_tms_password->get_panel(), 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);
+               _upload->Bind (wxEVT_CHECKBOX, boost::bind(&TMSPage::upload_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->Changed.connect (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 (_upload, config->upload_after_make_dcp());
+               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 upload_changed ()
        {
-               _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
-               _sizer->Layout ();
+               Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
        }
 
-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_protocol_changed ()
+       {
+               Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
        }
 
-       void remove_certificate ()
+       void tms_ip_changed ()
        {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
+               Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
+       }
 
-               _certificates->DeleteItem (i);
-               _chain->remove (i);
-               _set (_chain);
+       void tms_path_changed ()
+       {
+               Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
+       }
 
-               update_sensitivity ();
+       void tms_user_changed ()
+       {
+               Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
        }
 
-       void export_certificate ()
+       void tms_password_changed ()
        {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
+               Config::instance()->set_tms_password (_tms_password->get());
+       }
 
-               wxFileDialog* d = new wxFileDialog (
-                       this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
+       CheckBox* _upload;
+       wxChoice* _tms_protocol;
+       wxTextCtrl* _tms_ip;
+       wxTextCtrl* _tms_path;
+       wxTextCtrl* _tms_user;
+       PasswordEntry* _tms_password;
+};
 
-               dcp::CertificateChain::List all = _chain->root_to_leaf ();
-               dcp::CertificateChain::List::iterator j = all.begin ();
-               for (int k = 0; k < i; ++k) {
-                       ++j;
-               }
+static string
+column (string s)
+{
+       return s;
+}
 
-               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);
-                       }
+class EmailPage : public Page
+{
+public:
+       EmailPage (wxSize panel_size, int border)
+               : Page (panel_size, border)
+       {}
 
-                       string const s = j->certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+       wxString GetName () const
+       {
+               return _("Email");
        }
 
-       void update_certificate_list ()
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
        {
-               _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 ()));
+               return wxBitmap ("email", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
 
-                       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"));
-                       }
+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);
 
-                       ++n;
+               add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               {
+                       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+                       _server = new wxTextCtrl (_panel, wxID_ANY);
+                       s->Add (_server, 1, wxEXPAND | wxALL);
+                       add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       _port = new wxSpinCtrl (_panel, wxID_ANY);
+                       _port->SetRange (0, 65535);
+                       s->Add (_port);
+                       add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       _protocol = new wxChoice (_panel, wxID_ANY);
+                       /* Make sure this matches the switches in config_changed and port_changed below */
+                       _protocol->Append (_("Auto"));
+                       _protocol->Append (_("Plain"));
+                       _protocol->Append (_("STARTTLS"));
+                       _protocol->Append (_("SSL"));
+                       s->Add (_protocol);
+                       table->Add (s, 1, wxEXPAND | wxALL);
                }
 
-               static wxColour normal = _private_key_bad->GetForegroundColour ();
+               add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _user = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_user, 1, wxEXPAND | wxALL);
 
-               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));
-               }
+               add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _password = new PasswordEntry (_panel);
+               table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
+
+               _server->Bind (wxEVT_TEXT, boost::bind (&EmailPage::server_changed, this));
+               _port->Bind (wxEVT_SPINCTRL, boost::bind (&EmailPage::port_changed, this));
+               _protocol->Bind (wxEVT_CHOICE, boost::bind (&EmailPage::protocol_changed, this));
+               _user->Bind (wxEVT_TEXT, boost::bind (&EmailPage::user_changed, this));
+               _password->Changed.connect (boost::bind (&EmailPage::password_changed, this));
        }
 
-       void remake_certificates ()
+       void config_changed ()
        {
-               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 ();
+               Config* config = Config::instance ();
 
-               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 ();
+               checked_set (_server, config->mail_server ());
+               checked_set (_port, config->mail_port ());
+               switch (config->mail_protocol()) {
+               case EMAIL_PROTOCOL_AUTO:
+                       checked_set (_protocol, 0);
+                       break;
+               case EMAIL_PROTOCOL_PLAIN:
+                       checked_set (_protocol, 1);
+                       break;
+               case EMAIL_PROTOCOL_STARTTLS:
+                       checked_set (_protocol, 2);
+                       break;
+               case EMAIL_PROTOCOL_SSL:
+                       checked_set (_protocol, 3);
+                       break;
                }
-
-               if (all.size() >= 2) {
-                       /* Have a leaf */
-                       leaf_common_name = chain->leaf().subject_common_name ();
-               }
-
-               if (all.size() >= 3) {
-                       /* Have an intermediate */
-                       dcp::CertificateChain::List::iterator i = all.begin ();
-                       ++i;
-                       intermediate_common_name = i->subject_common_name ();
-               }
-
-               _nag_remake ();
-
-               MakeChainDialog* d = new MakeChainDialog (
-                       this,
-                       subject_organization_name,
-                       subject_organizational_unit_name,
-                       root_common_name,
-                       intermediate_common_name,
-                       leaf_common_name
-                       );
-
-               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 ()
-                                       )
-                               );
-
-                       _set (_chain);
-                       update_certificate_list ();
-                       update_private_key ();
-               }
-
-               d->Destroy ();
+               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 protocol_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 ()));
-                       }
+               switch (_protocol->GetSelection()) {
+               case 0:
+                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_AUTO);
+                       break;
+               case 1:
+                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_PLAIN);
+                       break;
+               case 2:
+                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_STARTTLS);
+                       break;
+               case 3:
+                       Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_SSL);
+                       break;
                }
-
-               d->Destroy ();
-
-               update_sensitivity ();
        }
 
-       void export_private_key ()
+       void user_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);
-                       }
+               Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
+       }
 
-                       string const s = _chain->key().get ();
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
+       void password_changed ()
+       {
+               Config::instance()->set_mail_password(_password->get());
        }
 
-       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;
+       wxChoice* _protocol;
+       wxTextCtrl* _user;
+       PasswordEntry* _password;
 };
 
-class KeysPage : public StandardPage
+class KDMEmailPage : public Page
 {
 public:
-       KeysPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
-       {}
-
-       wxString GetName () const
-       {
-               return _("Keys");
-       }
 
+       KDMEmailPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
-       {
-               return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
-       }
+               /* We have to force both width and height of this one */
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
+#else
+               : Page (panel_size, border)
 #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)
-                       );
-
-               _panel->GetSizer()->Add (_signer);
-
-               _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)
-                       );
-
-               _panel->GetSizer()->Add (_decryption);
-
-               _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_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 ()
-       {
-               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);
-                       }
-
-                       string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               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
-{
-public:
-       TMSPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
        {}
 
        wxString GetName () const
        {
-               return _("TMS");
+               return _("KDM Email");
        }
 
 #ifdef DCPOMATIC_OSX
        wxBitmap GetLargeIcon () const
        {
-               return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
+               return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
        }
 #endif
 
@@ -1360,109 +798,118 @@ private:
        {
                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);
+               _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _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, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _subject = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_subject, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("IP address"), true);
-               _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_ip, 1, wxEXPAND);
+               add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _from = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_from, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("Target path"), true);
-               _tms_path = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_path, 1, wxEXPAND);
+               vector<EditableListColumn> columns;
+               columns.push_back (EditableListColumn(_("Address")));
+               add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _cc = new EditableList<string, EmailDialog> (
+                       _panel,
+                       columns,
+                       bind (&Config::kdm_cc, Config::instance()),
+                       bind (&Config::set_kdm_cc, Config::instance(), _1),
+                       bind (&column, _1)
+                       );
+               table->Add (_cc, 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, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _bcc = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_bcc, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("Password"), true);
-               _tms_password = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_tms_password, 1, wxEXPAND);
+               _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
+               _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
 
-               _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
-               _tms_protocol->Append (_("FTP (for Dolby)"));
+               _reset_email = new Button (_panel, _("Reset to default subject and text"));
+               _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
 
-               _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));
+               _cc->layout ();
+
+               _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 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 (_subject, config->kdm_subject ());
+               checked_set (_from, config->kdm_from ());
+               checked_set (_bcc, config->kdm_bcc ());
+               checked_set (_email, Config::instance()->kdm_email ());
        }
 
-       void tms_protocol_changed ()
+       void kdm_subject_changed ()
        {
-               Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
+               Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
        }
 
-       void tms_ip_changed ()
+       void kdm_from_changed ()
        {
-               Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
+               Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
        }
 
-       void tms_path_changed ()
+       void kdm_bcc_changed ()
        {
-               Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
+               Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
        }
 
-       void tms_user_changed ()
+       void kdm_email_changed ()
        {
-               Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
+               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 tms_password_changed ()
+       void reset_email ()
        {
-               Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
+               Config::instance()->reset_kdm_email ();
+               checked_set (_email, Config::instance()->kdm_email ());
        }
 
-       wxChoice* _tms_protocol;
-       wxTextCtrl* _tms_ip;
-       wxTextCtrl* _tms_path;
-       wxTextCtrl* _tms_user;
-       wxTextCtrl* _tms_password;
+       wxTextCtrl* _subject;
+       wxTextCtrl* _from;
+       EditableList<string, EmailDialog>* _cc;
+       wxTextCtrl* _bcc;
+       wxTextCtrl* _email;
+       wxButton* _reset_email;
 };
 
-static string
-column (string s)
-{
-       return s;
-}
-
-class KDMEmailPage : public StandardPage
+class NotificationsPage : public Page
 {
 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)
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
 #else
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
 #endif
        {}
 
        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
 
@@ -1473,157 +920,155 @@ 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);
-               }
+               _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 user name"), true);
-               _mail_user = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_mail_user, 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, _("Mail password"), true);
-               _mail_password = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_mail_password, 1, wxEXPAND | wxALL);
+               add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _subject = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_subject, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("Subject"), true);
-               _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
+               add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _from = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_from, 1, wxEXPAND | wxALL);
 
-               add_label_to_sizer (table, _panel, _("From address"), true);
-               _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
-               table->Add (_kdm_from, 1, wxEXPAND | wxALL);
+               add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _to = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_to, 1, wxEXPAND | wxALL);
 
-               vector<string> columns;
-               columns.push_back (wx_to_std (_("Address")));
-               add_label_to_sizer (table, _panel, _("CC addresses"), true);
-               _kdm_cc = new EditableList<string, EmailDialog> (
+               vector<EditableListColumn> columns;
+               columns.push_back (EditableListColumn(_("Address")));
+               add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _cc = new EditableList<string, EmailDialog> (
                        _panel,
                        columns,
-                       bind (&Config::kdm_cc, Config::instance()),
-                       bind (&Config::set_kdm_cc, Config::instance(), _1),
+                       bind (&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);
+               add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _bcc = new wxTextCtrl (_panel, wxID_ANY);
+               table->Add (_bcc, 1, wxEXPAND | wxALL);
 
-               _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
+class CoverSheetPage : public Page
 {
 public:
 
        CoverSheetPage (wxSize panel_size, int border)
 #ifdef DCPOMATIC_OSX
                /* We have to force both width and height of this one */
-               : StandardPage (wxSize (480, 128), border)
+               : Page (wxSize (panel_size.GetWidth(), 128), border)
 #else
-               : StandardPage (panel_size, border)
+               : Page (panel_size, border)
 #endif
        {}
 
@@ -1645,7 +1090,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));
@@ -1679,26 +1124,158 @@ private:
 };
 
 
+class IdentifiersPage : public Page
+{
+public:
+       IdentifiersPage (wxSize panel_size, int border)
+               : Page (panel_size, border)
+       {}
+
+       wxString GetName () const
+       {
+               return _("Identifiers");
+       }
+
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
+       {
+               return wxBitmap ("identifiers", 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);
+
+               add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _issuer = new wxTextCtrl (_panel, wxID_ANY);
+               _issuer->SetToolTip (_("This will be written to the DCP's XML files as the <Issuer>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
+               table->Add (_issuer, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _creator = new wxTextCtrl (_panel, wxID_ANY);
+               _creator->SetToolTip (_("This will be written to the DCP's XML files as the <Creator>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
+               table->Add (_creator, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _company_name = new wxTextCtrl (_panel, wxID_ANY);
+               _company_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'company name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_company_name, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _product_name = new wxTextCtrl (_panel, wxID_ANY);
+               _product_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'product name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_product_name, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _product_version = new wxTextCtrl (_panel, wxID_ANY);
+               _product_version->SetToolTip (_("This will be written to the DCP's MXF files as the 'product version'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_product_version, 1, wxALL | wxEXPAND);
+
+               add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
+               _j2k_comment->SetToolTip (_("This will be written to the DCP's JPEG2000 data as a comment.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
+               table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
+
+               _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
+
+               _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
+               _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
+               _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
+               _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
+               _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
+               _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
+       }
+
+       void config_changed ()
+       {
+               Config* config = Config::instance ();
+               checked_set (_issuer, config->dcp_issuer ());
+               checked_set (_creator, config->dcp_creator ());
+               checked_set (_company_name, config->dcp_company_name ());
+               checked_set (_product_name, config->dcp_product_name ());
+               checked_set (_product_version, config->dcp_product_version ());
+               checked_set (_j2k_comment, config->dcp_j2k_comment ());
+       }
+
+       void issuer_changed ()
+       {
+               Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
+       }
+
+       void creator_changed ()
+       {
+               Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
+       }
+
+       void company_name_changed ()
+       {
+               Config::instance()->set_dcp_company_name (wx_to_std(_company_name->GetValue()));
+       }
+
+       void product_name_changed ()
+       {
+               Config::instance()->set_dcp_product_name (wx_to_std(_product_name->GetValue()));
+       }
+
+       void product_version_changed ()
+       {
+               Config::instance()->set_dcp_product_version (wx_to_std(_product_version->GetValue()));
+       }
+
+       void j2k_comment_changed ()
+       {
+               Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
+       }
+
+       wxTextCtrl* _issuer;
+       wxTextCtrl* _creator;
+       wxTextCtrl* _company_name;
+       wxTextCtrl* _product_name;
+       wxTextCtrl* _product_version;
+       wxTextCtrl* _j2k_comment;
+};
+
+
 /** @class AdvancedPage
  *  @brief Advanced page of the preferences dialog.
  */
-class AdvancedPage : public StockPage
+class AdvancedPage : public Page
 {
 public:
        AdvancedPage (wxSize panel_size, int border)
-               : StockPage (Kind_Advanced, panel_size, border)
+               : Page (panel_size, border)
                , _maximum_j2k_bandwidth (0)
                , _allow_any_dcp_frame_rate (0)
+               , _allow_any_container (0)
+               , _show_experimental_audio_processors (0)
                , _only_servers_encode (0)
                , _log_general (0)
                , _log_warning (0)
                , _log_error (0)
                , _log_timing (0)
-               , _log_debug_decode (0)
+               , _log_debug_threed (0)
                , _log_debug_encode (0)
                , _log_debug_email (0)
+               , _log_debug_video_view (0)
+               , _log_debug_player (0)
+               , _log_debug_audio_analysis (0)
        {}
 
+       wxString GetName () const
+       {
+               return _("Advanced");
+       }
+
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
+       {
+               return wxBitmap ("advanced", wxBITMAP_TYPE_PNG_RESOURCE);
+       }
+#endif
+
 private:
        void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
        {
@@ -1707,7 +1284,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);
        }
 
@@ -1718,7 +1295,7 @@ private:
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
                {
-                       add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
+                       add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
                        wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                        _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
                        s->Add (_maximum_j2k_bandwidth, 1);
@@ -1726,18 +1303,49 @@ private:
                        table->Add (s, 1);
                }
 
-               _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
+               add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _video_display_mode = new wxChoice (_panel, wxID_ANY);
+               table->Add (_video_display_mode);
+
+               wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
+               wxFont font = restart->GetFont();
+               font.SetStyle (wxFONTSTYLE_ITALIC);
+               font.SetPointSize (font.GetPointSize() - 1);
+               restart->SetFont (font);
+               table->AddSpacer (0);
+
+               _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
                table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
-               _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
+               _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
+               table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
+               table->AddSpacer (0);
+
+               restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
+               restart->SetFont (font);
+               table->AddSpacer (0);
+
+               _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
+               table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
+               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, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+                       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;
-                       titles['t'] = "type (cpl/pkl)";
+                       titles['t'] = wx_to_std (_("type (cpl/pkl)"));
                        dcp::NameFormat::Map examples;
                        examples['t'] = "cpl";
                        _dcp_metadata_filename_format = new NameFormatEditor (
@@ -1749,10 +1357,10 @@ private:
                {
                        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";
+                       titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
+                       titles['r'] = wx_to_std (_("reel number"));
+                       titles['n'] = wx_to_std (_("number of reels"));
+                       titles['c'] = wx_to_std (_("content filename"));
                        dcp::NameFormat::Map examples;
                        examples['t'] = "j2c";
                        examples['r'] = "1";
@@ -1767,43 +1375,58 @@ 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"));
-                       t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
-                       _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
+                       _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
+                       t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
+                       _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);
+                       _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
+                       t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
+                       _log_debug_player = new CheckBox (_panel, _("Debug: player"));
+                       t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
+                       _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
+                       t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
                        table->Add (t, 0, wxALL, 6);
                }
 
 #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
 
                _maximum_j2k_bandwidth->SetRange (1, 1000);
                _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
+               _video_display_mode->Append (_("Simple (safer)"));
+               _video_display_mode->Append (_("OpenGL (faster)"));
+               _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
                _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
+               _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
+               _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
                _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
+               _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
                _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_threed->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
                _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
                _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_video_view->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_player->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
+               _log_debug_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
 #ifdef DCPOMATIC_WINDOWS
                _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
 #endif
@@ -1814,15 +1437,29 @@ private:
                Config* config = Config::instance ();
 
                checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
+               switch (config->video_view_type()) {
+               case Config::VIDEO_VIEW_SIMPLE:
+                       checked_set (_video_display_mode, 0);
+                       break;
+               case Config::VIDEO_VIEW_OPENGL:
+                       checked_set (_video_display_mode, 1);
+                       break;
+               }
                checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
+               checked_set (_allow_any_container, config->allow_any_container ());
+               checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
                checked_set (_only_servers_encode, config->only_servers_encode ());
                checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
                checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
                checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
                checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
-               checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
+               checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREED);
                checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
                checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
+               checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
+               checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
+               checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
+               checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
 #ifdef DCPOMATIC_WINDOWS
                checked_set (_win32_console, config->win32_console());
 #endif
@@ -1833,11 +1470,35 @@ private:
                Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
        }
 
+       void video_display_mode_changed ()
+       {
+               if (_video_display_mode->GetSelection() == 0) {
+                       Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
+               } else {
+                       Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
+               }
+       }
+
+       void frames_in_memory_multiplier_changed ()
+       {
+               Config::instance()->set_frames_in_memory_multiplier (_frames_in_memory_multiplier->GetValue());
+       }
+
        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 show_experimental_audio_processors_changed ()
+       {
+               Config::instance()->set_show_experimental_audio_processors (_show_experimental_audio_processors->GetValue ());
+       }
+
        void only_servers_encode_changed ()
        {
                Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
@@ -1868,8 +1529,8 @@ private:
                if (_log_timing->GetValue ()) {
                        types |= LogEntry::TYPE_TIMING;
                }
-               if (_log_debug_decode->GetValue ()) {
-                       types |= LogEntry::TYPE_DEBUG_DECODE;
+               if (_log_debug_threed->GetValue ()) {
+                       types |= LogEntry::TYPE_DEBUG_THREED;
                }
                if (_log_debug_encode->GetValue ()) {
                        types |= LogEntry::TYPE_DEBUG_ENCODE;
@@ -1877,6 +1538,15 @@ private:
                if (_log_debug_email->GetValue ()) {
                        types |= LogEntry::TYPE_DEBUG_EMAIL;
                }
+               if (_log_debug_video_view->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
+               }
+               if (_log_debug_player->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_PLAYER;
+               }
+               if (_log_debug_audio_analysis->GetValue()) {
+                       types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
+               }
                Config::instance()->set_log_types (types);
        }
 
@@ -1888,7 +1558,11 @@ private:
 #endif
 
        wxSpinCtrl* _maximum_j2k_bandwidth;
+       wxChoice* _video_display_mode;
+       wxSpinCtrl* _frames_in_memory_multiplier;
        wxCheckBox* _allow_any_dcp_frame_rate;
+       wxCheckBox* _allow_any_container;
+       wxCheckBox* _show_experimental_audio_processors;
        wxCheckBox* _only_servers_encode;
        NameFormatEditor* _dcp_metadata_filename_format;
        NameFormatEditor* _dcp_asset_filename_format;
@@ -1896,9 +1570,12 @@ private:
        wxCheckBox* _log_warning;
        wxCheckBox* _log_error;
        wxCheckBox* _log_timing;
-       wxCheckBox* _log_debug_decode;
+       wxCheckBox* _log_debug_threed;
        wxCheckBox* _log_debug_encode;
        wxCheckBox* _log_debug_email;
+       wxCheckBox* _log_debug_video_view;
+       wxCheckBox* _log_debug_player;
+       wxCheckBox* _log_debug_audio_analysis;
 #ifdef DCPOMATIC_WINDOWS
        wxCheckBox* _win32_console;
 #endif
@@ -1914,20 +1591,24 @@ 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 (700, -1);
        int const border = 16;
 #else
        wxSize ps = wxSize (-1, -1);
        int const border = 8;
 #endif
 
-       e->AddPage (new GeneralPage (ps, border));
+       e->AddPage (new FullGeneralPage (ps, border));
+       e->AddPage (new SoundPage (ps, border));
        e->AddPage (new DefaultsPage (ps, border));
        e->AddPage (new EncodingServersPage (ps, border));
        e->AddPage (new KeysPage (ps, border));
        e->AddPage (new TMSPage (ps, border));
+       e->AddPage (new EmailPage (ps, border));
        e->AddPage (new KDMEmailPage (ps, border));
+       e->AddPage (new NotificationsPage (ps, border));
        e->AddPage (new CoverSheetPage (ps, border));
+       e->AddPage (new IdentifiersPage (ps, border));
        e->AddPage (new AdvancedPage (ps, border));
        return e;
 }