swaroop: fix up restart-after-crash.
[dcpomatic.git] / src / wx / config_dialog.cc
index 30585f4e341ece1af0cb9441a98dc9d0c98d3554..ccf1202ecd25b3bb30f05f6d9e78682590ed8780 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 */
 
 #include "config_dialog.h"
+#include "static_text.h"
+#include "check_box.h"
 #include "nag_dialog.h"
+#include "dcpomatic_button.h"
 
+using std::string;
+using std::vector;
+using std::pair;
+using std::make_pair;
+using std::map;
 using boost::bind;
+using boost::optional;
+using boost::shared_ptr;
+using boost::function;
 
 static
-void
+bool
 do_nothing ()
 {
-
+       return false;
 }
 
 Page::Page (wxSize panel_size, int border)
@@ -36,7 +47,7 @@ Page::Page (wxSize panel_size, int border)
        , _panel_size (panel_size)
        , _window_exists (false)
 {
-       _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
+       _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
 }
 
 wxWindow*
@@ -50,7 +61,7 @@ Page::create_window (wxWindow* parent)
        _window_exists = true;
        config_changed ();
 
-       _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
+       _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
 
        return _panel;
 }
@@ -104,26 +115,26 @@ GeneralPage::GeneralPage (wxSize panel_size, int border)
 void
 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
 {
-       _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
-       table->Add (_set_language, wxGBPosition (r, 0));
+       _set_language = new CheckBox (_panel, _("Set language"));
+       table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
        _language = new wxChoice (_panel, wxID_ANY);
-       std::vector<std::pair<std::string, std::string> > languages;
-       languages.push_back (std::make_pair ("Čeština", "cs_CZ"));
-       languages.push_back (std::make_pair ("汉语/漢語", "zh_CN"));
-       languages.push_back (std::make_pair ("Dansk", "da_DK"));
-       languages.push_back (std::make_pair ("Deutsch", "de_DE"));
-       languages.push_back (std::make_pair ("English", "en_GB"));
-       languages.push_back (std::make_pair ("Español", "es_ES"));
-       languages.push_back (std::make_pair ("Français", "fr_FR"));
-       languages.push_back (std::make_pair ("Italiano", "it_IT"));
-       languages.push_back (std::make_pair ("Nederlands", "nl_NL"));
-       languages.push_back (std::make_pair ("Русский", "ru_RU"));
-       languages.push_back (std::make_pair ("Polski", "pl_PL"));
-       languages.push_back (std::make_pair ("Português europeu", "pt_PT"));
-       languages.push_back (std::make_pair ("Português do Brasil", "pt_BR"));
-       languages.push_back (std::make_pair ("Svenska", "sv_SE"));
-       languages.push_back (std::make_pair ("Slovenský jazyk", "sk_SK"));
-       languages.push_back (std::make_pair ("українська мова", "uk_UA"));
+       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;
@@ -137,15 +148,15 @@ GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
        restart->SetFont (font);
        ++r;
 
-       _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
-       _language->Bind     (wxEVT_CHOICE,   boost::bind (&GeneralPage::language_changed,     this));
+       _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
+       _language->Bind     (wxEVT_CHOICE,   bind (&GeneralPage::language_changed,     this));
 }
 
 void
 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
 {
-       _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via"));
-       table->Add (_sound, wxGBPosition (r, 0));
+       _sound = new CheckBox (_panel, _("Play sound via"));
+       table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
        _sound_output = new wxChoice (_panel, wxID_ANY);
        table->Add (_sound_output, wxGBPosition (r, 1));
        ++r;
@@ -158,23 +169,23 @@ GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
                }
        }
 
-       _sound->Bind        (wxEVT_CHECKBOX, boost::bind (&GeneralPage::sound_changed, this));
-       _sound_output->Bind (wxEVT_CHOICE,   boost::bind (&GeneralPage::sound_output_changed, this));
+       _sound->Bind        (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
+       _sound_output->Bind (wxEVT_CHOICE,   bind (&GeneralPage::sound_output_changed, this));
 }
 
 void
 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
 {
-       _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
+       _check_for_updates = new CheckBox (_panel, _("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"));
+       _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
        table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
        ++r;
 
-       _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));
+       _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
+       _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
 }
 
 void
@@ -186,7 +197,7 @@ GeneralPage::config_changed ()
 
        /* Backwards compatibility of config file */
 
-       std::map<std::string, std::string> compat_map;
+       map<string, string> compat_map;
        compat_map["fr"] = "fr_FR";
        compat_map["it"] = "it_IT";
        compat_map["es"] = "es_ES";
@@ -201,7 +212,7 @@ GeneralPage::config_changed ()
        compat_map["cs"] = "cs_CZ";
        compat_map["uk"] = "uk_UA";
 
-       std::string lang = config->language().get_value_or ("en_GB");
+       string lang = config->language().get_value_or ("en_GB");
        if (compat_map.find (lang) != compat_map.end ()) {
                lang = compat_map[lang];
        }
@@ -213,8 +224,8 @@ GeneralPage::config_changed ()
 
        checked_set (_sound, config->sound ());
 
-       boost::optional<std::string> const current_so = get_sound_output ();
-       boost::optional<std::string> configured_so;
+       optional<string> const current_so = get_sound_output ();
+       optional<string> configured_so;
 
        if (config->sound_output()) {
                configured_so = config->sound_output().get();
@@ -252,12 +263,12 @@ GeneralPage::setup_sensitivity ()
 }
 
 /** @return Currently-selected preview sound output in the dialogue */
-boost::optional<std::string>
+optional<string>
 GeneralPage::get_sound_output ()
 {
        int const sel = _sound_output->GetSelection ();
        if (sel == wxNOT_FOUND) {
-               return boost::optional<std::string> ();
+               return optional<string> ();
        }
 
        return wx_to_std (_sound_output->GetString (sel));
@@ -307,7 +318,7 @@ void
 GeneralPage::sound_output_changed ()
 {
        RtAudio audio (DCPOMATIC_RTAUDIO_API);
-       boost::optional<std::string> const so = get_sound_output();
+       optional<string> const so = get_sound_output();
        if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
                Config::instance()->unset_sound_output ();
        } else {
@@ -319,11 +330,11 @@ CertificateChainEditor::CertificateChainEditor (
        wxWindow* parent,
        wxString title,
        int border,
-       boost::function<void (boost::shared_ptr<dcp::CertificateChain>)> set,
-       boost::function<boost::shared_ptr<const dcp::CertificateChain> (void)> get,
-       boost::function<void (void)> nag_remake
+       function<void (shared_ptr<dcp::CertificateChain>)> set,
+       function<shared_ptr<const dcp::CertificateChain> (void)> get,
+       function<bool (void)> nag_remake
        )
-       : wxPanel (parent)
+       : wxDialog (parent, wxID_ANY, title)
        , _set (set)
        , _get (get)
        , _nag_remake (nag_remake)
@@ -334,7 +345,7 @@ CertificateChainEditor::CertificateChainEditor (
        _sizer = new wxBoxSizer (wxVERTICAL);
 
        {
-               wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
+               wxStaticText* m = new StaticText (this, title);
                m->SetFont (subheading_font);
                _sizer->Add (m, 0, wxALL, border);
        }
@@ -369,11 +380,11 @@ CertificateChainEditor::CertificateChainEditor (
 
        {
                wxSizer* s = new wxBoxSizer (wxVERTICAL);
-               _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
+               _add_certificate = new Button (this, _("Add..."));
                s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-               _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
+               _remove_certificate = new Button (this, _("Remove"));
                s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-               _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
+               _export_certificate = new Button (this, _("Export"));
                s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
                certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
        }
@@ -383,47 +394,45 @@ CertificateChainEditor::CertificateChainEditor (
        int r = 0;
 
        add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
-       _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
+       _private_key = new StaticText (this, 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..."));
+       _import_private_key = new Button (this, _("Import..."));
+       table->Add (_import_private_key, wxGBPosition (r, 2));
+       _export_private_key = new Button (this, _("Export..."));
        table->Add (_export_private_key, wxGBPosition (r, 3));
        ++r;
 
        _button_sizer = new wxBoxSizer (wxHORIZONTAL);
-       _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
+       _remake_certificates = new Button (this, _("Re-make certificates and key..."));
        _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
        table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
        ++r;
 
-       _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
+       _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
        font = *wxSMALL_FONT;
        font.SetWeight (wxFONTWEIGHT_BOLD);
        _private_key_bad->SetFont (font);
        table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
        ++r;
 
-       _add_certificate->Bind     (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::add_certificate, this));
-       _remove_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remove_certificate, this));
-       _export_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_certificate, this));
-       _certificates->Bind        (wxEVT_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
-       _certificates->Bind        (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
-       _remake_certificates->Bind (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remake_certificates, this));
-       _load_private_key->Bind    (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::load_private_key, this));
-       _export_private_key->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_private_key, this));
+       _add_certificate->Bind     (wxEVT_BUTTON,       bind (&CertificateChainEditor::add_certificate, this));
+       _remove_certificate->Bind  (wxEVT_BUTTON,       bind (&CertificateChainEditor::remove_certificate, this));
+       _export_certificate->Bind  (wxEVT_BUTTON,       bind (&CertificateChainEditor::export_certificate, this));
+       _certificates->Bind        (wxEVT_LIST_ITEM_SELECTED,   bind (&CertificateChainEditor::update_sensitivity, this));
+       _certificates->Bind        (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
+       _remake_certificates->Bind (wxEVT_BUTTON,       bind (&CertificateChainEditor::remake_certificates, this));
+       _import_private_key->Bind  (wxEVT_BUTTON,       bind (&CertificateChainEditor::import_private_key, this));
+       _export_private_key->Bind  (wxEVT_BUTTON,       bind (&CertificateChainEditor::export_private_key, this));
+
+       wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
+       if (buttons) {
+               _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+       }
 
        SetSizerAndFit (_sizer);
-}
-
-
-void
-CertificateChainEditor::config_changed ()
-{
-       _chain.reset (new dcp::CertificateChain (*_get().get ()));
 
        update_certificate_list ();
        update_private_key ();
@@ -445,11 +454,11 @@ CertificateChainEditor::add_certificate ()
        if (d->ShowModal() == wxID_OK) {
                try {
                        dcp::Certificate c;
-                       std::string extra;
+                       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()));
+                               error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
                                d->Destroy ();
                                return;
                        }
@@ -461,20 +470,21 @@ CertificateChainEditor::add_certificate ()
                                          "Only the first certificate will be used.")
                                        );
                        }
-                       _chain->add (c);
-                       if (!_chain->chain_valid ()) {
+                       shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
+                       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);
+                               chain->remove (c);
                        } else {
-                               _set (_chain);
+                               _set (chain);
                                update_certificate_list ();
                        }
                } catch (dcp::MiscError& e) {
-                       error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+                       error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
                }
        }
 
@@ -492,8 +502,9 @@ CertificateChainEditor::remove_certificate ()
        }
 
        _certificates->DeleteItem (i);
-       _chain->remove (i);
-       _set (_chain);
+       shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
+       chain->remove (i);
+       _set (chain);
 
        update_sensitivity ();
 }
@@ -511,19 +522,20 @@ CertificateChainEditor::export_certificate ()
                wxFD_SAVE | wxFD_OVERWRITE_PROMPT
                );
 
-       dcp::CertificateChain::List all = _chain->root_to_leaf ();
+       dcp::CertificateChain::List all = _get()->root_to_leaf ();
        dcp::CertificateChain::List::iterator j = all.begin ();
        for (int k = 0; k < i; ++k) {
                ++j;
        }
 
        if (d->ShowModal () == wxID_OK) {
-               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               boost::filesystem::path path (wx_to_std(d->GetPath()));
+               FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+                       throw OpenFileError (path, errno, false);
                }
 
-               std::string const s = j->certificate (true);
+               string const s = j->certificate (true);
                fwrite (s.c_str(), 1, s.length(), f);
                fclose (f);
        }
@@ -535,7 +547,7 @@ CertificateChainEditor::update_certificate_list ()
 {
        _certificates->DeleteAllItems ();
        size_t n = 0;
-       dcp::CertificateChain::List certs = _chain->root_to_leaf ();
+       dcp::CertificateChain::List certs = _get()->root_to_leaf ();
        BOOST_FOREACH (dcp::Certificate const & i, certs) {
                wxListItem item;
                item.SetId (n);
@@ -555,7 +567,7 @@ CertificateChainEditor::update_certificate_list ()
 
        static wxColour normal = _private_key_bad->GetForegroundColour ();
 
-       if (_chain->private_key_valid ()) {
+       if (_get()->private_key_valid()) {
                _private_key_bad->Hide ();
                _private_key_bad->SetForegroundColour (normal);
        } else {
@@ -567,13 +579,13 @@ CertificateChainEditor::update_certificate_list ()
 void
 CertificateChainEditor::remake_certificates ()
 {
-       boost::shared_ptr<const dcp::CertificateChain> chain = _get ();
+       shared_ptr<const dcp::CertificateChain> chain = _get();
 
-       std::string subject_organization_name;
-       std::string subject_organizational_unit_name;
-       std::string root_common_name;
-       std::string intermediate_common_name;
-       std::string leaf_common_name;
+       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 ();
 
@@ -596,7 +608,10 @@ CertificateChainEditor::remake_certificates ()
                intermediate_common_name = i->subject_common_name ();
        }
 
-       _nag_remake ();
+       if (_nag_remake()) {
+               /* Cancel was clicked */
+               return;
+       }
 
        MakeChainDialog* d = new MakeChainDialog (
                this,
@@ -608,18 +623,19 @@ CertificateChainEditor::remake_certificates ()
                );
 
        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 (
+                       shared_ptr<dcp::CertificateChain> (
+                               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 ();
        }
@@ -638,12 +654,12 @@ CertificateChainEditor::update_sensitivity ()
 void
 CertificateChainEditor::update_private_key ()
 {
-       checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
+       checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
        _sizer->Layout ();
 }
 
 void
-CertificateChainEditor::load_private_key ()
+CertificateChainEditor::import_private_key ()
 {
        wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
 
@@ -658,11 +674,12 @@ CertificateChainEditor::load_private_key ()
                                return;
                        }
 
-                       _chain->set_key (dcp::file_to_string (p));
-                       _set (_chain);
+                       shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
+                       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 ()));
+                       error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
                }
        }
 
@@ -674,7 +691,7 @@ CertificateChainEditor::load_private_key ()
 void
 CertificateChainEditor::export_private_key ()
 {
-       boost::optional<std::string> key = _chain->key ();
+       optional<string> key = _get()->key();
        if (!key) {
                return;
        }
@@ -685,12 +702,13 @@ CertificateChainEditor::export_private_key ()
                );
 
        if (d->ShowModal () == wxID_OK) {
-               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               boost::filesystem::path path (wx_to_std(d->GetPath()));
+               FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+                       throw OpenFileError (path, errno, false);
                }
 
-               std::string const s = _chain->key().get ();
+               string const s = _get()->key().get ();
                fwrite (s.c_str(), 1, s.length(), f);
                fclose (f);
        }
@@ -706,51 +724,146 @@ KeysPage::GetName () const
 void
 KeysPage::setup ()
 {
-       if (_sign) {
-               _signer = new CertificateChainEditor (
-                       _panel, _("Signing DCPs and KDMs"), _border,
-                       bind (&Config::set_signer_chain, Config::instance (), _1),
-                       bind (&Config::signer_chain, Config::instance ()),
-                       bind (&do_nothing)
-                       );
+       wxFont subheading_font (*wxNORMAL_FONT);
+       subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
+
+       wxSizer* sizer = _panel->GetSizer();
+
+       {
+               wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
+               m->SetFont (subheading_font);
+               sizer->Add (m, 0, wxALL, _border);
+       }
 
-               _panel->GetSizer()->Add (_signer);
+       wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
+       sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
+       wxButton* export_decryption_chain = new Button (_panel, _("Export KDM decryption chain..."));
+       sizer->Add (export_decryption_chain, 0, wxLEFT, _border);
+       wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
+       sizer->Add (export_settings, 0, wxLEFT, _border);
+       wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
+       sizer->Add (import_settings, 0, wxLEFT, _border);
+       wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
+       sizer->Add (decryption_advanced, 0, wxALL, _border);
+
+       export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
+       export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
+       export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
+       import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
+       decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
+
+       {
+               wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
+               m->SetFont (subheading_font);
+               sizer->Add (m, 0, wxALL, _border);
        }
 
-       _decryption = new CertificateChainEditor (
+       wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
+       sizer->Add (signing_advanced, 0, wxLEFT, _border);
+       signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
+}
+
+void
+KeysPage::decryption_advanced ()
+{
+       CertificateChainEditor* c = new CertificateChainEditor (
                _panel, _("Decrypting KDMs"), _border,
                bind (&Config::set_decryption_chain, Config::instance (), _1),
                bind (&Config::decryption_chain, Config::instance ()),
                bind (&KeysPage::nag_remake_decryption_chain, this)
                );
 
-       _panel->GetSizer()->Add (_decryption);
+       c->ShowModal();
+}
 
-       _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);
+void
+KeysPage::signing_advanced ()
+{
+       CertificateChainEditor* c = new CertificateChainEditor (
+               _panel, _("Signing DCPs and KDMs"), _border,
+               bind (&Config::set_signer_chain, Config::instance (), _1),
+               bind (&Config::signer_chain, Config::instance ()),
+               bind (&do_nothing)
+               );
 
-       _export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
-       _export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
+       c->ShowModal();
 }
 
 void
-KeysPage::config_changed ()
+KeysPage::export_decryption_chain_and_key ()
 {
-       if (_sign) {
-               _signer->config_changed ();
+       wxFileDialog* d = new wxFileDialog (
+               _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
+               wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               boost::filesystem::path path (wx_to_std(d->GetPath()));
+               FILE* f = fopen_boost (path, "w");
+               if (!f) {
+                       throw OpenFileError (path, errno, false);
+               }
+
+               string const chain = Config::instance()->decryption_chain()->chain();
+               fwrite (chain.c_str(), 1, chain.length(), f);
+               optional<string> const key = Config::instance()->decryption_chain()->key();
+               DCPOMATIC_ASSERT (key);
+               fwrite (key->c_str(), 1, key->length(), f);
+               fclose (f);
        }
-       _decryption->config_changed ();
+       d->Destroy ();
+
 }
 
 void
+KeysPage::import_decryption_chain_and_key ()
+{
+       wxFileDialog* d = new wxFileDialog (
+               _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
+
+               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
+               if (!f) {
+                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+               }
+
+               string current;
+               while (!feof (f)) {
+                       char buffer[128];
+                       if (fgets (buffer, 128, f) == 0) {
+                               break;
+                       }
+                       current += buffer;
+                       if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
+                               new_chain->add (dcp::Certificate (current));
+                               current = "";
+                       } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
+                               new_chain->set_key (current);
+                               current = "";
+                       }
+               }
+               fclose (f);
+
+               if (new_chain->chain_valid() && new_chain->private_key_valid()) {
+                       Config::instance()->set_decryption_chain (new_chain);
+               } else {
+                       error_dialog (_panel, _("Invalid DCP-o-matic export file"));
+               }
+       }
+       d->Destroy ();
+}
+
+bool
 KeysPage::nag_remake_decryption_chain ()
 {
-       NagDialog::maybe_nag (
+       return 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!")
+               _("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!"),
+               true
                );
 }
 
@@ -758,17 +871,18 @@ void
 KeysPage::export_decryption_chain ()
 {
        wxFileDialog* d = new wxFileDialog (
-               _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               _panel, _("Select Chain File"), wxEmptyString, _("dcpomatic_kdm_decryption_chain.pem"), 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");
+               boost::filesystem::path path (wx_to_std(d->GetPath()));
+               FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+                       throw OpenFileError (path, errno, false);
                }
 
-               std::string const s = Config::instance()->decryption_chain()->chain();
+               string const s = Config::instance()->decryption_chain()->chain();
                fwrite (s.c_str(), 1, s.length(), f);
                fclose (f);
        }
@@ -779,19 +893,21 @@ void
 KeysPage::export_decryption_certificate ()
 {
        wxFileDialog* d = new wxFileDialog (
-               _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), 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");
+               boost::filesystem::path path (wx_to_std(d->GetPath()));
+               FILE* f = fopen_boost (path, "w");
                if (!f) {
-                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+                       throw OpenFileError (path, errno, false);
                }
 
-               std::string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
+               string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
                fwrite (s.c_str(), 1, s.length(), f);
                fclose (f);
        }
+
        d->Destroy ();
 }