2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/full_config_dialog.cc
23 * @brief A dialogue to edit all DCP-o-matic configuration.
27 #include "check_box.h"
28 #include "config_dialog.h"
29 #include "config_move_dialog.h"
30 #include "dcpomatic_button.h"
31 #include "dir_picker_ctrl.h"
32 #include "editable_list.h"
33 #include "email_dialog.h"
34 #include "file_picker_ctrl.h"
35 #include "filter_dialog.h"
36 #include "full_config_dialog.h"
37 #include "kdm_choice.h"
38 #include "make_chain_dialog.h"
39 #include "nag_dialog.h"
40 #include "name_format_editor.h"
41 #include "password_entry.h"
42 #include "send_test_email_dialog.h"
43 #include "server_dialog.h"
44 #include "static_text.h"
46 #include "lib/config.h"
47 #include "lib/cross.h"
48 #include "lib/dcp_content_type.h"
49 #include "lib/emailer.h"
50 #include "lib/exceptions.h"
51 #include "lib/filter.h"
53 #include "lib/ratio.h"
55 #include <dcp/certificate_chain.h>
56 #include <dcp/exceptions.h>
57 #include <dcp/locale_convert.h>
58 #include <dcp/warnings.h>
59 LIBDCP_DISABLE_WARNINGS
60 #include <wx/filepicker.h>
61 #include <wx/preferences.h>
62 #include <wx/spinctrl.h>
63 #include <wx/stdpaths.h>
64 LIBDCP_ENABLE_WARNINGS
66 #include <boost/filesystem.hpp>
76 using std::shared_ptr;
80 using boost::optional;
81 #if BOOST_VERSION >= 106100
82 using namespace boost::placeholders;
84 using dcp::locale_convert;
87 class FullGeneralPage : public GeneralPage
90 FullGeneralPage (wxSize panel_size, int border)
91 : GeneralPage (panel_size, border)
95 void setup () override
97 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
98 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
101 add_language_controls (table, r);
103 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
104 _master_encoding_threads = new wxSpinCtrl (_panel);
105 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
108 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
109 _server_encoding_threads = new wxSpinCtrl (_panel);
110 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
113 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
114 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
115 table->Add (_config_file, wxGBPosition (r, 1));
118 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
119 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
120 table->Add (_cinemas_file, wxGBPosition (r, 1));
121 auto export_cinemas = new Button (_panel, _("Export..."));
122 table->Add (export_cinemas, wxGBPosition (r, 2));
125 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
126 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
127 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
131 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
132 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
135 add_update_controls (table, r);
137 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
138 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
140 _master_encoding_threads->SetRange (1, 128);
141 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
142 _server_encoding_threads->SetRange (1, 128);
143 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
144 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
146 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
147 _analyse_ebur128->bind(&FullGeneralPage::analyse_ebur128_changed, this);
149 _automatic_audio_analysis->bind(&FullGeneralPage::automatic_audio_analysis_changed, this);
152 void config_changed () override
154 auto config = Config::instance ();
156 checked_set (_master_encoding_threads, config->master_encoding_threads ());
157 checked_set (_server_encoding_threads, config->server_encoding_threads ());
158 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
159 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
161 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
162 checked_set (_config_file, config->config_read_file());
163 checked_set (_cinemas_file, config->cinemas_file());
165 GeneralPage::config_changed ();
168 void export_cinemas_file ()
170 auto d = new wxFileDialog (
171 _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
172 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
175 if (d->ShowModal () == wxID_OK) {
176 boost::filesystem::copy_file(Config::instance()->cinemas_file(), wx_to_std(d->GetPath()), boost::filesystem::copy_option::overwrite_if_exists);
181 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
182 void analyse_ebur128_changed ()
184 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
188 void automatic_audio_analysis_changed ()
190 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
193 void master_encoding_threads_changed ()
195 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
198 void server_encoding_threads_changed ()
200 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
203 void config_file_changed ()
205 auto config = Config::instance();
206 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
207 if (new_file == config->config_read_file()) {
210 bool copy_and_link = true;
211 if (boost::filesystem::exists(new_file)) {
212 auto d = new ConfigMoveDialog (_panel, new_file);
213 if (d->ShowModal() == wxID_OK) {
214 copy_and_link = false;
221 if (new_file != config->config_read_file()) {
222 config->copy_and_link (new_file);
225 config->link (new_file);
229 void cinemas_file_changed ()
231 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
234 wxSpinCtrl* _master_encoding_threads;
235 wxSpinCtrl* _server_encoding_threads;
236 FilePickerCtrl* _config_file;
237 FilePickerCtrl* _cinemas_file;
238 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
239 CheckBox* _analyse_ebur128;
241 CheckBox* _automatic_audio_analysis;
245 class DefaultsPage : public Page
248 DefaultsPage (wxSize panel_size, int border)
249 : Page (panel_size, border)
252 wxString GetName () const override
254 return _("Defaults");
258 wxBitmap GetLargeIcon () const override
260 return wxBitmap(icon_path("defaults"), wxBITMAP_TYPE_PNG);
265 void setup () override
267 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
268 table->AddGrowableCol (1, 1);
269 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
272 add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
273 auto s = new wxBoxSizer (wxHORIZONTAL);
274 _still_length = new wxSpinCtrl (_panel);
275 s->Add (_still_length);
276 add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
280 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
281 #ifdef DCPOMATIC_USE_OWN_PICKER
282 _directory = new DirPickerCtrl (_panel);
284 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
286 table->Add (_directory, 1, wxEXPAND);
288 add_label_to_sizer (table, _panel, _("Default container"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
289 _container = new wxChoice (_panel, wxID_ANY);
290 table->Add (_container);
292 add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
293 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
294 table->Add (_dcp_content_type);
296 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
297 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
298 table->Add (_dcp_audio_channels);
301 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
302 auto s = new wxBoxSizer (wxHORIZONTAL);
303 _j2k_bandwidth = new wxSpinCtrl (_panel);
304 s->Add (_j2k_bandwidth);
305 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
310 add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
311 auto s = new wxBoxSizer (wxHORIZONTAL);
312 _audio_delay = new wxSpinCtrl (_panel);
313 s->Add (_audio_delay);
314 add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
318 add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
319 _standard = new wxChoice (_panel, wxID_ANY);
320 table->Add (_standard);
322 table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
323 table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
325 table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
326 table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
328 table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
329 table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
331 table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
332 table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
334 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
335 #ifdef DCPOMATIC_USE_OWN_PICKER
336 _kdm_directory = new DirPickerCtrl (_panel);
338 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
340 table->Add (_kdm_directory, 1, wxEXPAND);
342 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
343 _kdm_type = new KDMChoice (_panel);
344 table->Add (_kdm_type, 1, wxEXPAND);
346 add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
347 _kdm_duration = new wxSpinCtrl (_panel);
348 _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
349 auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
350 kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
351 kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
352 table->Add (kdm_duration_sizer, 1, wxEXPAND);
354 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
356 _still_length->SetRange (1, 3600);
357 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
359 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
360 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
361 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
362 _kdm_duration_unit->Append (_("days"));
363 _kdm_duration_unit->Append (_("weeks"));
364 _kdm_duration_unit->Append (_("months"));
365 _kdm_duration_unit->Append (_("years"));
367 _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
368 _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
370 _use_isdcf_name_by_default->bind(&DefaultsPage::use_isdcf_name_by_default_changed, this);
372 for (auto i: Ratio::containers()) {
373 _container->Append (std_to_wx(i->container_nickname()));
376 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
378 for (auto i: DCPContentType::all()) {
379 _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
382 setup_audio_channels_choice (_dcp_audio_channels, 2);
384 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
385 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
387 _j2k_bandwidth->SetRange (50, 250);
388 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
390 _audio_delay->SetRange (-1000, 1000);
391 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
393 _standard->Append (_("SMPTE"));
394 _standard->Append (_("Interop"));
395 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
397 for (auto const& i: _enable_metadata) {
398 i.second->bind(&DefaultsPage::metadata_changed, this);
401 for (auto const& i: _metadata) {
402 i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
406 void config_changed () override
408 auto config = Config::instance ();
410 auto containers = Ratio::containers ();
411 for (size_t i = 0; i < containers.size(); ++i) {
412 if (containers[i] == config->default_container()) {
413 _container->SetSelection (i);
417 auto const ct = DCPContentType::all ();
418 for (size_t i = 0; i < ct.size(); ++i) {
419 if (ct[i] == config->default_dcp_content_type()) {
420 _dcp_content_type->SetSelection (i);
424 checked_set (_still_length, config->default_still_length ());
425 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
426 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
427 _kdm_type->set (config->default_kdm_type());
428 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
429 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
430 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
431 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
432 checked_set (_audio_delay, config->default_audio_delay ());
433 checked_set (_standard, config->default_interop() ? 1 : 0);
435 auto metadata = config->default_metadata();
437 for (auto const& i: metadata) {
438 _enable_metadata[i.first]->SetValue(true);
439 checked_set (_metadata[i.first], i.second);
442 for (auto const& i: _enable_metadata) {
443 if (metadata.find(i.first) == metadata.end()) {
444 checked_set (i.second, false);
448 for (auto const& i: _metadata) {
449 if (metadata.find(i.first) == metadata.end()) {
450 checked_set (i.second, wxT(""));
454 checked_set (_kdm_duration, config->default_kdm_duration().duration);
455 switch (config->default_kdm_duration().unit) {
456 case RoughDuration::Unit::DAYS:
457 _kdm_duration->SetRange(1, 365);
458 checked_set (_kdm_duration_unit, 0);
460 case RoughDuration::Unit::WEEKS:
461 _kdm_duration->SetRange(1, 52);
462 checked_set (_kdm_duration_unit, 1);
464 case RoughDuration::Unit::MONTHS:
465 _kdm_duration->SetRange(1, 12);
466 checked_set (_kdm_duration_unit, 2);
468 case RoughDuration::Unit::YEARS:
469 _kdm_duration->SetRange(1, 40);
470 checked_set (_kdm_duration_unit, 3);
474 setup_sensitivity ();
477 void kdm_duration_changed ()
479 auto config = Config::instance();
480 auto duration = _kdm_duration->GetValue();
481 RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
482 switch (_kdm_duration_unit->GetSelection()) {
484 unit = RoughDuration::Unit::DAYS;
487 unit = RoughDuration::Unit::WEEKS;
490 unit = RoughDuration::Unit::MONTHS;
493 unit = RoughDuration::Unit::YEARS;
496 config->set_default_kdm_duration (RoughDuration(duration, unit));
499 void j2k_bandwidth_changed ()
501 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
504 void audio_delay_changed ()
506 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
509 void dcp_audio_channels_changed ()
511 int const s = _dcp_audio_channels->GetSelection ();
512 if (s != wxNOT_FOUND) {
513 Config::instance()->set_default_dcp_audio_channels (
514 locale_convert<int>(string_client_data(_dcp_audio_channels->GetClientObject(s)))
519 void directory_changed ()
521 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
524 void kdm_directory_changed ()
526 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
529 void kdm_type_changed ()
531 Config::instance()->set_default_kdm_type(_kdm_type->get());
534 void use_isdcf_name_by_default_changed ()
536 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
539 void still_length_changed ()
541 Config::instance()->set_default_still_length (_still_length->GetValue ());
544 void container_changed ()
546 auto ratio = Ratio::containers ();
547 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
550 void dcp_content_type_changed ()
552 auto ct = DCPContentType::all ();
553 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
556 void standard_changed ()
558 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
561 void metadata_changed ()
563 map<string, string> metadata;
564 for (auto const& i: _enable_metadata) {
565 if (i.second->GetValue()) {
566 metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
569 Config::instance()->set_default_metadata (metadata);
570 setup_sensitivity ();
573 void setup_sensitivity ()
575 for (auto const& i: _enable_metadata) {
576 _metadata[i.first]->Enable(i.second->GetValue());
580 wxSpinCtrl* _j2k_bandwidth;
581 wxSpinCtrl* _audio_delay;
582 wxSpinCtrl* _still_length;
583 #ifdef DCPOMATIC_USE_OWN_PICKER
584 DirPickerCtrl* _directory;
585 DirPickerCtrl* _kdm_directory;
587 wxDirPickerCtrl* _directory;
588 wxDirPickerCtrl* _kdm_directory;
590 KDMChoice* _kdm_type;
591 wxSpinCtrl* _kdm_duration;
592 wxChoice* _kdm_duration_unit;
593 CheckBox* _use_isdcf_name_by_default;
594 wxChoice* _container;
595 wxChoice* _dcp_content_type;
596 wxChoice* _dcp_audio_channels;
598 map<string, CheckBox*> _enable_metadata;
599 map<string, wxTextCtrl*> _metadata;
603 class EncodingServersPage : public Page
606 EncodingServersPage (wxSize panel_size, int border)
607 : Page (panel_size, border)
610 wxString GetName () const override
616 wxBitmap GetLargeIcon () const override
618 return wxBitmap(icon_path("servers"), wxBITMAP_TYPE_PNG);
623 void setup () override
625 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
626 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
628 vector<EditableListColumn> columns;
629 columns.push_back (EditableListColumn(_("IP address / host name")));
630 _servers_list = new EditableList<string, ServerDialog> (
633 boost::bind (&Config::servers, Config::instance()),
634 boost::bind (&Config::set_servers, Config::instance(), _1),
635 boost::bind (&EncodingServersPage::server_column, this, _1),
636 EditableListTitle::INVISIBLE,
637 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
640 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
642 _use_any_servers->bind(&EncodingServersPage::use_any_servers_changed, this);
645 void config_changed () override
647 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
648 _servers_list->refresh ();
651 void use_any_servers_changed ()
653 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
656 string server_column (string s)
661 CheckBox* _use_any_servers;
662 EditableList<string, ServerDialog>* _servers_list;
666 class TMSPage : public Page
669 TMSPage (wxSize panel_size, int border)
670 : Page (panel_size, border)
673 wxString GetName () const override
679 wxBitmap GetLargeIcon () const override
681 return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
686 void setup () override
688 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
689 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
691 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
692 table->AddGrowableCol (1, 1);
693 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
695 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
696 _tms_protocol = new wxChoice (_panel, wxID_ANY);
697 table->Add (_tms_protocol, 1, wxEXPAND);
699 _tms_passive = new CheckBox(_panel, _("Passive mode"));
700 table->Add(_tms_passive, 1, wxEXPAND);
703 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
704 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
705 table->Add (_tms_ip, 1, wxEXPAND);
707 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
708 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
709 table->Add (_tms_path, 1, wxEXPAND);
711 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
712 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
713 table->Add (_tms_user, 1, wxEXPAND);
715 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
716 _tms_password = new PasswordEntry (_panel);
717 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
719 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
720 _tms_protocol->Append (_("FTP (for Dolby)"));
722 _upload->bind(&TMSPage::upload_changed, this);
723 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
724 _tms_passive->bind(&TMSPage::tms_passive_changed, this);
726 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
727 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
728 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
729 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
732 void config_changed () override
734 auto config = Config::instance ();
736 checked_set (_upload, config->upload_after_make_dcp());
737 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
738 checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
739 checked_set (_tms_ip, config->tms_ip ());
740 checked_set (_tms_path, config->tms_path ());
741 checked_set (_tms_user, config->tms_user ());
742 checked_set (_tms_password, config->tms_password ());
744 _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
747 void upload_changed ()
749 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
752 void tms_protocol_changed ()
754 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
757 void tms_passive_changed()
759 Config::instance()->set_tms_passive(_tms_passive->get());
762 void tms_ip_changed ()
764 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
767 void tms_path_changed ()
769 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
772 void tms_user_changed ()
774 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
777 void tms_password_changed ()
779 Config::instance()->set_tms_password (_tms_password->get());
783 CheckBox* _tms_passive;
784 wxChoice* _tms_protocol;
786 wxTextCtrl* _tms_path;
787 wxTextCtrl* _tms_user;
788 PasswordEntry* _tms_password;
792 class EmailPage : public Page
795 EmailPage (wxSize panel_size, int border)
796 : Page (panel_size, border)
799 wxString GetName () const override
805 wxBitmap GetLargeIcon () const override
807 return wxBitmap(icon_path("email"), wxBITMAP_TYPE_PNG);
812 void setup () override
814 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
815 table->AddGrowableCol (1, 1);
816 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
818 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
820 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
821 _server = new wxTextCtrl (_panel, wxID_ANY);
822 s->Add (_server, 1, wxEXPAND | wxALL);
823 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
824 _port = new wxSpinCtrl (_panel, wxID_ANY);
825 _port->SetRange (0, 65535);
827 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
828 _protocol = new wxChoice (_panel, wxID_ANY);
829 /* Make sure this matches the switches in config_changed and port_changed below */
830 _protocol->Append (_("Auto"));
831 _protocol->Append (_("Plain"));
832 _protocol->Append (_("STARTTLS"));
833 _protocol->Append (_("SSL"));
834 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
835 table->Add (s, 1, wxEXPAND | wxALL);
838 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
839 _user = new wxTextCtrl (_panel, wxID_ANY);
840 table->Add (_user, 1, wxEXPAND | wxALL);
842 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
843 _password = new PasswordEntry (_panel);
844 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
846 table->AddSpacer (0);
847 _send_test_email = new Button (_panel, _("Send test email..."));
848 table->Add (_send_test_email);
850 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
851 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
852 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
853 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
854 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
855 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
858 void config_changed () override
860 auto config = Config::instance ();
862 checked_set (_server, config->mail_server());
863 checked_set (_port, config->mail_port());
864 switch (config->mail_protocol()) {
865 case EmailProtocol::AUTO:
866 checked_set (_protocol, 0);
868 case EmailProtocol::PLAIN:
869 checked_set (_protocol, 1);
871 case EmailProtocol::STARTTLS:
872 checked_set (_protocol, 2);
874 case EmailProtocol::SSL:
875 checked_set (_protocol, 3);
878 checked_set (_user, config->mail_user());
879 checked_set (_password, config->mail_password());
882 void server_changed ()
884 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
889 Config::instance()->set_mail_port(_port->GetValue());
892 void protocol_changed ()
894 switch (_protocol->GetSelection()) {
896 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
899 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
902 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
905 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
912 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
915 void password_changed ()
917 Config::instance()->set_mail_password(_password->get());
920 void send_test_email_clicked ()
922 auto dialog = new SendTestEmailDialog(_panel);
923 auto result = dialog->ShowModal();
925 if (result == wxID_OK) {
927 wx_to_std(dialog->from()),
928 { wx_to_std(dialog->to()) },
929 wx_to_std(_("DCP-o-matic test email")),
930 wx_to_std(_("This is a test email from DCP-o-matic."))
932 auto config = Config::instance();
934 emailer.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
935 } catch (NetworkError& e) {
936 error_dialog (_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
938 } catch (std::exception& e) {
939 error_dialog (_panel, _("Test email sending failed."), std_to_wx(e.what()));
942 error_dialog (_panel, _("Test email sending failed."));
945 message_dialog (_panel, _("Test email sent."));
953 PasswordEntry* _password;
954 Button* _send_test_email;
958 class KDMEmailPage : public Page
962 KDMEmailPage (wxSize panel_size, int border)
964 /* We have to force both width and height of this one */
965 : Page (wxSize (panel_size.GetWidth(), 128), border)
967 : Page (panel_size, border)
971 wxString GetName () const override
973 return _("KDM Email");
977 wxBitmap GetLargeIcon () const override
979 return wxBitmap(icon_path("kdm_email"), wxBITMAP_TYPE_PNG);
984 void setup () override
986 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
987 table->AddGrowableCol (1, 1);
988 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
990 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
991 _subject = new wxTextCtrl (_panel, wxID_ANY);
992 table->Add (_subject, 1, wxEXPAND | wxALL);
994 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
995 _from = new wxTextCtrl (_panel, wxID_ANY);
996 table->Add (_from, 1, wxEXPAND | wxALL);
998 vector<EditableListColumn> columns;
999 columns.push_back (EditableListColumn(_("Address")));
1000 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1001 _cc = new EditableList<string, EmailDialog> (
1004 bind (&Config::kdm_cc, Config::instance()),
1005 bind (&Config::set_kdm_cc, Config::instance(), _1),
1006 [] (string s, int) {
1009 EditableListTitle::VISIBLE,
1010 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1012 table->Add (_cc, 1, wxEXPAND | wxALL);
1014 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1015 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1016 table->Add (_bcc, 1, wxEXPAND | wxALL);
1018 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1019 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1021 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1022 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1026 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1027 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1028 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1029 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1030 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
1033 void config_changed () override
1035 auto config = Config::instance ();
1037 checked_set (_subject, config->kdm_subject ());
1038 checked_set (_from, config->kdm_from ());
1039 checked_set (_bcc, config->kdm_bcc ());
1040 checked_set (_email, Config::instance()->kdm_email ());
1043 void kdm_subject_changed ()
1045 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
1048 void kdm_from_changed ()
1050 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
1053 void kdm_bcc_changed ()
1055 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
1058 void kdm_email_changed ()
1060 if (_email->GetValue().IsEmpty ()) {
1061 /* Sometimes we get sent an erroneous notification that the email
1062 is empty; I don't know why.
1066 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
1071 Config::instance()->reset_kdm_email ();
1072 checked_set (_email, Config::instance()->kdm_email ());
1075 wxTextCtrl* _subject;
1077 EditableList<string, EmailDialog>* _cc;
1080 wxButton* _reset_email;
1084 class NotificationsPage : public Page
1087 NotificationsPage (wxSize panel_size, int border)
1088 #ifdef DCPOMATIC_OSX
1089 /* We have to force both width and height of this one */
1090 : Page (wxSize (panel_size.GetWidth(), 128), border)
1092 : Page (panel_size, border)
1096 wxString GetName () const override
1098 return _("Notifications");
1101 #ifdef DCPOMATIC_OSX
1102 wxBitmap GetLargeIcon () const override
1104 return wxBitmap(icon_path("notifications"), wxBITMAP_TYPE_PNG);
1109 void setup () override
1111 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1112 table->AddGrowableCol (1, 1);
1113 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1115 _enable_message_box = new CheckBox (_panel, _("Message box"));
1116 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1117 table->AddSpacer (0);
1119 _enable_email = new CheckBox (_panel, _("Email"));
1120 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1121 table->AddSpacer (0);
1123 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1124 _subject = new wxTextCtrl (_panel, wxID_ANY);
1125 table->Add (_subject, 1, wxEXPAND | wxALL);
1127 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1128 _from = new wxTextCtrl (_panel, wxID_ANY);
1129 table->Add (_from, 1, wxEXPAND | wxALL);
1131 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1132 _to = new wxTextCtrl (_panel, wxID_ANY);
1133 table->Add (_to, 1, wxEXPAND | wxALL);
1135 vector<EditableListColumn> columns;
1136 columns.push_back (EditableListColumn(_("Address")));
1137 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1138 _cc = new EditableList<string, EmailDialog> (
1141 bind (&Config::notification_cc, Config::instance()),
1142 bind (&Config::set_notification_cc, Config::instance(), _1),
1143 [] (string s, int) {
1146 EditableListTitle::VISIBLE,
1147 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1149 table->Add (_cc, 1, wxEXPAND | wxALL);
1151 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1152 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1153 table->Add (_bcc, 1, wxEXPAND | wxALL);
1155 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1156 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1158 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1159 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1163 _enable_message_box->bind(&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX);
1164 _enable_email->bind(&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL);
1166 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1167 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1168 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1169 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1170 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1171 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1173 setup_sensitivity ();
1176 void setup_sensitivity ()
1178 bool const s = _enable_email->GetValue();
1179 _subject->Enable(s);
1185 _reset_email->Enable(s);
1188 void config_changed () override
1190 auto config = Config::instance ();
1192 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1193 checked_set (_enable_email, config->notification(Config::EMAIL));
1194 checked_set (_subject, config->notification_subject ());
1195 checked_set (_from, config->notification_from ());
1196 checked_set (_to, config->notification_to ());
1197 checked_set (_bcc, config->notification_bcc ());
1198 checked_set (_email, Config::instance()->notification_email ());
1200 setup_sensitivity ();
1203 void notification_subject_changed ()
1205 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1208 void notification_from_changed ()
1210 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1213 void notification_to_changed ()
1215 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1218 void notification_bcc_changed ()
1220 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1223 void notification_email_changed ()
1225 if (_email->GetValue().IsEmpty()) {
1226 /* Sometimes we get sent an erroneous notification that the email
1227 is empty; I don't know why.
1231 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1236 Config::instance()->reset_notification_email();
1237 checked_set (_email, Config::instance()->notification_email());
1240 void type_changed (CheckBox* b, Config::Notification n)
1242 Config::instance()->set_notification(n, b->GetValue());
1243 setup_sensitivity ();
1246 CheckBox* _enable_message_box;
1247 CheckBox* _enable_email;
1249 wxTextCtrl* _subject;
1252 EditableList<string, EmailDialog>* _cc;
1255 wxButton* _reset_email;
1259 class CoverSheetPage : public Page
1263 CoverSheetPage (wxSize panel_size, int border)
1264 #ifdef DCPOMATIC_OSX
1265 /* We have to force both width and height of this one */
1266 : Page (wxSize (panel_size.GetWidth(), 128), border)
1268 : Page (panel_size, border)
1272 wxString GetName () const override
1274 return _("Cover Sheet");
1277 #ifdef DCPOMATIC_OSX
1278 wxBitmap GetLargeIcon () const override
1280 return wxBitmap(icon_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1285 void setup () override
1287 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1288 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1290 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1291 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1293 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1294 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1297 void config_changed () override
1299 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1302 void cover_sheet_changed ()
1304 if (_cover_sheet->GetValue().IsEmpty ()) {
1305 /* Sometimes we get sent an erroneous notification that the cover sheet
1306 is empty; I don't know why.
1310 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1313 void reset_cover_sheet ()
1315 Config::instance()->reset_cover_sheet();
1316 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1319 wxTextCtrl* _cover_sheet;
1320 wxButton* _reset_cover_sheet;
1324 class IdentifiersPage : public Page
1327 IdentifiersPage (wxSize panel_size, int border)
1328 : Page (panel_size, border)
1331 wxString GetName () const override
1333 return _("Identifiers");
1336 #ifdef DCPOMATIC_OSX
1337 wxBitmap GetLargeIcon () const override
1339 return wxBitmap(icon_path("identifiers"), wxBITMAP_TYPE_PNG);
1344 void setup () override
1346 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1347 table->AddGrowableCol (1, 1);
1349 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1350 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1351 _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."));
1352 table->Add (_issuer, 1, wxALL | wxEXPAND);
1354 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1355 _creator = new wxTextCtrl (_panel, wxID_ANY);
1356 _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."));
1357 table->Add (_creator, 1, wxALL | wxEXPAND);
1359 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1360 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1361 _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."));
1362 table->Add (_company_name, 1, wxALL | wxEXPAND);
1364 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1365 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1366 _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."));
1367 table->Add (_product_name, 1, wxALL | wxEXPAND);
1369 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1370 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1371 _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."));
1372 table->Add (_product_version, 1, wxALL | wxEXPAND);
1374 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1375 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1376 _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."));
1377 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1379 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1381 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1382 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1383 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1384 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1385 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1386 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1389 void config_changed () override
1391 auto config = Config::instance ();
1392 checked_set (_issuer, config->dcp_issuer ());
1393 checked_set (_creator, config->dcp_creator ());
1394 checked_set (_company_name, config->dcp_company_name ());
1395 checked_set (_product_name, config->dcp_product_name ());
1396 checked_set (_product_version, config->dcp_product_version ());
1397 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1400 void issuer_changed ()
1402 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1405 void creator_changed ()
1407 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1410 void company_name_changed ()
1412 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1415 void product_name_changed ()
1417 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1420 void product_version_changed ()
1422 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1425 void j2k_comment_changed ()
1427 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1430 wxTextCtrl* _issuer;
1431 wxTextCtrl* _creator;
1432 wxTextCtrl* _company_name;
1433 wxTextCtrl* _product_name;
1434 wxTextCtrl* _product_version;
1435 wxTextCtrl* _j2k_comment;
1439 /** @class AdvancedPage
1440 * @brief Advanced page of the preferences dialog.
1442 class AdvancedPage : public Page
1445 AdvancedPage (wxSize panel_size, int border)
1446 : Page (panel_size, border)
1449 wxString GetName () const override
1451 return _("Advanced");
1454 #ifdef DCPOMATIC_OSX
1455 wxBitmap GetLargeIcon () const override
1457 return wxBitmap(icon_path("advanced"), wxBITMAP_TYPE_PNG);
1462 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1464 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1466 flags |= wxALIGN_RIGHT;
1469 wxStaticText* m = new StaticText (parent, text);
1470 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1473 void setup () override
1475 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1476 table->AddGrowableCol (1, 1);
1477 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1480 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1481 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1482 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1483 s->Add (_maximum_j2k_bandwidth, 1);
1484 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1488 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1489 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1490 table->Add (_video_display_mode);
1492 auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1493 auto font = restart->GetFont();
1494 font.SetStyle (wxFONTSTYLE_ITALIC);
1495 font.SetPointSize (font.GetPointSize() - 1);
1496 restart->SetFont (font);
1497 table->AddSpacer (0);
1499 _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
1500 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1501 table->AddSpacer (0);
1503 _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
1504 table->Add (_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1505 restart = new StaticText (_panel, _("(restart DCP-o-matic to see all ratios)"));
1506 table->Add (restart, 1, wxEXPAND | wxALL | wxALIGN_CENTRE_VERTICAL);
1507 restart->SetFont (font);
1509 _allow_96khz_audio = new CheckBox (_panel, _("Allow creation of DCPs with 96kHz audio"));
1510 table->Add (_allow_96khz_audio, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1511 table->AddSpacer (0);
1513 _use_all_audio_channels = new CheckBox(_panel, _("Allow mapping to all audio channels"));
1514 table->Add(_use_all_audio_channels, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1515 table->AddSpacer(0);
1517 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1518 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1519 table->AddSpacer (0);
1521 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1522 table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1523 table->AddSpacer (0);
1526 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1527 auto s = new wxBoxSizer (wxHORIZONTAL);
1528 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1529 s->Add (_frames_in_memory_multiplier, 1);
1534 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1535 #ifdef DCPOMATIC_OSX
1536 auto align = new wxBoxSizer (wxHORIZONTAL);
1537 align->Add (format, 0, wxTOP, 2);
1538 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1540 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1542 dcp::NameFormat::Map titles;
1543 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1544 dcp::NameFormat::Map examples;
1545 examples['t'] = "cpl";
1546 _dcp_metadata_filename_format = new NameFormatEditor (
1547 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1549 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1553 auto format = create_label (_panel, _("DCP asset filename format"), true);
1554 #ifdef DCPOMATIC_OSX
1555 auto align = new wxBoxSizer (wxHORIZONTAL);
1556 align->Add (format, 0, wxTOP, 2);
1557 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1559 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1561 dcp::NameFormat::Map titles;
1562 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1563 titles['r'] = wx_to_std (_("reel number"));
1564 titles['n'] = wx_to_std (_("number of reels"));
1565 titles['c'] = wx_to_std (_("content filename"));
1566 dcp::NameFormat::Map examples;
1567 examples['t'] = "j2c";
1568 examples['r'] = "1";
1569 examples['n'] = "4";
1570 examples['c'] = "myfile.mp4";
1571 _dcp_asset_filename_format = new NameFormatEditor (
1572 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1574 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1578 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1579 auto t = new wxFlexGridSizer (2);
1580 _log_general = new CheckBox (_panel, _("General"));
1581 t->Add (_log_general, 1, wxEXPAND | wxALL);
1582 _log_warning = new CheckBox (_panel, _("Warnings"));
1583 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1584 _log_error = new CheckBox (_panel, _("Errors"));
1585 t->Add (_log_error, 1, wxEXPAND | wxALL);
1586 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1587 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1588 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1589 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1590 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1591 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1592 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1593 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1594 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1595 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1596 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1597 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1598 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1599 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1600 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1601 table->Add (t, 0, wxALL, 6);
1604 #ifdef DCPOMATIC_WINDOWS
1605 _win32_console = new CheckBox (_panel, _("Open console window"));
1606 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1607 table->AddSpacer (0);
1610 _maximum_j2k_bandwidth->SetRange (1, 1000);
1611 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1612 _video_display_mode->Append (_("Simple (safer)"));
1613 #if wxCHECK_VERSION(3, 1, 0)
1614 _video_display_mode->Append (_("OpenGL (faster)"));
1616 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1617 _allow_any_dcp_frame_rate->bind(&AdvancedPage::allow_any_dcp_frame_rate_changed, this);
1618 _allow_any_container->bind(&AdvancedPage::allow_any_container_changed, this);
1619 _allow_96khz_audio->bind(&AdvancedPage::allow_96khz_audio_changed, this);
1620 _use_all_audio_channels->bind(&AdvancedPage::use_all_channels_changed, this);
1621 _show_experimental_audio_processors->bind(&AdvancedPage::show_experimental_audio_processors_changed, this);
1622 _only_servers_encode->bind(&AdvancedPage::only_servers_encode_changed, this);
1623 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1624 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1625 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1626 _log_general->bind(&AdvancedPage::log_changed, this);
1627 _log_warning->bind(&AdvancedPage::log_changed, this);
1628 _log_error->bind(&AdvancedPage::log_changed, this);
1629 _log_timing->bind(&AdvancedPage::log_changed, this);
1630 _log_debug_threed->bind(&AdvancedPage::log_changed, this);
1631 _log_debug_encode->bind(&AdvancedPage::log_changed, this);
1632 _log_debug_email->bind(&AdvancedPage::log_changed, this);
1633 _log_debug_video_view->bind(&AdvancedPage::log_changed, this);
1634 _log_debug_player->bind(&AdvancedPage::log_changed, this);
1635 _log_debug_audio_analysis->bind(&AdvancedPage::log_changed, this);
1636 #ifdef DCPOMATIC_WINDOWS
1637 _win32_console->bind(&AdvancedPage::win32_console_changed, this);
1641 void config_changed () override
1643 auto config = Config::instance ();
1645 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1646 switch (config->video_view_type()) {
1647 case Config::VIDEO_VIEW_SIMPLE:
1648 checked_set (_video_display_mode, 0);
1650 case Config::VIDEO_VIEW_OPENGL:
1651 checked_set (_video_display_mode, 1);
1654 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1655 checked_set (_allow_any_container, config->allow_any_container ());
1656 checked_set (_allow_96khz_audio, config->allow_96khz_audio());
1657 checked_set (_use_all_audio_channels, config->use_all_audio_channels());
1658 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1659 checked_set (_only_servers_encode, config->only_servers_encode ());
1660 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1661 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1662 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1663 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1664 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1665 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1666 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1667 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1668 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1669 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1670 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1671 #ifdef DCPOMATIC_WINDOWS
1672 checked_set (_win32_console, config->win32_console());
1676 void maximum_j2k_bandwidth_changed ()
1678 Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
1681 void video_display_mode_changed ()
1683 if (_video_display_mode->GetSelection() == 0) {
1684 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1686 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1690 void frames_in_memory_multiplier_changed ()
1692 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1695 void allow_any_dcp_frame_rate_changed ()
1697 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1700 void allow_any_container_changed ()
1702 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1705 void allow_96khz_audio_changed ()
1707 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1710 void use_all_channels_changed ()
1712 Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
1715 void show_experimental_audio_processors_changed ()
1717 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1720 void only_servers_encode_changed ()
1722 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1725 void dcp_metadata_filename_format_changed ()
1727 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1730 void dcp_asset_filename_format_changed ()
1732 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1738 if (_log_general->GetValue ()) {
1739 types |= LogEntry::TYPE_GENERAL;
1741 if (_log_warning->GetValue ()) {
1742 types |= LogEntry::TYPE_WARNING;
1744 if (_log_error->GetValue ()) {
1745 types |= LogEntry::TYPE_ERROR;
1747 if (_log_timing->GetValue ()) {
1748 types |= LogEntry::TYPE_TIMING;
1750 if (_log_debug_threed->GetValue ()) {
1751 types |= LogEntry::TYPE_DEBUG_THREE_D;
1753 if (_log_debug_encode->GetValue ()) {
1754 types |= LogEntry::TYPE_DEBUG_ENCODE;
1756 if (_log_debug_email->GetValue ()) {
1757 types |= LogEntry::TYPE_DEBUG_EMAIL;
1759 if (_log_debug_video_view->GetValue()) {
1760 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1762 if (_log_debug_player->GetValue()) {
1763 types |= LogEntry::TYPE_DEBUG_PLAYER;
1765 if (_log_debug_audio_analysis->GetValue()) {
1766 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1768 Config::instance()->set_log_types (types);
1771 #ifdef DCPOMATIC_WINDOWS
1772 void win32_console_changed ()
1774 Config::instance()->set_win32_console(_win32_console->GetValue());
1778 wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
1779 wxChoice* _video_display_mode = nullptr;
1780 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1781 CheckBox* _allow_any_dcp_frame_rate = nullptr;
1782 CheckBox* _allow_any_container = nullptr;
1783 CheckBox* _allow_96khz_audio = nullptr;
1784 CheckBox* _use_all_audio_channels = nullptr;
1785 CheckBox* _show_experimental_audio_processors = nullptr;
1786 CheckBox* _only_servers_encode = nullptr;
1787 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1788 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1789 CheckBox* _log_general = nullptr;
1790 CheckBox* _log_warning = nullptr;
1791 CheckBox* _log_error = nullptr;
1792 CheckBox* _log_timing = nullptr;
1793 CheckBox* _log_debug_threed = nullptr;
1794 CheckBox* _log_debug_encode = nullptr;
1795 CheckBox* _log_debug_email = nullptr;
1796 CheckBox* _log_debug_video_view = nullptr;
1797 CheckBox* _log_debug_player = nullptr;
1798 CheckBox* _log_debug_audio_analysis = nullptr;
1799 #ifdef DCPOMATIC_WINDOWS
1800 CheckBox* _win32_console = nullptr;
1805 wxPreferencesEditor*
1806 create_full_config_dialog ()
1808 auto e = new wxPreferencesEditor ();
1810 #ifdef DCPOMATIC_OSX
1811 /* Width that we force some of the config panels to be on OSX so that
1812 the containing window doesn't shrink too much when we select those panels.
1813 This is obviously an unpleasant hack.
1815 wxSize ps = wxSize (750, -1);
1816 int const border = 16;
1818 wxSize ps = wxSize (-1, -1);
1819 int const border = 8;
1822 e->AddPage (new FullGeneralPage (ps, border));
1823 e->AddPage (new SoundPage (ps, border));
1824 e->AddPage (new DefaultsPage (ps, border));
1825 e->AddPage (new EncodingServersPage(ps, border));
1826 e->AddPage (new KeysPage (ps, border));
1827 e->AddPage (new TMSPage (ps, border));
1828 e->AddPage (new EmailPage (ps, border));
1829 e->AddPage (new KDMEmailPage (ps, border));
1830 e->AddPage (new NotificationsPage (ps, border));
1831 e->AddPage (new CoverSheetPage (ps, border));
1832 e->AddPage (new IdentifiersPage (ps, border));
1833 e->AddPage (new AdvancedPage (ps, border));