2 Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/config_dialog.cc
21 * @brief A dialogue to edit DCP-o-matic configuration.
24 #include "config_dialog.h"
26 #include "editable_list.h"
27 #include "filter_dialog.h"
28 #include "dir_picker_ctrl.h"
29 #include "isdcf_metadata_dialog.h"
30 #include "server_dialog.h"
31 #include "make_chain_dialog.h"
32 #include "lib/config.h"
33 #include "lib/ratio.h"
34 #include "lib/filter.h"
35 #include "lib/dcp_content_type.h"
38 #include "lib/raw_convert.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include <dcp/exceptions.h>
42 #include <dcp/certificate_chain.h>
43 #include <wx/stdpaths.h>
44 #include <wx/preferences.h>
45 #include <wx/filepicker.h>
46 #include <wx/spinctrl.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/filesystem.hpp>
49 #include <boost/foreach.hpp>
57 using boost::shared_ptr;
58 using boost::lexical_cast;
59 using boost::function;
64 Page (wxSize panel_size, int border)
67 , _panel_size (panel_size)
68 , _window_exists (false)
70 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
76 wxWindow* create_window (wxWindow* parent)
78 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
79 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
83 _window_exists = true;
86 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
95 virtual void config_changed () = 0;
96 virtual void setup () = 0;
98 void config_changed_wrapper ()
100 if (_window_exists) {
105 void window_destroyed ()
107 _window_exists = false;
111 boost::signals2::scoped_connection _config_connection;
115 class StockPage : public wxStockPreferencesPage, public Page
118 StockPage (Kind kind, wxSize panel_size, int border)
119 : wxStockPreferencesPage (kind)
120 , Page (panel_size, border)
123 wxWindow* CreateWindow (wxWindow* parent)
125 return create_window (parent);
129 class StandardPage : public wxPreferencesPage, public Page
132 StandardPage (wxSize panel_size, int border)
133 : Page (panel_size, border)
136 wxWindow* CreateWindow (wxWindow* parent)
138 return create_window (parent);
142 class GeneralPage : public StockPage
145 GeneralPage (wxSize panel_size, int border)
146 : StockPage (Kind_General, panel_size, border)
152 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
153 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
156 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
157 table->Add (_set_language, wxGBPosition (r, 0));
158 _language = new wxChoice (_panel, wxID_ANY);
159 _language->Append (wxT ("Deutsch"));
160 _language->Append (wxT ("English"));
161 _language->Append (wxT ("Español"));
162 _language->Append (wxT ("Français"));
163 _language->Append (wxT ("Italiano"));
164 _language->Append (wxT ("Nederlands"));
165 _language->Append (wxT ("Svenska"));
166 _language->Append (wxT ("Русский"));
167 _language->Append (wxT ("Polski"));
168 _language->Append (wxT ("Danske"));
169 table->Add (_language, wxGBPosition (r, 1));
172 wxStaticText* restart = add_label_to_grid_bag_sizer (
173 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
175 wxFont font = restart->GetFont();
176 font.SetStyle (wxFONTSTYLE_ITALIC);
177 font.SetPointSize (font.GetPointSize() - 1);
178 restart->SetFont (font);
181 add_label_to_grid_bag_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
182 _num_local_encoding_threads = new wxSpinCtrl (_panel);
183 table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
186 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
187 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
190 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
191 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
194 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
195 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
198 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
199 bottom_table->AddGrowableCol (1, 1);
201 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
202 _issuer = new wxTextCtrl (_panel, wxID_ANY);
203 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
205 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
206 _creator = new wxTextCtrl (_panel, wxID_ANY);
207 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
209 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
212 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
213 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
215 _num_local_encoding_threads->SetRange (1, 128);
216 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
218 _automatic_audio_analysis->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
219 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
220 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
222 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::issuer_changed, this));
223 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::creator_changed, this));
226 void config_changed ()
228 Config* config = Config::instance ();
230 checked_set (_set_language, config->language ());
232 if (config->language().get_value_or ("") == "fr") {
233 checked_set (_language, 3);
234 } else if (config->language().get_value_or ("") == "it") {
235 checked_set (_language, 4);
236 } else if (config->language().get_value_or ("") == "es") {
237 checked_set (_language, 2);
238 } else if (config->language().get_value_or ("") == "sv") {
239 checked_set (_language, 6);
240 } else if (config->language().get_value_or ("") == "de") {
241 checked_set (_language, 0);
242 } else if (config->language().get_value_or ("") == "nl") {
243 checked_set (_language, 5);
244 } else if (config->language().get_value_or ("") == "ru") {
245 checked_set (_language, 7);
246 } else if (config->language().get_value_or ("") == "pl") {
247 checked_set (_language, 8);
248 } else if (config->language().get_value_or ("") == "da") {
249 checked_set (_language, 9);
251 _language->SetSelection (1);
254 setup_language_sensitivity ();
256 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
257 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
258 checked_set (_check_for_updates, config->check_for_updates ());
259 checked_set (_check_for_test_updates, config->check_for_test_updates ());
260 checked_set (_issuer, config->dcp_issuer ());
261 checked_set (_creator, config->dcp_creator ());
264 void setup_language_sensitivity ()
266 _language->Enable (_set_language->GetValue ());
269 void set_language_changed ()
271 setup_language_sensitivity ();
272 if (_set_language->GetValue ()) {
275 Config::instance()->unset_language ();
279 void language_changed ()
281 switch (_language->GetSelection ()) {
283 Config::instance()->set_language ("de");
286 Config::instance()->set_language ("en");
289 Config::instance()->set_language ("es");
292 Config::instance()->set_language ("fr");
295 Config::instance()->set_language ("it");
298 Config::instance()->set_language ("nl");
301 Config::instance()->set_language ("sv");
304 Config::instance()->set_language ("ru");
307 Config::instance()->set_language ("pl");
310 Config::instance()->set_language ("da");
315 void automatic_audio_analysis_changed ()
317 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
320 void check_for_updates_changed ()
322 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
325 void check_for_test_updates_changed ()
327 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
330 void num_local_encoding_threads_changed ()
332 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
335 void issuer_changed ()
337 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
340 void creator_changed ()
342 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
345 wxCheckBox* _set_language;
347 wxSpinCtrl* _num_local_encoding_threads;
348 wxCheckBox* _automatic_audio_analysis;
349 wxCheckBox* _check_for_updates;
350 wxCheckBox* _check_for_test_updates;
352 wxTextCtrl* _creator;
355 class DefaultsPage : public StandardPage
358 DefaultsPage (wxSize panel_size, int border)
359 : StandardPage (panel_size, border)
362 wxString GetName () const
364 return _("Defaults");
368 wxBitmap GetLargeIcon () const
370 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
377 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
378 table->AddGrowableCol (1, 1);
379 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
382 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
383 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
384 _still_length = new wxSpinCtrl (_panel);
385 s->Add (_still_length);
386 add_label_to_sizer (s, _panel, _("s"), false);
390 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
391 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
392 _directory = new DirPickerCtrl (_panel);
394 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
396 table->Add (_directory, 1, wxEXPAND);
398 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
399 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
400 table->Add (_isdcf_metadata_button);
402 add_label_to_sizer (table, _panel, _("Default container"), true);
403 _container = new wxChoice (_panel, wxID_ANY);
404 table->Add (_container);
406 add_label_to_sizer (table, _panel, _("Default content type"), true);
407 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
408 table->Add (_dcp_content_type);
411 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
412 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
413 _j2k_bandwidth = new wxSpinCtrl (_panel);
414 s->Add (_j2k_bandwidth);
415 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
420 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
421 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
422 _audio_delay = new wxSpinCtrl (_panel);
423 s->Add (_audio_delay);
424 add_label_to_sizer (s, _panel, _("ms"), false);
428 _still_length->SetRange (1, 3600);
429 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
431 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
433 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
435 vector<Ratio const *> ratios = Ratio::all ();
436 for (size_t i = 0; i < ratios.size(); ++i) {
437 _container->Append (std_to_wx (ratios[i]->nickname ()));
440 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
442 vector<DCPContentType const *> const ct = DCPContentType::all ();
443 for (size_t i = 0; i < ct.size(); ++i) {
444 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
447 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
449 _j2k_bandwidth->SetRange (50, 250);
450 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
452 _audio_delay->SetRange (-1000, 1000);
453 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
456 void config_changed ()
458 Config* config = Config::instance ();
460 vector<Ratio const *> ratios = Ratio::all ();
461 for (size_t i = 0; i < ratios.size(); ++i) {
462 if (ratios[i] == config->default_container ()) {
463 _container->SetSelection (i);
467 vector<DCPContentType const *> const ct = DCPContentType::all ();
468 for (size_t i = 0; i < ct.size(); ++i) {
469 if (ct[i] == config->default_dcp_content_type ()) {
470 _dcp_content_type->SetSelection (i);
474 checked_set (_still_length, config->default_still_length ());
475 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
476 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
477 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
478 checked_set (_audio_delay, config->default_audio_delay ());
481 void j2k_bandwidth_changed ()
483 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
486 void audio_delay_changed ()
488 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
491 void directory_changed ()
493 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
496 void edit_isdcf_metadata_clicked ()
498 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
500 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
504 void still_length_changed ()
506 Config::instance()->set_default_still_length (_still_length->GetValue ());
509 void container_changed ()
511 vector<Ratio const *> ratio = Ratio::all ();
512 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
515 void dcp_content_type_changed ()
517 vector<DCPContentType const *> ct = DCPContentType::all ();
518 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
521 wxSpinCtrl* _j2k_bandwidth;
522 wxSpinCtrl* _audio_delay;
523 wxButton* _isdcf_metadata_button;
524 wxSpinCtrl* _still_length;
525 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
526 DirPickerCtrl* _directory;
528 wxDirPickerCtrl* _directory;
530 wxChoice* _container;
531 wxChoice* _dcp_content_type;
534 class EncodingServersPage : public StandardPage
537 EncodingServersPage (wxSize panel_size, int border)
538 : StandardPage (panel_size, border)
541 wxString GetName () const
547 wxBitmap GetLargeIcon () const
549 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
556 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
557 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
559 vector<string> columns;
560 columns.push_back (wx_to_std (_("IP address / host name")));
561 _servers_list = new EditableList<string, ServerDialog> (
564 boost::bind (&Config::servers, Config::instance()),
565 boost::bind (&Config::set_servers, Config::instance(), _1),
566 boost::bind (&EncodingServersPage::server_column, this, _1)
569 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
571 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
574 void config_changed ()
576 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
577 _servers_list->refresh ();
580 void use_any_servers_changed ()
582 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
585 string server_column (string s)
590 wxCheckBox* _use_any_servers;
591 EditableList<string, ServerDialog>* _servers_list;
594 class CertificateChainEditor : public wxPanel
597 CertificateChainEditor (
601 function<void (shared_ptr<dcp::CertificateChain>)> set,
602 function<shared_ptr<const dcp::CertificateChain> (void)> get
608 wxFont subheading_font (*wxNORMAL_FONT);
609 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
611 _sizer = new wxBoxSizer (wxVERTICAL);
614 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
615 m->SetFont (subheading_font);
616 _sizer->Add (m, 0, wxALL, border);
619 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
620 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
622 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
627 ip.SetText (_("Type"));
629 _certificates->InsertColumn (0, ip);
635 ip.SetText (_("Thumbprint"));
638 wxFont font = ip.GetFont ();
639 font.SetFamily (wxFONTFAMILY_TELETYPE);
642 _certificates->InsertColumn (1, ip);
645 certificates_sizer->Add (_certificates, 1, wxEXPAND);
648 wxSizer* s = new wxBoxSizer (wxVERTICAL);
649 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
650 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
651 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
652 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
653 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
656 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
657 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
660 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
661 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
662 wxFont font = _private_key->GetFont ();
663 font.SetFamily (wxFONTFAMILY_TELETYPE);
664 _private_key->SetFont (font);
665 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
666 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
667 table->Add (_load_private_key, wxGBPosition (r, 2));
670 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
671 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
672 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
673 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
676 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::add_certificate, this));
677 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remove_certificate, this));
678 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
679 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
680 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remake_certificates, this));
681 _load_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::load_private_key, this));
683 SetSizerAndFit (_sizer);
686 void config_changed ()
688 _chain.reset (new dcp::CertificateChain (*_get().get ()));
690 update_certificate_list ();
691 update_private_key ();
692 update_sensitivity ();
695 void add_button (wxWindow* button)
697 _button_sizer->Add (button);
702 void add_certificate ()
704 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
706 if (d->ShowModal() == wxID_OK) {
708 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
711 update_certificate_list ();
712 } catch (dcp::MiscError& e) {
713 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
719 update_sensitivity ();
722 void remove_certificate ()
724 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
729 _certificates->DeleteItem (i);
733 update_sensitivity ();
736 void update_certificate_list ()
738 _certificates->DeleteAllItems ();
740 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
741 BOOST_FOREACH (dcp::Certificate const & i, certs) {
744 _certificates->InsertItem (item);
745 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
748 _certificates->SetItem (n, 0, _("Root"));
749 } else if (n == (certs.size() - 1)) {
750 _certificates->SetItem (n, 0, _("Leaf"));
752 _certificates->SetItem (n, 0, _("Intermediate"));
759 void remake_certificates ()
761 shared_ptr<const dcp::CertificateChain> chain = _get ();
763 string intermediate_common_name;
764 if (chain->root_to_leaf().size() >= 3) {
765 dcp::CertificateChain::List all = chain->root_to_leaf ();
766 dcp::CertificateChain::List::iterator i = all.begin ();
768 intermediate_common_name = i->subject_common_name ();
771 MakeChainDialog* d = new MakeChainDialog (
773 chain->root().subject_organization_name (),
774 chain->root().subject_organizational_unit_name (),
775 chain->root().subject_common_name (),
776 intermediate_common_name,
777 chain->leaf().subject_common_name ()
780 if (d->ShowModal () == wxID_OK) {
782 new dcp::CertificateChain (
785 d->organisational_unit (),
786 d->root_common_name (),
787 d->intermediate_common_name (),
788 d->leaf_common_name ()
793 update_certificate_list ();
794 update_private_key ();
800 void update_sensitivity ()
802 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
805 void update_private_key ()
807 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
811 void load_private_key ()
813 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
815 if (d->ShowModal() == wxID_OK) {
817 boost::filesystem::path p (wx_to_std (d->GetPath ()));
818 if (boost::filesystem::file_size (p) > 1024) {
819 error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
823 _chain->set_key (dcp::file_to_string (p));
825 update_private_key ();
826 } catch (dcp::MiscError& e) {
827 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
833 update_sensitivity ();
836 wxListCtrl* _certificates;
837 wxButton* _add_certificate;
838 wxButton* _remove_certificate;
839 wxButton* _remake_certificates;
840 wxStaticText* _private_key;
841 wxButton* _load_private_key;
843 wxBoxSizer* _button_sizer;
844 shared_ptr<dcp::CertificateChain> _chain;
845 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
846 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
849 class KeysPage : public StandardPage
852 KeysPage (wxSize panel_size, int border)
853 : StandardPage (panel_size, border)
856 wxString GetName () const
862 wxBitmap GetLargeIcon () const
864 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
872 _signer = new CertificateChainEditor (
873 _panel, _("Signing DCPs and KDMs"), _border,
874 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
875 boost::bind (&Config::signer_chain, Config::instance ())
878 _panel->GetSizer()->Add (_signer);
880 _decryption = new CertificateChainEditor (
881 _panel, _("Decrypting DCPs"), _border,
882 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
883 boost::bind (&Config::decryption_chain, Config::instance ())
886 _panel->GetSizer()->Add (_decryption);
888 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
889 _decryption->add_button (_export_decryption_certificate);
891 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
894 void export_decryption_certificate ()
896 wxFileDialog* d = new wxFileDialog (
897 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
898 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
901 if (d->ShowModal () == wxID_OK) {
902 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
904 throw OpenFileError (wx_to_std (d->GetPath ()));
907 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
908 fwrite (s.c_str(), 1, s.length(), f);
914 void config_changed ()
916 _signer->config_changed ();
917 _decryption->config_changed ();
920 CertificateChainEditor* _signer;
921 CertificateChainEditor* _decryption;
922 wxButton* _export_decryption_certificate;
925 class TMSPage : public StandardPage
928 TMSPage (wxSize panel_size, int border)
929 : StandardPage (panel_size, border)
932 wxString GetName () const
938 wxBitmap GetLargeIcon () const
940 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
947 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
948 table->AddGrowableCol (1, 1);
949 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
951 add_label_to_sizer (table, _panel, _("Protocol"), true);
952 _tms_protocol = new wxChoice (_panel, wxID_ANY);
953 table->Add (_tms_protocol, 1, wxEXPAND);
955 add_label_to_sizer (table, _panel, _("IP address"), true);
956 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
957 table->Add (_tms_ip, 1, wxEXPAND);
959 add_label_to_sizer (table, _panel, _("Target path"), true);
960 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
961 table->Add (_tms_path, 1, wxEXPAND);
963 add_label_to_sizer (table, _panel, _("User name"), true);
964 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
965 table->Add (_tms_user, 1, wxEXPAND);
967 add_label_to_sizer (table, _panel, _("Password"), true);
968 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
969 table->Add (_tms_password, 1, wxEXPAND);
971 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
972 _tms_protocol->Append (_("FTP (for Dolby)"));
974 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
975 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
976 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
977 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
978 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
981 void config_changed ()
983 Config* config = Config::instance ();
985 checked_set (_tms_protocol, config->tms_protocol ());
986 checked_set (_tms_ip, config->tms_ip ());
987 checked_set (_tms_path, config->tms_path ());
988 checked_set (_tms_user, config->tms_user ());
989 checked_set (_tms_password, config->tms_password ());
992 void tms_protocol_changed ()
994 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
997 void tms_ip_changed ()
999 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1002 void tms_path_changed ()
1004 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1007 void tms_user_changed ()
1009 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1012 void tms_password_changed ()
1014 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1017 wxChoice* _tms_protocol;
1018 wxTextCtrl* _tms_ip;
1019 wxTextCtrl* _tms_path;
1020 wxTextCtrl* _tms_user;
1021 wxTextCtrl* _tms_password;
1024 class KDMEmailPage : public StandardPage
1028 KDMEmailPage (wxSize panel_size, int border)
1029 #ifdef DCPOMATIC_OSX
1030 /* We have to force both width and height of this one */
1031 : StandardPage (wxSize (480, 128), border)
1033 : StandardPage (panel_size, border)
1037 wxString GetName () const
1039 return _("KDM Email");
1042 #ifdef DCPOMATIC_OSX
1043 wxBitmap GetLargeIcon () const
1045 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1052 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1053 table->AddGrowableCol (1, 1);
1054 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1056 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1058 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1059 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1060 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1061 add_label_to_sizer (s, _panel, _("port"), false);
1062 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1063 _mail_port->SetRange (0, 65535);
1064 s->Add (_mail_port);
1065 table->Add (s, 1, wxEXPAND | wxALL);
1068 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1069 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1070 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1072 add_label_to_sizer (table, _panel, _("Mail password"), true);
1073 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1074 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1076 add_label_to_sizer (table, _panel, _("Subject"), true);
1077 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1078 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1080 add_label_to_sizer (table, _panel, _("From address"), true);
1081 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1082 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1084 add_label_to_sizer (table, _panel, _("CC address"), true);
1085 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1086 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1088 add_label_to_sizer (table, _panel, _("BCC address"), true);
1089 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1090 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1092 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1093 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1095 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1096 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1098 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1099 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1100 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1101 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1102 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1103 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1104 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1105 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1106 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1107 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1110 void config_changed ()
1112 Config* config = Config::instance ();
1114 checked_set (_mail_server, config->mail_server ());
1115 checked_set (_mail_port, config->mail_port ());
1116 checked_set (_mail_user, config->mail_user ());
1117 checked_set (_mail_password, config->mail_password ());
1118 checked_set (_kdm_subject, config->kdm_subject ());
1119 checked_set (_kdm_from, config->kdm_from ());
1120 checked_set (_kdm_cc, config->kdm_cc ());
1121 checked_set (_kdm_bcc, config->kdm_bcc ());
1122 checked_set (_kdm_email, Config::instance()->kdm_email ());
1125 void mail_server_changed ()
1127 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1130 void mail_port_changed ()
1132 Config::instance()->set_mail_port (_mail_port->GetValue ());
1135 void mail_user_changed ()
1137 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1140 void mail_password_changed ()
1142 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1145 void kdm_subject_changed ()
1147 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1150 void kdm_from_changed ()
1152 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1155 void kdm_cc_changed ()
1157 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1160 void kdm_bcc_changed ()
1162 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1165 void kdm_email_changed ()
1167 if (_kdm_email->GetValue().IsEmpty ()) {
1168 /* Sometimes we get sent an erroneous notification that the email
1169 is empty; I don't know why.
1173 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1176 void reset_kdm_email ()
1178 Config::instance()->reset_kdm_email ();
1179 checked_set (_kdm_email, Config::instance()->kdm_email ());
1182 wxTextCtrl* _mail_server;
1183 wxSpinCtrl* _mail_port;
1184 wxTextCtrl* _mail_user;
1185 wxTextCtrl* _mail_password;
1186 wxTextCtrl* _kdm_subject;
1187 wxTextCtrl* _kdm_from;
1188 wxTextCtrl* _kdm_cc;
1189 wxTextCtrl* _kdm_bcc;
1190 wxTextCtrl* _kdm_email;
1191 wxButton* _reset_kdm_email;
1194 /** @class AdvancedPage
1195 * @brief Advanced page of the preferences dialog.
1197 class AdvancedPage : public StockPage
1200 AdvancedPage (wxSize panel_size, int border)
1201 : StockPage (Kind_Advanced, panel_size, border)
1202 , _maximum_j2k_bandwidth (0)
1203 , _allow_any_dcp_frame_rate (0)
1204 , _only_servers_encode (0)
1209 , _log_debug_decode (0)
1210 , _log_debug_encode (0)
1216 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1217 table->AddGrowableCol (1, 1);
1218 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1221 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1222 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1223 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1224 s->Add (_maximum_j2k_bandwidth, 1);
1225 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1229 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1230 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1231 table->AddSpacer (0);
1233 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1234 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1235 table->AddSpacer (0);
1238 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1239 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1241 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1242 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1246 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1247 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1248 t->Add (_log_general, 1, wxEXPAND | wxALL);
1249 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1250 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1251 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1252 t->Add (_log_error, 1, wxEXPAND | wxALL);
1253 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1254 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1255 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1256 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1257 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1258 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1259 table->Add (t, 0, wxALL, 6);
1262 #ifdef DCPOMATIC_WINDOWS
1263 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1264 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1265 table->AddSpacer (0);
1268 _maximum_j2k_bandwidth->SetRange (1, 1000);
1269 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1270 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1271 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1272 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1273 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1274 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1275 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1276 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1277 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1278 #ifdef DCPOMATIC_WINDOWS
1279 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1283 void config_changed ()
1285 Config* config = Config::instance ();
1287 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1288 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1289 checked_set (_only_servers_encode, config->only_servers_encode ());
1290 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1291 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1292 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1293 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1294 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1295 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1296 #ifdef DCPOMATIC_WINDOWS
1297 checked_set (_win32_console, config->win32_console());
1301 void maximum_j2k_bandwidth_changed ()
1303 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1306 void allow_any_dcp_frame_rate_changed ()
1308 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1311 void only_servers_encode_changed ()
1313 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1319 if (_log_general->GetValue ()) {
1320 types |= Log::TYPE_GENERAL;
1322 if (_log_warning->GetValue ()) {
1323 types |= Log::TYPE_WARNING;
1325 if (_log_error->GetValue ()) {
1326 types |= Log::TYPE_ERROR;
1328 if (_log_timing->GetValue ()) {
1329 types |= Log::TYPE_TIMING;
1331 if (_log_debug_decode->GetValue ()) {
1332 types |= Log::TYPE_DEBUG_DECODE;
1334 if (_log_debug_encode->GetValue ()) {
1335 types |= Log::TYPE_DEBUG_ENCODE;
1337 Config::instance()->set_log_types (types);
1340 #ifdef DCPOMATIC_WINDOWS
1341 void win32_console_changed ()
1343 Config::instance()->set_win32_console (_win32_console->GetValue ());
1347 wxSpinCtrl* _maximum_j2k_bandwidth;
1348 wxCheckBox* _allow_any_dcp_frame_rate;
1349 wxCheckBox* _only_servers_encode;
1350 wxCheckBox* _log_general;
1351 wxCheckBox* _log_warning;
1352 wxCheckBox* _log_error;
1353 wxCheckBox* _log_timing;
1354 wxCheckBox* _log_debug_decode;
1355 wxCheckBox* _log_debug_encode;
1356 #ifdef DCPOMATIC_WINDOWS
1357 wxCheckBox* _win32_console;
1361 wxPreferencesEditor*
1362 create_config_dialog ()
1364 wxPreferencesEditor* e = new wxPreferencesEditor ();
1366 #ifdef DCPOMATIC_OSX
1367 /* Width that we force some of the config panels to be on OSX so that
1368 the containing window doesn't shrink too much when we select those panels.
1369 This is obviously an unpleasant hack.
1371 wxSize ps = wxSize (520, -1);
1372 int const border = 16;
1374 wxSize ps = wxSize (-1, -1);
1375 int const border = 8;
1378 e->AddPage (new GeneralPage (ps, border));
1379 e->AddPage (new DefaultsPage (ps, border));
1380 e->AddPage (new EncodingServersPage (ps, border));
1381 e->AddPage (new KeysPage (ps, border));
1382 e->AddPage (new TMSPage (ps, border));
1383 e->AddPage (new KDMEmailPage (ps, border));
1384 e->AddPage (new AdvancedPage (ps, border));