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 "dcpomatic_choice.h"
32 #include "dir_picker_ctrl.h"
33 #include "editable_list.h"
34 #include "email_dialog.h"
35 #include "file_picker_ctrl.h"
36 #include "filter_dialog.h"
37 #include "full_config_dialog.h"
38 #include "kdm_choice.h"
39 #include "language_tag_widget.h"
40 #include "make_chain_dialog.h"
41 #include "nag_dialog.h"
42 #include "name_format_editor.h"
43 #include "password_entry.h"
44 #include "region_subtag_widget.h"
45 #include "send_test_email_dialog.h"
46 #include "server_dialog.h"
47 #include "static_text.h"
49 #include "grok/gpu_config_panel.h"
52 #include "wx_variant.h"
53 #include "lib/config.h"
54 #include "lib/cross.h"
55 #include "lib/email.h"
56 #include "lib/exceptions.h"
57 #include "lib/filter.h"
59 #include "lib/ratio.h"
61 #include <dcp/certificate_chain.h>
62 #include <dcp/exceptions.h>
63 #include <dcp/filesystem.h>
64 #include <dcp/locale_convert.h>
65 #include <dcp/warnings.h>
66 LIBDCP_DISABLE_WARNINGS
67 #include <wx/filepicker.h>
68 #include <wx/preferences.h>
69 #include <wx/spinctrl.h>
70 #include <wx/stdpaths.h>
71 LIBDCP_ENABLE_WARNINGS
73 #include <boost/filesystem.hpp>
83 using std::shared_ptr;
87 using boost::optional;
88 #if BOOST_VERSION >= 106100
89 using namespace boost::placeholders;
91 using dcp::locale_convert;
94 class FullGeneralPage : public GeneralPage
97 FullGeneralPage (wxSize panel_size, int border)
98 : GeneralPage (panel_size, border)
102 void setup () override
104 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
105 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
108 add_language_controls (table, r);
110 add_label_to_sizer(table, _panel, variant::wx::insert_dcpomatic(_("Number of threads %s should use")), true, wxGBPosition(r, 0));
111 _master_encoding_threads = new wxSpinCtrl (_panel);
112 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
115 add_label_to_sizer(table, _panel, variant::wx::insert_dcpomatic_encode_server(_("Number of threads %s should use")), true, wxGBPosition(r, 0));
116 _server_encoding_threads = new wxSpinCtrl (_panel);
117 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
120 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
121 _config_file = new FilePickerCtrl(_panel, _("Select configuration file"), "*.xml", true, false, "ConfigFilePath");
122 table->Add (_config_file, wxGBPosition (r, 1));
125 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
126 _cinemas_file = new FilePickerCtrl(_panel, _("Select cinema and screen database file"), "*.xml", true, false, "CinemaDatabasePath");
127 table->Add (_cinemas_file, wxGBPosition (r, 1));
128 auto export_cinemas = new Button (_panel, _("Export..."));
129 table->Add (export_cinemas, wxGBPosition (r, 2));
132 add_label_to_sizer(table, _panel, _("Default \"add file\" location"), true, wxGBPosition(r, 0));
133 _default_add_file_location = new Choice(_panel);
134 table->Add(_default_add_file_location, wxGBPosition(r, 1));
137 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
138 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
139 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
143 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
144 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
147 add_update_controls (table, r);
149 _default_add_file_location->add_entry(_("Same place as last time"));
150 _default_add_file_location->add_entry(_("Same place as project"));
151 _default_add_file_location->bind(&FullGeneralPage::default_add_file_location_changed, this);
153 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
154 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
156 _master_encoding_threads->SetRange (1, 128);
157 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
158 _server_encoding_threads->SetRange (1, 128);
159 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
160 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
162 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
163 _analyse_ebur128->bind(&FullGeneralPage::analyse_ebur128_changed, this);
165 _automatic_audio_analysis->bind(&FullGeneralPage::automatic_audio_analysis_changed, this);
168 void config_changed () override
170 auto config = Config::instance ();
172 checked_set (_master_encoding_threads, config->master_encoding_threads ());
173 checked_set (_server_encoding_threads, config->server_encoding_threads ());
174 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
175 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
177 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
178 checked_set (_config_file, config->config_read_file());
179 checked_set (_cinemas_file, config->cinemas_file());
180 checked_set(_default_add_file_location, config->default_add_file_location() == Config::DefaultAddFileLocation::SAME_AS_LAST_TIME ? 0 : 1);
182 GeneralPage::config_changed ();
185 void export_cinemas_file ()
188 _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
189 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
192 if (dialog.ShowModal() == wxID_OK) {
193 dcp::filesystem::copy_file(Config::instance()->cinemas_file(), wx_to_std(dialog.GetPath()), boost::filesystem::copy_option::overwrite_if_exists);
197 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
198 void analyse_ebur128_changed ()
200 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
204 void automatic_audio_analysis_changed ()
206 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
209 void master_encoding_threads_changed ()
211 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
214 void server_encoding_threads_changed ()
216 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
219 void config_file_changed ()
221 auto config = Config::instance();
222 auto const new_file = _config_file->path();
223 if (!new_file || *new_file == config->config_read_file()) {
226 bool copy_and_link = true;
227 if (dcp::filesystem::exists(*new_file)) {
228 ConfigMoveDialog dialog(_panel, *new_file);
229 if (dialog.ShowModal() == wxID_OK) {
230 copy_and_link = false;
236 if (new_file != config->config_read_file()) {
237 config->copy_and_link(*new_file);
240 config->link(*new_file);
244 void cinemas_file_changed ()
246 if (auto path = _cinemas_file->path()) {
247 Config::instance()->set_cinemas_file(*path);
251 void default_add_file_location_changed()
253 Config::instance()->set_default_add_file_location(
254 _default_add_file_location->get().get_value_or(0) == 0 ? Config::DefaultAddFileLocation::SAME_AS_LAST_TIME : Config::DefaultAddFileLocation::SAME_AS_PROJECT
258 Choice* _default_add_file_location;
259 wxSpinCtrl* _master_encoding_threads;
260 wxSpinCtrl* _server_encoding_threads;
261 FilePickerCtrl* _config_file;
262 FilePickerCtrl* _cinemas_file;
263 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
264 CheckBox* _analyse_ebur128;
266 CheckBox* _automatic_audio_analysis;
270 class DefaultsPage : public Page
273 DefaultsPage (wxSize panel_size, int border)
274 : Page (panel_size, border)
277 wxString GetName () const override
279 return _("Defaults");
283 wxBitmap GetLargeIcon () const override
285 return wxBitmap(icon_path("defaults"), wxBITMAP_TYPE_PNG);
290 void setup () override
292 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
293 table->AddGrowableCol (1, 1);
294 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
297 add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
298 auto s = new wxBoxSizer (wxHORIZONTAL);
299 _still_length = new wxSpinCtrl (_panel);
300 s->Add (_still_length);
301 add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
305 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
306 #ifdef DCPOMATIC_USE_OWN_PICKER
307 _directory = new DirPickerCtrl (_panel);
309 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
311 table->Add (_directory, 1, wxEXPAND);
314 add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
315 auto s = new wxBoxSizer (wxHORIZONTAL);
316 _audio_delay = new wxSpinCtrl (_panel);
317 s->Add (_audio_delay);
318 add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
322 _enable_audio_language = new CheckBox(_panel, _("Default audio language"));
323 table->Add(_enable_audio_language, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL);
324 _audio_language = new LanguageTagWidget(_panel, _("Default audio language to use for new DCPs"), Config::instance()->default_audio_language(), wxString("cmnr-Hant-"));
325 table->Add(_audio_language->sizer());
327 _enable_territory = new CheckBox(_panel, _("Default territory"));
328 table->Add(_enable_territory, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL);
329 _territory = new RegionSubtagWidget(_panel, _("Default territory to use for new DCPs"), Config::instance()->default_territory(), wxString("cmnr-Hant-"));
330 table->Add(_territory->sizer());
332 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
333 #ifdef DCPOMATIC_USE_OWN_PICKER
334 _kdm_directory = new DirPickerCtrl (_panel);
336 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
338 table->Add (_kdm_directory, 1, wxEXPAND);
340 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
341 _kdm_type = new KDMChoice (_panel);
342 table->Add (_kdm_type, 1, wxEXPAND);
344 add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
345 _kdm_duration = new wxSpinCtrl (_panel);
346 _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
347 auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
348 kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
349 kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
350 table->Add (kdm_duration_sizer, 1, wxEXPAND);
352 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
354 _still_length->SetRange (1, 3600);
355 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
357 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
358 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
359 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
360 _kdm_duration_unit->Append (_("days"));
361 _kdm_duration_unit->Append (_("weeks"));
362 _kdm_duration_unit->Append (_("months"));
363 _kdm_duration_unit->Append (_("years"));
365 _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
366 _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
368 _use_isdcf_name_by_default->bind(&DefaultsPage::use_isdcf_name_by_default_changed, this);
370 _audio_delay->SetRange (-1000, 1000);
371 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
373 _enable_audio_language->bind(&DefaultsPage::enable_audio_language_toggled, this);
374 _audio_language->Changed.connect(boost::bind(&DefaultsPage::audio_language_changed, this));
376 _enable_territory->bind(&DefaultsPage::enable_territory_toggled, this);
377 _territory->Changed.connect(boost::bind(&DefaultsPage::territory_changed, this));
380 void config_changed () override
382 auto config = Config::instance ();
383 checked_set (_still_length, config->default_still_length ());
384 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
385 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
386 _kdm_type->set (config->default_kdm_type());
387 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
388 checked_set (_audio_delay, config->default_audio_delay ());
389 auto dal = config->default_audio_language();
390 checked_set(_enable_audio_language, static_cast<bool>(dal));
391 checked_set(_audio_language, dal ? dal : boost::none);
392 auto dt = config->default_territory();
393 checked_set(_enable_territory, static_cast<bool>(dt));
394 checked_set(_territory, dt ? dt : boost::none);
396 checked_set (_kdm_duration, config->default_kdm_duration().duration);
397 switch (config->default_kdm_duration().unit) {
398 case RoughDuration::Unit::DAYS:
399 _kdm_duration->SetRange(1, 365);
400 checked_set (_kdm_duration_unit, 0);
402 case RoughDuration::Unit::WEEKS:
403 _kdm_duration->SetRange(1, 52);
404 checked_set (_kdm_duration_unit, 1);
406 case RoughDuration::Unit::MONTHS:
407 _kdm_duration->SetRange(1, 12);
408 checked_set (_kdm_duration_unit, 2);
410 case RoughDuration::Unit::YEARS:
411 _kdm_duration->SetRange(1, 40);
412 checked_set (_kdm_duration_unit, 3);
416 setup_sensitivity ();
419 void kdm_duration_changed ()
421 auto config = Config::instance();
422 auto duration = _kdm_duration->GetValue();
423 RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
424 switch (_kdm_duration_unit->GetSelection()) {
426 unit = RoughDuration::Unit::DAYS;
429 unit = RoughDuration::Unit::WEEKS;
432 unit = RoughDuration::Unit::MONTHS;
435 unit = RoughDuration::Unit::YEARS;
438 config->set_default_kdm_duration (RoughDuration(duration, unit));
441 void audio_delay_changed ()
443 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
446 void directory_changed ()
448 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
451 void kdm_directory_changed ()
453 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
456 void kdm_type_changed ()
458 Config::instance()->set_default_kdm_type(_kdm_type->get());
461 void use_isdcf_name_by_default_changed ()
463 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
466 void still_length_changed ()
468 Config::instance()->set_default_still_length (_still_length->GetValue ());
471 void enable_audio_language_toggled()
474 audio_language_changed();
477 void audio_language_changed()
479 if (_enable_audio_language->get()) {
480 Config::instance()->set_default_audio_language(_audio_language->get().get_value_or(dcp::LanguageTag("en-US")));
482 Config::instance()->unset_default_audio_language();
486 void enable_territory_toggled()
492 void territory_changed()
494 if (_enable_territory->get()) {
495 Config::instance()->set_default_territory(_territory->get().get_value_or(dcp::LanguageTag::RegionSubtag("US")));
497 Config::instance()->unset_default_territory();
501 void setup_sensitivity ()
503 _audio_language->enable(_enable_audio_language->get());
504 _territory->enable(_enable_territory->get());
507 wxSpinCtrl* _audio_delay;
508 wxSpinCtrl* _still_length;
509 #ifdef DCPOMATIC_USE_OWN_PICKER
510 DirPickerCtrl* _directory;
511 DirPickerCtrl* _kdm_directory;
513 wxDirPickerCtrl* _directory;
514 wxDirPickerCtrl* _kdm_directory;
516 KDMChoice* _kdm_type;
517 wxSpinCtrl* _kdm_duration;
518 wxChoice* _kdm_duration_unit;
519 CheckBox* _use_isdcf_name_by_default;
520 CheckBox* _enable_audio_language;
521 LanguageTagWidget* _audio_language;
522 CheckBox* _enable_territory;
523 RegionSubtagWidget* _territory;
527 class EncodingServersPage : public Page
530 EncodingServersPage (wxSize panel_size, int border)
531 : Page (panel_size, border)
534 wxString GetName () const override
540 wxBitmap GetLargeIcon () const override
542 return wxBitmap(icon_path("servers"), wxBITMAP_TYPE_PNG);
547 void setup () override
549 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
550 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
552 vector<EditableListColumn> columns;
553 columns.push_back (EditableListColumn(_("IP address / host name")));
554 _servers_list = new EditableList<string, ServerDialog> (
557 boost::bind (&Config::servers, Config::instance()),
558 boost::bind (&Config::set_servers, Config::instance(), _1),
559 boost::bind (&EncodingServersPage::server_column, this, _1),
560 EditableListTitle::INVISIBLE,
561 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
564 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
566 _use_any_servers->bind(&EncodingServersPage::use_any_servers_changed, this);
569 void config_changed () override
571 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
572 _servers_list->refresh ();
575 void use_any_servers_changed ()
577 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
580 string server_column (string s)
585 CheckBox* _use_any_servers;
586 EditableList<string, ServerDialog>* _servers_list;
590 class TMSPage : public Page
593 TMSPage (wxSize panel_size, int border)
594 : Page (panel_size, border)
597 wxString GetName () const override
603 wxBitmap GetLargeIcon () const override
605 return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
610 void setup () override
612 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
613 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
615 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
616 table->AddGrowableCol (1, 1);
617 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
619 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
620 _tms_protocol = new wxChoice (_panel, wxID_ANY);
621 table->Add (_tms_protocol, 1, wxEXPAND);
623 _tms_passive = new CheckBox(_panel, _("Passive mode"));
624 table->Add(_tms_passive, 1, wxEXPAND);
627 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
628 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
629 table->Add (_tms_ip, 1, wxEXPAND);
631 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
632 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
633 table->Add (_tms_path, 1, wxEXPAND);
635 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
636 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
637 table->Add (_tms_user, 1, wxEXPAND);
639 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
640 _tms_password = new PasswordEntry (_panel);
641 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
643 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
644 _tms_protocol->Append (_("FTP (for Dolby)"));
646 _upload->bind(&TMSPage::upload_changed, this);
647 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
648 _tms_passive->bind(&TMSPage::tms_passive_changed, this);
650 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
651 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
652 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
653 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
656 void config_changed () override
658 auto config = Config::instance ();
660 checked_set (_upload, config->upload_after_make_dcp());
661 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
662 checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
663 checked_set (_tms_ip, config->tms_ip ());
664 checked_set (_tms_path, config->tms_path ());
665 checked_set (_tms_user, config->tms_user ());
666 checked_set (_tms_password, config->tms_password ());
668 _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
671 void upload_changed ()
673 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
676 void tms_protocol_changed ()
678 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
681 void tms_passive_changed()
683 Config::instance()->set_tms_passive(_tms_passive->get());
686 void tms_ip_changed ()
688 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
691 void tms_path_changed ()
693 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
696 void tms_user_changed ()
698 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
701 void tms_password_changed ()
703 Config::instance()->set_tms_password (_tms_password->get());
707 CheckBox* _tms_passive;
708 wxChoice* _tms_protocol;
710 wxTextCtrl* _tms_path;
711 wxTextCtrl* _tms_user;
712 PasswordEntry* _tms_password;
716 class EmailPage : public Page
719 EmailPage (wxSize panel_size, int border)
720 : Page (panel_size, border)
723 wxString GetName () const override
729 wxBitmap GetLargeIcon () const override
731 return wxBitmap(icon_path("email"), wxBITMAP_TYPE_PNG);
736 void setup () override
738 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
739 table->AddGrowableCol (1, 1);
740 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
742 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
744 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
745 _server = new wxTextCtrl (_panel, wxID_ANY);
746 s->Add (_server, 1, wxEXPAND | wxALL);
747 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
748 _port = new wxSpinCtrl (_panel, wxID_ANY);
749 _port->SetRange (0, 65535);
751 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
752 _protocol = new wxChoice (_panel, wxID_ANY);
753 /* Make sure this matches the switches in config_changed and port_changed below */
754 _protocol->Append (_("Auto"));
755 _protocol->Append (_("Plain"));
756 _protocol->Append (_("STARTTLS"));
757 _protocol->Append (_("SSL"));
758 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
759 table->Add (s, 1, wxEXPAND | wxALL);
762 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
763 _user = new wxTextCtrl (_panel, wxID_ANY);
764 table->Add (_user, 1, wxEXPAND | wxALL);
766 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
767 _password = new PasswordEntry (_panel);
768 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
770 table->AddSpacer (0);
771 _send_test_email = new Button (_panel, _("Send test email..."));
772 table->Add (_send_test_email);
774 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
775 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
776 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
777 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
778 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
779 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
782 void config_changed () override
784 auto config = Config::instance ();
786 checked_set (_server, config->mail_server());
787 checked_set (_port, config->mail_port());
788 switch (config->mail_protocol()) {
789 case EmailProtocol::AUTO:
790 checked_set (_protocol, 0);
792 case EmailProtocol::PLAIN:
793 checked_set (_protocol, 1);
795 case EmailProtocol::STARTTLS:
796 checked_set (_protocol, 2);
798 case EmailProtocol::SSL:
799 checked_set (_protocol, 3);
802 checked_set (_user, config->mail_user());
803 checked_set (_password, config->mail_password());
806 void server_changed ()
808 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
813 Config::instance()->set_mail_port(_port->GetValue());
816 void protocol_changed ()
818 switch (_protocol->GetSelection()) {
820 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
823 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
826 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
829 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
836 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
839 void password_changed ()
841 Config::instance()->set_mail_password(_password->get());
844 void send_test_email_clicked ()
846 SendTestEmailDialog dialog(_panel);
847 if (dialog.ShowModal() != wxID_OK) {
852 wx_to_std(dialog.from()),
853 { wx_to_std(dialog.to()) },
854 wx_to_std(variant::wx::insert_dcpomatic(_("%s test email"))),
855 wx_to_std(variant::wx::insert_dcpomatic(_("This is a test email from %s.")))
857 auto config = Config::instance();
859 email.send(config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
860 } catch (NetworkError& e) {
861 error_dialog(_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
863 } catch (std::exception& e) {
864 error_dialog(_panel, _("Test email sending failed."), std_to_wx(e.what()));
867 error_dialog(_panel, _("Test email sending failed."));
870 message_dialog(_panel, _("Test email sent."));
877 PasswordEntry* _password;
878 Button* _send_test_email;
882 class KDMEmailPage : public Page
886 KDMEmailPage (wxSize panel_size, int border)
888 /* We have to force both width and height of this one */
889 : Page (wxSize (panel_size.GetWidth(), 128), border)
891 : Page (panel_size, border)
895 wxString GetName () const override
897 return _("KDM Email");
901 wxBitmap GetLargeIcon () const override
903 return wxBitmap(icon_path("kdm_email"), wxBITMAP_TYPE_PNG);
908 void setup () override
910 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
911 table->AddGrowableCol (1, 1);
912 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
914 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
915 _subject = new wxTextCtrl (_panel, wxID_ANY);
916 table->Add (_subject, 1, wxEXPAND | wxALL);
918 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
919 _from = new wxTextCtrl (_panel, wxID_ANY);
920 table->Add (_from, 1, wxEXPAND | wxALL);
922 vector<EditableListColumn> columns;
923 columns.push_back (EditableListColumn(_("Address")));
924 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
925 _cc = new EditableList<string, EmailDialog> (
928 bind (&Config::kdm_cc, Config::instance()),
929 bind (&Config::set_kdm_cc, Config::instance(), _1),
933 EditableListTitle::VISIBLE,
934 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
936 table->Add (_cc, 1, wxEXPAND | wxALL);
938 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
939 _bcc = new wxTextCtrl (_panel, wxID_ANY);
940 table->Add (_bcc, 1, wxEXPAND | wxALL);
942 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
943 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
945 _reset_email = new Button (_panel, _("Reset to default subject and text"));
946 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
950 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
951 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
952 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
953 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
954 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
957 void config_changed () override
959 auto config = Config::instance ();
961 checked_set (_subject, config->kdm_subject ());
962 checked_set (_from, config->kdm_from ());
963 checked_set (_bcc, config->kdm_bcc ());
964 checked_set (_email, Config::instance()->kdm_email ());
967 void kdm_subject_changed ()
969 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
972 void kdm_from_changed ()
974 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
977 void kdm_bcc_changed ()
979 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
982 void kdm_email_changed ()
984 if (_email->GetValue().IsEmpty ()) {
985 /* Sometimes we get sent an erroneous notification that the email
986 is empty; I don't know why.
990 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
995 Config::instance()->reset_kdm_email ();
996 checked_set (_email, Config::instance()->kdm_email ());
999 wxTextCtrl* _subject;
1001 EditableList<string, EmailDialog>* _cc;
1004 wxButton* _reset_email;
1008 class NotificationsPage : public Page
1011 NotificationsPage (wxSize panel_size, int border)
1012 #ifdef DCPOMATIC_OSX
1013 /* We have to force both width and height of this one */
1014 : Page (wxSize (panel_size.GetWidth(), 128), border)
1016 : Page (panel_size, border)
1020 wxString GetName () const override
1022 return _("Notifications");
1025 #ifdef DCPOMATIC_OSX
1026 wxBitmap GetLargeIcon () const override
1028 return wxBitmap(icon_path("notifications"), wxBITMAP_TYPE_PNG);
1033 void setup () override
1035 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1036 table->AddGrowableCol (1, 1);
1037 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1039 _enable_message_box = new CheckBox (_panel, _("Message box"));
1040 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1041 table->AddSpacer (0);
1043 _enable_email = new CheckBox (_panel, _("Email"));
1044 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1045 table->AddSpacer (0);
1047 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1048 _subject = new wxTextCtrl (_panel, wxID_ANY);
1049 table->Add (_subject, 1, wxEXPAND | wxALL);
1051 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1052 _from = new wxTextCtrl (_panel, wxID_ANY);
1053 table->Add (_from, 1, wxEXPAND | wxALL);
1055 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1056 _to = new wxTextCtrl (_panel, wxID_ANY);
1057 table->Add (_to, 1, wxEXPAND | wxALL);
1059 vector<EditableListColumn> columns;
1060 columns.push_back (EditableListColumn(_("Address")));
1061 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1062 _cc = new EditableList<string, EmailDialog> (
1065 bind (&Config::notification_cc, Config::instance()),
1066 bind (&Config::set_notification_cc, Config::instance(), _1),
1067 [] (string s, int) {
1070 EditableListTitle::VISIBLE,
1071 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1073 table->Add (_cc, 1, wxEXPAND | wxALL);
1075 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1076 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1077 table->Add (_bcc, 1, wxEXPAND | wxALL);
1079 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1080 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1082 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1083 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1087 _enable_message_box->bind(&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX);
1088 _enable_email->bind(&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL);
1090 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1091 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1092 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1093 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1094 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1095 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1097 setup_sensitivity ();
1100 void setup_sensitivity ()
1102 bool const s = _enable_email->GetValue();
1103 _subject->Enable(s);
1109 _reset_email->Enable(s);
1112 void config_changed () override
1114 auto config = Config::instance ();
1116 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1117 checked_set (_enable_email, config->notification(Config::EMAIL));
1118 checked_set (_subject, config->notification_subject ());
1119 checked_set (_from, config->notification_from ());
1120 checked_set (_to, config->notification_to ());
1121 checked_set (_bcc, config->notification_bcc ());
1122 checked_set (_email, Config::instance()->notification_email ());
1124 setup_sensitivity ();
1127 void notification_subject_changed ()
1129 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1132 void notification_from_changed ()
1134 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1137 void notification_to_changed ()
1139 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1142 void notification_bcc_changed ()
1144 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1147 void notification_email_changed ()
1149 if (_email->GetValue().IsEmpty()) {
1150 /* Sometimes we get sent an erroneous notification that the email
1151 is empty; I don't know why.
1155 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1160 Config::instance()->reset_notification_email();
1161 checked_set (_email, Config::instance()->notification_email());
1164 void type_changed (CheckBox* b, Config::Notification n)
1166 Config::instance()->set_notification(n, b->GetValue());
1167 setup_sensitivity ();
1170 CheckBox* _enable_message_box;
1171 CheckBox* _enable_email;
1173 wxTextCtrl* _subject;
1176 EditableList<string, EmailDialog>* _cc;
1179 wxButton* _reset_email;
1183 class CoverSheetPage : public Page
1187 CoverSheetPage (wxSize panel_size, int border)
1188 #ifdef DCPOMATIC_OSX
1189 /* We have to force both width and height of this one */
1190 : Page (wxSize (panel_size.GetWidth(), 128), border)
1192 : Page (panel_size, border)
1196 wxString GetName () const override
1198 return _("Cover Sheet");
1201 #ifdef DCPOMATIC_OSX
1202 wxBitmap GetLargeIcon () const override
1204 return wxBitmap(icon_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1209 void setup () override
1211 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1212 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1214 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1215 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1217 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1218 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1221 void config_changed () override
1223 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1226 void cover_sheet_changed ()
1228 if (_cover_sheet->GetValue().IsEmpty ()) {
1229 /* Sometimes we get sent an erroneous notification that the cover sheet
1230 is empty; I don't know why.
1234 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1237 void reset_cover_sheet ()
1239 Config::instance()->reset_cover_sheet();
1240 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1243 wxTextCtrl* _cover_sheet;
1244 wxButton* _reset_cover_sheet;
1248 class IdentifiersPage : public Page
1251 IdentifiersPage (wxSize panel_size, int border)
1252 : Page (panel_size, border)
1255 wxString GetName () const override
1257 return _("Identifiers");
1260 #ifdef DCPOMATIC_OSX
1261 wxBitmap GetLargeIcon () const override
1263 return wxBitmap(icon_path("identifiers"), wxBITMAP_TYPE_PNG);
1268 void setup () override
1270 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1271 table->AddGrowableCol (1, 1);
1273 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1274 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1275 _issuer->SetToolTip(
1276 variant::wx::insert_dcpomatic(
1277 _("This will be written to the DCP's XML files as the <Issuer>. If it is blank, a default value mentioning %s will be used.")
1279 table->Add (_issuer, 1, wxALL | wxEXPAND);
1281 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1282 _creator = new wxTextCtrl (_panel, wxID_ANY);
1283 _creator->SetToolTip(
1284 variant::wx::insert_dcpomatic(
1285 _("This will be written to the DCP's XML files as the <Creator>. If it is blank, a default value mentioning %s will be used.")
1287 table->Add (_creator, 1, wxALL | wxEXPAND);
1289 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1290 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1291 _company_name->SetToolTip(
1292 variant::wx::insert_dcpomatic(
1293 _("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 %s library) will be used.")
1295 table->Add (_company_name, 1, wxALL | wxEXPAND);
1297 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1298 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1299 _product_name->SetToolTip(
1300 variant::wx::insert_dcpomatic(
1301 _("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 %s library) will be used.")
1303 table->Add (_product_name, 1, wxALL | wxEXPAND);
1305 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1306 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1307 _product_version->SetToolTip(
1308 variant::wx::insert_dcpomatic(
1309 _("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 %s library) will be used.")
1311 table->Add (_product_version, 1, wxALL | wxEXPAND);
1313 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1314 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1315 _j2k_comment->SetToolTip(
1316 variant::wx::insert_dcpomatic(
1317 _("This will be written to the DCP's JPEG2000 data as a comment. If it is blank, a default value mentioning libdcp (an internal %s library) will be used.")
1319 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1321 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1323 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1324 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1325 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1326 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1327 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1328 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1331 void config_changed () override
1333 auto config = Config::instance ();
1334 checked_set (_issuer, config->dcp_issuer ());
1335 checked_set (_creator, config->dcp_creator ());
1336 checked_set (_company_name, config->dcp_company_name ());
1337 checked_set (_product_name, config->dcp_product_name ());
1338 checked_set (_product_version, config->dcp_product_version ());
1339 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1342 void issuer_changed ()
1344 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1347 void creator_changed ()
1349 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1352 void company_name_changed ()
1354 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1357 void product_name_changed ()
1359 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1362 void product_version_changed ()
1364 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1367 void j2k_comment_changed ()
1369 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1372 wxTextCtrl* _issuer;
1373 wxTextCtrl* _creator;
1374 wxTextCtrl* _company_name;
1375 wxTextCtrl* _product_name;
1376 wxTextCtrl* _product_version;
1377 wxTextCtrl* _j2k_comment;
1381 class NonStandardPage : public Page
1384 NonStandardPage(wxSize panel_size, int border)
1385 : Page(panel_size, border)
1388 wxString GetName() const override
1390 return _("Non-standard");
1393 #ifdef DCPOMATIC_OSX
1394 wxBitmap GetLargeIcon() const override
1396 return wxBitmap(icon_path("non_standard"), wxBITMAP_TYPE_PNG);
1401 void setup() override
1403 auto table = new wxFlexGridSizer(2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1404 table->AddGrowableCol(1, 1);
1405 _panel->GetSizer()->Add(table, 1, wxALL | wxEXPAND, _border);
1408 add_label_to_sizer(table, _panel, _("Maximum JPEG2000 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1409 auto s = new wxBoxSizer(wxHORIZONTAL);
1410 _maximum_j2k_video_bit_rate = new wxSpinCtrl(_panel);
1411 s->Add(_maximum_j2k_video_bit_rate, 1);
1412 add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1417 add_label_to_sizer(table, _panel, _("Maximum MPEG2 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1418 auto s = new wxBoxSizer(wxHORIZONTAL);
1419 _maximum_mpeg2_video_bit_rate = new wxSpinCtrl(_panel);
1420 s->Add(_maximum_mpeg2_video_bit_rate, 1);
1421 add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1425 auto checkbox = [this, table](wxString name, CheckBox*& variable) {
1426 variable = new CheckBox(_panel, name);
1427 table->Add(variable, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1428 table->AddSpacer(0);
1431 checkbox(_("Allow any DCP frame rate"), _allow_any_dcp_frame_rate);
1433 _allow_any_container = new CheckBox(_panel, _("Allow full-frame and non-standard container ratios"));
1434 table->Add(_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1435 table->AddSpacer(0);
1437 checkbox(_("Allow creation of DCPs with 96kHz audio"), _allow_96khz_audio);
1438 checkbox(_("Allow mapping to all audio channels"), _use_all_audio_channels);
1439 checkbox(_("Allow use of SMPTE Bv2.0"), _allow_smpte_bv20);
1442 add_label_to_sizer(table, _panel, _("ISDCF name part length"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1443 auto s = new wxBoxSizer(wxHORIZONTAL);
1444 _isdcf_name_part_length = new wxSpinCtrl(_panel);
1445 s->Add(_isdcf_name_part_length, 1);
1449 _maximum_j2k_video_bit_rate->SetRange(1, 1000);
1450 _maximum_j2k_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_j2k_video_bit_rate_changed, this));
1451 _maximum_mpeg2_video_bit_rate->SetRange(1, 100);
1452 _maximum_mpeg2_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_mpeg2_video_bit_rate_changed, this));
1453 _allow_any_dcp_frame_rate->bind(&NonStandardPage::allow_any_dcp_frame_rate_changed, this);
1454 _allow_any_container->bind(&NonStandardPage::allow_any_container_changed, this);
1455 _allow_96khz_audio->bind(&NonStandardPage::allow_96khz_audio_changed, this);
1456 _use_all_audio_channels->bind(&NonStandardPage::use_all_channels_changed, this);
1457 _allow_smpte_bv20->bind(&NonStandardPage::allow_smpte_bv20_changed, this);
1458 _isdcf_name_part_length->SetRange(1, 256);
1459 _isdcf_name_part_length->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::isdcf_name_part_length_changed, this));
1462 void config_changed() override
1464 auto config = Config::instance();
1466 checked_set(_maximum_j2k_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::JPEG2000) / 1000000);
1467 checked_set(_maximum_mpeg2_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::MPEG2) / 1000000);
1468 checked_set(_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate());
1469 checked_set(_allow_any_container, config->allow_any_container());
1470 checked_set(_allow_96khz_audio, config->allow_96khz_audio());
1471 checked_set(_use_all_audio_channels, config->use_all_audio_channels());
1472 checked_set(_allow_smpte_bv20, config->allow_smpte_bv20());
1473 checked_set(_isdcf_name_part_length, config->isdcf_name_part_length());
1476 void maximum_j2k_video_bit_rate_changed()
1478 Config::instance()->set_maximum_video_bit_rate(VideoEncoding::JPEG2000, _maximum_j2k_video_bit_rate->GetValue() * 1000000);
1481 void maximum_mpeg2_video_bit_rate_changed()
1483 Config::instance()->set_maximum_video_bit_rate(VideoEncoding::MPEG2, _maximum_mpeg2_video_bit_rate->GetValue() * 1000000);
1486 void allow_any_dcp_frame_rate_changed()
1488 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1491 void allow_any_container_changed()
1493 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1496 void allow_96khz_audio_changed()
1498 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1501 void use_all_channels_changed()
1503 Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
1506 void allow_smpte_bv20_changed()
1508 Config::instance()->set_allow_smpte_bv20(_allow_smpte_bv20->GetValue());
1511 void isdcf_name_part_length_changed()
1513 Config::instance()->set_isdcf_name_part_length(_isdcf_name_part_length->GetValue());
1516 wxSpinCtrl* _maximum_j2k_video_bit_rate = nullptr;
1517 wxSpinCtrl* _maximum_mpeg2_video_bit_rate = nullptr;
1518 CheckBox* _allow_any_dcp_frame_rate = nullptr;
1519 CheckBox* _allow_any_container = nullptr;
1520 CheckBox* _allow_96khz_audio = nullptr;
1521 CheckBox* _use_all_audio_channels = nullptr;
1522 CheckBox* _allow_smpte_bv20 = nullptr;
1523 wxSpinCtrl* _isdcf_name_part_length = nullptr;
1528 /** @class AdvancedPage
1529 * @brief Advanced page of the preferences dialog.
1531 class AdvancedPage : public Page
1534 AdvancedPage (wxSize panel_size, int border)
1535 : Page (panel_size, border)
1538 wxString GetName () const override
1540 return _("Advanced");
1543 #ifdef DCPOMATIC_OSX
1544 wxBitmap GetLargeIcon () const override
1546 return wxBitmap(icon_path("advanced"), wxBITMAP_TYPE_PNG);
1551 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1553 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1555 flags |= wxALIGN_RIGHT;
1558 wxStaticText* m = new StaticText (parent, text);
1559 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1562 void setup () override
1564 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1565 table->AddGrowableCol (1, 1);
1566 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1568 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1569 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1570 table->Add (_video_display_mode);
1572 auto restart = add_label_to_sizer(table, _panel, variant::wx::insert_dcpomatic(_("(restart %s to change display mode)")), false);
1573 auto font = restart->GetFont();
1574 font.SetStyle (wxFONTSTYLE_ITALIC);
1575 font.SetPointSize (font.GetPointSize() - 1);
1576 restart->SetFont (font);
1577 table->AddSpacer (0);
1579 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1580 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1581 table->AddSpacer (0);
1583 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1584 table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1585 table->AddSpacer (0);
1588 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1589 auto s = new wxBoxSizer (wxHORIZONTAL);
1590 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1591 s->Add (_frames_in_memory_multiplier, 1);
1596 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1597 #ifdef DCPOMATIC_OSX
1598 auto align = new wxBoxSizer (wxHORIZONTAL);
1599 align->Add (format, 0, wxTOP, 2);
1600 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1602 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1604 dcp::NameFormat::Map titles;
1605 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1606 dcp::NameFormat::Map examples;
1607 examples['t'] = "cpl";
1608 _dcp_metadata_filename_format = new NameFormatEditor (
1609 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1611 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1615 auto format = create_label (_panel, _("DCP asset filename format"), true);
1616 #ifdef DCPOMATIC_OSX
1617 auto align = new wxBoxSizer (wxHORIZONTAL);
1618 align->Add (format, 0, wxTOP, 2);
1619 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1621 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1623 dcp::NameFormat::Map titles;
1624 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1625 titles['r'] = wx_to_std (_("reel number"));
1626 titles['n'] = wx_to_std (_("number of reels"));
1627 titles['c'] = wx_to_std (_("content filename"));
1628 dcp::NameFormat::Map examples;
1629 examples['t'] = "j2c";
1630 examples['r'] = "1";
1631 examples['n'] = "4";
1632 examples['c'] = "myfile.mp4";
1633 _dcp_asset_filename_format = new NameFormatEditor (
1634 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1636 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1640 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1641 auto t = new wxFlexGridSizer (2);
1642 _log_general = new CheckBox (_panel, _("General"));
1643 t->Add (_log_general, 1, wxEXPAND | wxALL);
1644 _log_warning = new CheckBox (_panel, _("Warnings"));
1645 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1646 _log_error = new CheckBox (_panel, _("Errors"));
1647 t->Add (_log_error, 1, wxEXPAND | wxALL);
1648 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1649 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1650 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1651 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1652 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1653 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1654 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1655 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1656 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1657 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1658 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1659 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1660 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1661 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1662 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1663 table->Add (t, 0, wxALL, 6);
1666 #ifdef DCPOMATIC_WINDOWS
1667 _win32_console = new CheckBox (_panel, _("Open console window"));
1668 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1669 table->AddSpacer (0);
1672 _video_display_mode->Append (_("Simple (safer)"));
1673 #if wxCHECK_VERSION(3, 1, 0)
1674 _video_display_mode->Append (_("OpenGL (faster)"));
1676 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1677 _show_experimental_audio_processors->bind(&AdvancedPage::show_experimental_audio_processors_changed, this);
1678 _only_servers_encode->bind(&AdvancedPage::only_servers_encode_changed, this);
1679 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1680 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1681 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1682 _log_general->bind(&AdvancedPage::log_changed, this);
1683 _log_warning->bind(&AdvancedPage::log_changed, this);
1684 _log_error->bind(&AdvancedPage::log_changed, this);
1685 _log_timing->bind(&AdvancedPage::log_changed, this);
1686 _log_debug_threed->bind(&AdvancedPage::log_changed, this);
1687 _log_debug_encode->bind(&AdvancedPage::log_changed, this);
1688 _log_debug_email->bind(&AdvancedPage::log_changed, this);
1689 _log_debug_video_view->bind(&AdvancedPage::log_changed, this);
1690 _log_debug_player->bind(&AdvancedPage::log_changed, this);
1691 _log_debug_audio_analysis->bind(&AdvancedPage::log_changed, this);
1692 #ifdef DCPOMATIC_WINDOWS
1693 _win32_console->bind(&AdvancedPage::win32_console_changed, this);
1697 void config_changed () override
1699 auto config = Config::instance ();
1701 switch (config->video_view_type()) {
1702 case Config::VIDEO_VIEW_SIMPLE:
1703 checked_set (_video_display_mode, 0);
1705 case Config::VIDEO_VIEW_OPENGL:
1706 checked_set (_video_display_mode, 1);
1709 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1710 checked_set (_only_servers_encode, config->only_servers_encode ());
1711 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1712 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1713 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1714 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1715 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1716 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1717 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1718 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1719 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1720 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1721 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1722 #ifdef DCPOMATIC_WINDOWS
1723 checked_set (_win32_console, config->win32_console());
1727 void video_display_mode_changed ()
1729 if (_video_display_mode->GetSelection() == 0) {
1730 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1732 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1736 void frames_in_memory_multiplier_changed ()
1738 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1741 void show_experimental_audio_processors_changed ()
1743 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1746 void only_servers_encode_changed ()
1748 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1751 void dcp_metadata_filename_format_changed ()
1753 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1756 void dcp_asset_filename_format_changed ()
1758 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1764 if (_log_general->GetValue ()) {
1765 types |= LogEntry::TYPE_GENERAL;
1767 if (_log_warning->GetValue ()) {
1768 types |= LogEntry::TYPE_WARNING;
1770 if (_log_error->GetValue ()) {
1771 types |= LogEntry::TYPE_ERROR;
1773 if (_log_timing->GetValue ()) {
1774 types |= LogEntry::TYPE_TIMING;
1776 if (_log_debug_threed->GetValue ()) {
1777 types |= LogEntry::TYPE_DEBUG_THREE_D;
1779 if (_log_debug_encode->GetValue ()) {
1780 types |= LogEntry::TYPE_DEBUG_ENCODE;
1782 if (_log_debug_email->GetValue ()) {
1783 types |= LogEntry::TYPE_DEBUG_EMAIL;
1785 if (_log_debug_video_view->GetValue()) {
1786 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1788 if (_log_debug_player->GetValue()) {
1789 types |= LogEntry::TYPE_DEBUG_PLAYER;
1791 if (_log_debug_audio_analysis->GetValue()) {
1792 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1794 Config::instance()->set_log_types (types);
1797 #ifdef DCPOMATIC_WINDOWS
1798 void win32_console_changed ()
1800 Config::instance()->set_win32_console(_win32_console->GetValue());
1804 wxChoice* _video_display_mode = nullptr;
1805 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1806 CheckBox* _show_experimental_audio_processors = nullptr;
1807 CheckBox* _only_servers_encode = nullptr;
1808 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1809 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1810 CheckBox* _log_general = nullptr;
1811 CheckBox* _log_warning = nullptr;
1812 CheckBox* _log_error = nullptr;
1813 CheckBox* _log_timing = nullptr;
1814 CheckBox* _log_debug_threed = nullptr;
1815 CheckBox* _log_debug_encode = nullptr;
1816 CheckBox* _log_debug_email = nullptr;
1817 CheckBox* _log_debug_video_view = nullptr;
1818 CheckBox* _log_debug_player = nullptr;
1819 CheckBox* _log_debug_audio_analysis = nullptr;
1820 #ifdef DCPOMATIC_WINDOWS
1821 CheckBox* _win32_console = nullptr;
1826 wxPreferencesEditor*
1827 create_full_config_dialog ()
1829 auto e = new wxPreferencesEditor ();
1831 #ifdef DCPOMATIC_OSX
1832 /* Width that we force some of the config panels to be on OSX so that
1833 the containing window doesn't shrink too much when we select those panels.
1834 This is obviously an unpleasant hack.
1836 wxSize ps = wxSize(900, -1);
1837 int const border = 16;
1839 wxSize ps = wxSize (-1, -1);
1840 int const border = 8;
1843 e->AddPage (new FullGeneralPage (ps, border));
1844 e->AddPage (new SoundPage (ps, border));
1845 e->AddPage (new DefaultsPage (ps, border));
1846 e->AddPage (new EncodingServersPage(ps, border));
1847 #ifdef DCPOMATIC_GROK
1848 e->AddPage (new GPUPage (ps, border));
1850 e->AddPage (new KeysPage (ps, border));
1851 e->AddPage (new TMSPage (ps, border));
1852 e->AddPage (new EmailPage (ps, border));
1853 e->AddPage (new KDMEmailPage (ps, border));
1854 e->AddPage (new NotificationsPage (ps, border));
1855 e->AddPage (new CoverSheetPage (ps, border));
1856 e->AddPage (new IdentifiersPage (ps, border));
1857 e->AddPage (new NonStandardPage (ps, border));
1858 e->AddPage (new AdvancedPage (ps, border));