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 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
328 #ifdef DCPOMATIC_USE_OWN_PICKER
329 _kdm_directory = new DirPickerCtrl (_panel);
331 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
333 table->Add (_kdm_directory, 1, wxEXPAND);
335 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
336 _kdm_type = new KDMChoice (_panel);
337 table->Add (_kdm_type, 1, wxEXPAND);
339 add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
340 _kdm_duration = new wxSpinCtrl (_panel);
341 _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
342 auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
343 kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
344 kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
345 table->Add (kdm_duration_sizer, 1, wxEXPAND);
347 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
349 _still_length->SetRange (1, 3600);
350 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
352 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
353 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
354 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
355 _kdm_duration_unit->Append (_("days"));
356 _kdm_duration_unit->Append (_("weeks"));
357 _kdm_duration_unit->Append (_("months"));
358 _kdm_duration_unit->Append (_("years"));
360 _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
361 _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
363 _use_isdcf_name_by_default->bind(&DefaultsPage::use_isdcf_name_by_default_changed, this);
365 _audio_delay->SetRange (-1000, 1000);
366 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
368 _enable_audio_language->bind(&DefaultsPage::enable_audio_language_toggled, this);
369 _audio_language->Changed.connect(boost::bind(&DefaultsPage::audio_language_changed, this));
372 void config_changed () override
374 auto config = Config::instance ();
375 checked_set (_still_length, config->default_still_length ());
376 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
377 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
378 _kdm_type->set (config->default_kdm_type());
379 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
380 checked_set (_audio_delay, config->default_audio_delay ());
381 auto dal = config->default_audio_language();
382 checked_set(_enable_audio_language, static_cast<bool>(dal));
383 checked_set(_audio_language, dal ? dal : boost::none);
385 checked_set (_kdm_duration, config->default_kdm_duration().duration);
386 switch (config->default_kdm_duration().unit) {
387 case RoughDuration::Unit::DAYS:
388 _kdm_duration->SetRange(1, 365);
389 checked_set (_kdm_duration_unit, 0);
391 case RoughDuration::Unit::WEEKS:
392 _kdm_duration->SetRange(1, 52);
393 checked_set (_kdm_duration_unit, 1);
395 case RoughDuration::Unit::MONTHS:
396 _kdm_duration->SetRange(1, 12);
397 checked_set (_kdm_duration_unit, 2);
399 case RoughDuration::Unit::YEARS:
400 _kdm_duration->SetRange(1, 40);
401 checked_set (_kdm_duration_unit, 3);
405 setup_sensitivity ();
408 void kdm_duration_changed ()
410 auto config = Config::instance();
411 auto duration = _kdm_duration->GetValue();
412 RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
413 switch (_kdm_duration_unit->GetSelection()) {
415 unit = RoughDuration::Unit::DAYS;
418 unit = RoughDuration::Unit::WEEKS;
421 unit = RoughDuration::Unit::MONTHS;
424 unit = RoughDuration::Unit::YEARS;
427 config->set_default_kdm_duration (RoughDuration(duration, unit));
430 void audio_delay_changed ()
432 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
435 void directory_changed ()
437 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
440 void kdm_directory_changed ()
442 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
445 void kdm_type_changed ()
447 Config::instance()->set_default_kdm_type(_kdm_type->get());
450 void use_isdcf_name_by_default_changed ()
452 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
455 void still_length_changed ()
457 Config::instance()->set_default_still_length (_still_length->GetValue ());
460 void enable_audio_language_toggled()
463 audio_language_changed();
466 void audio_language_changed()
468 if (_enable_audio_language->get()) {
469 Config::instance()->set_default_audio_language(_audio_language->get().get_value_or(dcp::LanguageTag("en-US")));
471 Config::instance()->unset_default_audio_language();
475 void setup_sensitivity ()
477 _audio_language->enable(_enable_audio_language->get());
480 wxSpinCtrl* _audio_delay;
481 wxSpinCtrl* _still_length;
482 #ifdef DCPOMATIC_USE_OWN_PICKER
483 DirPickerCtrl* _directory;
484 DirPickerCtrl* _kdm_directory;
486 wxDirPickerCtrl* _directory;
487 wxDirPickerCtrl* _kdm_directory;
489 KDMChoice* _kdm_type;
490 wxSpinCtrl* _kdm_duration;
491 wxChoice* _kdm_duration_unit;
492 CheckBox* _use_isdcf_name_by_default;
493 CheckBox* _enable_audio_language;
494 LanguageTagWidget* _audio_language;
498 class EncodingServersPage : public Page
501 EncodingServersPage (wxSize panel_size, int border)
502 : Page (panel_size, border)
505 wxString GetName () const override
511 wxBitmap GetLargeIcon () const override
513 return wxBitmap(icon_path("servers"), wxBITMAP_TYPE_PNG);
518 void setup () override
520 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
521 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
523 vector<EditableListColumn> columns;
524 columns.push_back (EditableListColumn(_("IP address / host name")));
525 _servers_list = new EditableList<string, ServerDialog> (
528 boost::bind (&Config::servers, Config::instance()),
529 boost::bind (&Config::set_servers, Config::instance(), _1),
530 boost::bind (&EncodingServersPage::server_column, this, _1),
531 EditableListTitle::INVISIBLE,
532 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
535 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
537 _use_any_servers->bind(&EncodingServersPage::use_any_servers_changed, this);
540 void config_changed () override
542 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
543 _servers_list->refresh ();
546 void use_any_servers_changed ()
548 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
551 string server_column (string s)
556 CheckBox* _use_any_servers;
557 EditableList<string, ServerDialog>* _servers_list;
561 class TMSPage : public Page
564 TMSPage (wxSize panel_size, int border)
565 : Page (panel_size, border)
568 wxString GetName () const override
574 wxBitmap GetLargeIcon () const override
576 return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
581 void setup () override
583 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
584 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
586 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
587 table->AddGrowableCol (1, 1);
588 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
590 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
591 _tms_protocol = new wxChoice (_panel, wxID_ANY);
592 table->Add (_tms_protocol, 1, wxEXPAND);
594 _tms_passive = new CheckBox(_panel, _("Passive mode"));
595 table->Add(_tms_passive, 1, wxEXPAND);
598 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
599 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
600 table->Add (_tms_ip, 1, wxEXPAND);
602 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
603 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
604 table->Add (_tms_path, 1, wxEXPAND);
606 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
607 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
608 table->Add (_tms_user, 1, wxEXPAND);
610 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
611 _tms_password = new PasswordEntry (_panel);
612 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
614 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
615 _tms_protocol->Append (_("FTP (for Dolby)"));
617 _upload->bind(&TMSPage::upload_changed, this);
618 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
619 _tms_passive->bind(&TMSPage::tms_passive_changed, this);
621 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
622 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
623 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
624 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
627 void config_changed () override
629 auto config = Config::instance ();
631 checked_set (_upload, config->upload_after_make_dcp());
632 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
633 checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
634 checked_set (_tms_ip, config->tms_ip ());
635 checked_set (_tms_path, config->tms_path ());
636 checked_set (_tms_user, config->tms_user ());
637 checked_set (_tms_password, config->tms_password ());
639 _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
642 void upload_changed ()
644 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
647 void tms_protocol_changed ()
649 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
652 void tms_passive_changed()
654 Config::instance()->set_tms_passive(_tms_passive->get());
657 void tms_ip_changed ()
659 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
662 void tms_path_changed ()
664 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
667 void tms_user_changed ()
669 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
672 void tms_password_changed ()
674 Config::instance()->set_tms_password (_tms_password->get());
678 CheckBox* _tms_passive;
679 wxChoice* _tms_protocol;
681 wxTextCtrl* _tms_path;
682 wxTextCtrl* _tms_user;
683 PasswordEntry* _tms_password;
687 class EmailPage : public Page
690 EmailPage (wxSize panel_size, int border)
691 : Page (panel_size, border)
694 wxString GetName () const override
700 wxBitmap GetLargeIcon () const override
702 return wxBitmap(icon_path("email"), wxBITMAP_TYPE_PNG);
707 void setup () override
709 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
710 table->AddGrowableCol (1, 1);
711 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
713 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
715 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
716 _server = new wxTextCtrl (_panel, wxID_ANY);
717 s->Add (_server, 1, wxEXPAND | wxALL);
718 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
719 _port = new wxSpinCtrl (_panel, wxID_ANY);
720 _port->SetRange (0, 65535);
722 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
723 _protocol = new wxChoice (_panel, wxID_ANY);
724 /* Make sure this matches the switches in config_changed and port_changed below */
725 _protocol->Append (_("Auto"));
726 _protocol->Append (_("Plain"));
727 _protocol->Append (_("STARTTLS"));
728 _protocol->Append (_("SSL"));
729 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
730 table->Add (s, 1, wxEXPAND | wxALL);
733 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
734 _user = new wxTextCtrl (_panel, wxID_ANY);
735 table->Add (_user, 1, wxEXPAND | wxALL);
737 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
738 _password = new PasswordEntry (_panel);
739 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
741 table->AddSpacer (0);
742 _send_test_email = new Button (_panel, _("Send test email..."));
743 table->Add (_send_test_email);
745 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
746 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
747 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
748 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
749 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
750 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
753 void config_changed () override
755 auto config = Config::instance ();
757 checked_set (_server, config->mail_server());
758 checked_set (_port, config->mail_port());
759 switch (config->mail_protocol()) {
760 case EmailProtocol::AUTO:
761 checked_set (_protocol, 0);
763 case EmailProtocol::PLAIN:
764 checked_set (_protocol, 1);
766 case EmailProtocol::STARTTLS:
767 checked_set (_protocol, 2);
769 case EmailProtocol::SSL:
770 checked_set (_protocol, 3);
773 checked_set (_user, config->mail_user());
774 checked_set (_password, config->mail_password());
777 void server_changed ()
779 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
784 Config::instance()->set_mail_port(_port->GetValue());
787 void protocol_changed ()
789 switch (_protocol->GetSelection()) {
791 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
794 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
797 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
800 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
807 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
810 void password_changed ()
812 Config::instance()->set_mail_password(_password->get());
815 void send_test_email_clicked ()
817 SendTestEmailDialog dialog(_panel);
818 if (dialog.ShowModal() != wxID_OK) {
823 wx_to_std(dialog.from()),
824 { wx_to_std(dialog.to()) },
825 wx_to_std(variant::wx::insert_dcpomatic(_("%s test email"))),
826 wx_to_std(variant::wx::insert_dcpomatic(_("This is a test email from %s.")))
828 auto config = Config::instance();
830 email.send(config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
831 } catch (NetworkError& e) {
832 error_dialog(_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
834 } catch (std::exception& e) {
835 error_dialog(_panel, _("Test email sending failed."), std_to_wx(e.what()));
838 error_dialog(_panel, _("Test email sending failed."));
841 message_dialog(_panel, _("Test email sent."));
848 PasswordEntry* _password;
849 Button* _send_test_email;
853 class KDMEmailPage : public Page
857 KDMEmailPage (wxSize panel_size, int border)
859 /* We have to force both width and height of this one */
860 : Page (wxSize (panel_size.GetWidth(), 128), border)
862 : Page (panel_size, border)
866 wxString GetName () const override
868 return _("KDM Email");
872 wxBitmap GetLargeIcon () const override
874 return wxBitmap(icon_path("kdm_email"), wxBITMAP_TYPE_PNG);
879 void setup () override
881 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
882 table->AddGrowableCol (1, 1);
883 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
885 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
886 _subject = new wxTextCtrl (_panel, wxID_ANY);
887 table->Add (_subject, 1, wxEXPAND | wxALL);
889 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
890 _from = new wxTextCtrl (_panel, wxID_ANY);
891 table->Add (_from, 1, wxEXPAND | wxALL);
893 vector<EditableListColumn> columns;
894 columns.push_back (EditableListColumn(_("Address")));
895 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
896 _cc = new EditableList<string, EmailDialog> (
899 bind (&Config::kdm_cc, Config::instance()),
900 bind (&Config::set_kdm_cc, Config::instance(), _1),
904 EditableListTitle::VISIBLE,
905 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
907 table->Add (_cc, 1, wxEXPAND | wxALL);
909 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
910 _bcc = new wxTextCtrl (_panel, wxID_ANY);
911 table->Add (_bcc, 1, wxEXPAND | wxALL);
913 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
914 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
916 _reset_email = new Button (_panel, _("Reset to default subject and text"));
917 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
921 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
922 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
923 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
924 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
925 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
928 void config_changed () override
930 auto config = Config::instance ();
932 checked_set (_subject, config->kdm_subject ());
933 checked_set (_from, config->kdm_from ());
934 checked_set (_bcc, config->kdm_bcc ());
935 checked_set (_email, Config::instance()->kdm_email ());
938 void kdm_subject_changed ()
940 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
943 void kdm_from_changed ()
945 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
948 void kdm_bcc_changed ()
950 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
953 void kdm_email_changed ()
955 if (_email->GetValue().IsEmpty ()) {
956 /* Sometimes we get sent an erroneous notification that the email
957 is empty; I don't know why.
961 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
966 Config::instance()->reset_kdm_email ();
967 checked_set (_email, Config::instance()->kdm_email ());
970 wxTextCtrl* _subject;
972 EditableList<string, EmailDialog>* _cc;
975 wxButton* _reset_email;
979 class NotificationsPage : public Page
982 NotificationsPage (wxSize panel_size, int border)
984 /* We have to force both width and height of this one */
985 : Page (wxSize (panel_size.GetWidth(), 128), border)
987 : Page (panel_size, border)
991 wxString GetName () const override
993 return _("Notifications");
997 wxBitmap GetLargeIcon () const override
999 return wxBitmap(icon_path("notifications"), wxBITMAP_TYPE_PNG);
1004 void setup () override
1006 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1007 table->AddGrowableCol (1, 1);
1008 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1010 _enable_message_box = new CheckBox (_panel, _("Message box"));
1011 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1012 table->AddSpacer (0);
1014 _enable_email = new CheckBox (_panel, _("Email"));
1015 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1016 table->AddSpacer (0);
1018 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1019 _subject = new wxTextCtrl (_panel, wxID_ANY);
1020 table->Add (_subject, 1, wxEXPAND | wxALL);
1022 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1023 _from = new wxTextCtrl (_panel, wxID_ANY);
1024 table->Add (_from, 1, wxEXPAND | wxALL);
1026 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1027 _to = new wxTextCtrl (_panel, wxID_ANY);
1028 table->Add (_to, 1, wxEXPAND | wxALL);
1030 vector<EditableListColumn> columns;
1031 columns.push_back (EditableListColumn(_("Address")));
1032 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1033 _cc = new EditableList<string, EmailDialog> (
1036 bind (&Config::notification_cc, Config::instance()),
1037 bind (&Config::set_notification_cc, Config::instance(), _1),
1038 [] (string s, int) {
1041 EditableListTitle::VISIBLE,
1042 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1044 table->Add (_cc, 1, wxEXPAND | wxALL);
1046 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1047 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1048 table->Add (_bcc, 1, wxEXPAND | wxALL);
1050 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1051 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1053 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1054 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1058 _enable_message_box->bind(&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX);
1059 _enable_email->bind(&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL);
1061 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1062 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1063 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1064 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1065 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1066 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1068 setup_sensitivity ();
1071 void setup_sensitivity ()
1073 bool const s = _enable_email->GetValue();
1074 _subject->Enable(s);
1080 _reset_email->Enable(s);
1083 void config_changed () override
1085 auto config = Config::instance ();
1087 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1088 checked_set (_enable_email, config->notification(Config::EMAIL));
1089 checked_set (_subject, config->notification_subject ());
1090 checked_set (_from, config->notification_from ());
1091 checked_set (_to, config->notification_to ());
1092 checked_set (_bcc, config->notification_bcc ());
1093 checked_set (_email, Config::instance()->notification_email ());
1095 setup_sensitivity ();
1098 void notification_subject_changed ()
1100 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1103 void notification_from_changed ()
1105 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1108 void notification_to_changed ()
1110 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1113 void notification_bcc_changed ()
1115 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1118 void notification_email_changed ()
1120 if (_email->GetValue().IsEmpty()) {
1121 /* Sometimes we get sent an erroneous notification that the email
1122 is empty; I don't know why.
1126 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1131 Config::instance()->reset_notification_email();
1132 checked_set (_email, Config::instance()->notification_email());
1135 void type_changed (CheckBox* b, Config::Notification n)
1137 Config::instance()->set_notification(n, b->GetValue());
1138 setup_sensitivity ();
1141 CheckBox* _enable_message_box;
1142 CheckBox* _enable_email;
1144 wxTextCtrl* _subject;
1147 EditableList<string, EmailDialog>* _cc;
1150 wxButton* _reset_email;
1154 class CoverSheetPage : public Page
1158 CoverSheetPage (wxSize panel_size, int border)
1159 #ifdef DCPOMATIC_OSX
1160 /* We have to force both width and height of this one */
1161 : Page (wxSize (panel_size.GetWidth(), 128), border)
1163 : Page (panel_size, border)
1167 wxString GetName () const override
1169 return _("Cover Sheet");
1172 #ifdef DCPOMATIC_OSX
1173 wxBitmap GetLargeIcon () const override
1175 return wxBitmap(icon_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1180 void setup () override
1182 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1183 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1185 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1186 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1188 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1189 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1192 void config_changed () override
1194 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1197 void cover_sheet_changed ()
1199 if (_cover_sheet->GetValue().IsEmpty ()) {
1200 /* Sometimes we get sent an erroneous notification that the cover sheet
1201 is empty; I don't know why.
1205 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1208 void reset_cover_sheet ()
1210 Config::instance()->reset_cover_sheet();
1211 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1214 wxTextCtrl* _cover_sheet;
1215 wxButton* _reset_cover_sheet;
1219 class IdentifiersPage : public Page
1222 IdentifiersPage (wxSize panel_size, int border)
1223 : Page (panel_size, border)
1226 wxString GetName () const override
1228 return _("Identifiers");
1231 #ifdef DCPOMATIC_OSX
1232 wxBitmap GetLargeIcon () const override
1234 return wxBitmap(icon_path("identifiers"), wxBITMAP_TYPE_PNG);
1239 void setup () override
1241 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1242 table->AddGrowableCol (1, 1);
1244 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1245 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1246 _issuer->SetToolTip(
1247 variant::wx::insert_dcpomatic(
1248 _("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.")
1250 table->Add (_issuer, 1, wxALL | wxEXPAND);
1252 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1253 _creator = new wxTextCtrl (_panel, wxID_ANY);
1254 _creator->SetToolTip(
1255 variant::wx::insert_dcpomatic(
1256 _("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.")
1258 table->Add (_creator, 1, wxALL | wxEXPAND);
1260 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1261 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1262 _company_name->SetToolTip(
1263 variant::wx::insert_dcpomatic(
1264 _("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.")
1266 table->Add (_company_name, 1, wxALL | wxEXPAND);
1268 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1269 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1270 _product_name->SetToolTip(
1271 variant::wx::insert_dcpomatic(
1272 _("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.")
1274 table->Add (_product_name, 1, wxALL | wxEXPAND);
1276 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1277 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1278 _product_version->SetToolTip(
1279 variant::wx::insert_dcpomatic(
1280 _("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.")
1282 table->Add (_product_version, 1, wxALL | wxEXPAND);
1284 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1285 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1286 _j2k_comment->SetToolTip(
1287 variant::wx::insert_dcpomatic(
1288 _("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.")
1290 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1292 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1294 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1295 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1296 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1297 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1298 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1299 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1302 void config_changed () override
1304 auto config = Config::instance ();
1305 checked_set (_issuer, config->dcp_issuer ());
1306 checked_set (_creator, config->dcp_creator ());
1307 checked_set (_company_name, config->dcp_company_name ());
1308 checked_set (_product_name, config->dcp_product_name ());
1309 checked_set (_product_version, config->dcp_product_version ());
1310 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1313 void issuer_changed ()
1315 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1318 void creator_changed ()
1320 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1323 void company_name_changed ()
1325 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1328 void product_name_changed ()
1330 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1333 void product_version_changed ()
1335 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1338 void j2k_comment_changed ()
1340 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1343 wxTextCtrl* _issuer;
1344 wxTextCtrl* _creator;
1345 wxTextCtrl* _company_name;
1346 wxTextCtrl* _product_name;
1347 wxTextCtrl* _product_version;
1348 wxTextCtrl* _j2k_comment;
1352 class NonStandardPage : public Page
1355 NonStandardPage(wxSize panel_size, int border)
1356 : Page(panel_size, border)
1359 wxString GetName() const override
1361 return _("Non-standard");
1364 #ifdef DCPOMATIC_OSX
1365 wxBitmap GetLargeIcon() const override
1367 return wxBitmap(icon_path("non_standard"), wxBITMAP_TYPE_PNG);
1372 void setup() override
1374 auto table = new wxFlexGridSizer(2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1375 table->AddGrowableCol(1, 1);
1376 _panel->GetSizer()->Add(table, 1, wxALL | wxEXPAND, _border);
1379 add_label_to_sizer(table, _panel, _("Maximum JPEG2000 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1380 auto s = new wxBoxSizer(wxHORIZONTAL);
1381 _maximum_j2k_video_bit_rate = new wxSpinCtrl(_panel);
1382 s->Add(_maximum_j2k_video_bit_rate, 1);
1383 add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1388 add_label_to_sizer(table, _panel, _("Maximum MPEG2 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1389 auto s = new wxBoxSizer(wxHORIZONTAL);
1390 _maximum_mpeg2_video_bit_rate = new wxSpinCtrl(_panel);
1391 s->Add(_maximum_mpeg2_video_bit_rate, 1);
1392 add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1396 auto checkbox = [this, table](wxString name, CheckBox*& variable) {
1397 variable = new CheckBox(_panel, name);
1398 table->Add(variable, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1399 table->AddSpacer(0);
1402 checkbox(_("Allow any DCP frame rate"), _allow_any_dcp_frame_rate);
1404 _allow_any_container = new CheckBox(_panel, _("Allow full-frame and non-standard container ratios"));
1405 table->Add(_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1406 table->AddSpacer(0);
1408 checkbox(_("Allow creation of DCPs with 96kHz audio"), _allow_96khz_audio);
1409 checkbox(_("Allow mapping to all audio channels"), _use_all_audio_channels);
1410 checkbox(_("Allow use of SMPTE Bv2.0"), _allow_smpte_bv20);
1413 add_label_to_sizer(table, _panel, _("ISDCF name part length"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1414 auto s = new wxBoxSizer(wxHORIZONTAL);
1415 _isdcf_name_part_length = new wxSpinCtrl(_panel);
1416 s->Add(_isdcf_name_part_length, 1);
1420 _maximum_j2k_video_bit_rate->SetRange(1, 1000);
1421 _maximum_j2k_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_j2k_video_bit_rate_changed, this));
1422 _maximum_mpeg2_video_bit_rate->SetRange(1, 100);
1423 _maximum_mpeg2_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_mpeg2_video_bit_rate_changed, this));
1424 _allow_any_dcp_frame_rate->bind(&NonStandardPage::allow_any_dcp_frame_rate_changed, this);
1425 _allow_any_container->bind(&NonStandardPage::allow_any_container_changed, this);
1426 _allow_96khz_audio->bind(&NonStandardPage::allow_96khz_audio_changed, this);
1427 _use_all_audio_channels->bind(&NonStandardPage::use_all_channels_changed, this);
1428 _allow_smpte_bv20->bind(&NonStandardPage::allow_smpte_bv20_changed, this);
1429 _isdcf_name_part_length->SetRange(1, 256);
1430 _isdcf_name_part_length->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::isdcf_name_part_length_changed, this));
1433 void config_changed() override
1435 auto config = Config::instance();
1437 checked_set(_maximum_j2k_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::JPEG2000) / 1000000);
1438 checked_set(_maximum_mpeg2_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::MPEG2) / 1000000);
1439 checked_set(_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate());
1440 checked_set(_allow_any_container, config->allow_any_container());
1441 checked_set(_allow_96khz_audio, config->allow_96khz_audio());
1442 checked_set(_use_all_audio_channels, config->use_all_audio_channels());
1443 checked_set(_allow_smpte_bv20, config->allow_smpte_bv20());
1444 checked_set(_isdcf_name_part_length, config->isdcf_name_part_length());
1447 void maximum_j2k_video_bit_rate_changed()
1449 Config::instance()->set_maximum_video_bit_rate(VideoEncoding::JPEG2000, _maximum_j2k_video_bit_rate->GetValue() * 1000000);
1452 void maximum_mpeg2_video_bit_rate_changed()
1454 Config::instance()->set_maximum_video_bit_rate(VideoEncoding::MPEG2, _maximum_mpeg2_video_bit_rate->GetValue() * 1000000);
1457 void allow_any_dcp_frame_rate_changed()
1459 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1462 void allow_any_container_changed()
1464 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1467 void allow_96khz_audio_changed()
1469 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1472 void use_all_channels_changed()
1474 Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
1477 void allow_smpte_bv20_changed()
1479 Config::instance()->set_allow_smpte_bv20(_allow_smpte_bv20->GetValue());
1482 void isdcf_name_part_length_changed()
1484 Config::instance()->set_isdcf_name_part_length(_isdcf_name_part_length->GetValue());
1487 wxSpinCtrl* _maximum_j2k_video_bit_rate = nullptr;
1488 wxSpinCtrl* _maximum_mpeg2_video_bit_rate = nullptr;
1489 CheckBox* _allow_any_dcp_frame_rate = nullptr;
1490 CheckBox* _allow_any_container = nullptr;
1491 CheckBox* _allow_96khz_audio = nullptr;
1492 CheckBox* _use_all_audio_channels = nullptr;
1493 CheckBox* _allow_smpte_bv20 = nullptr;
1494 wxSpinCtrl* _isdcf_name_part_length = nullptr;
1499 /** @class AdvancedPage
1500 * @brief Advanced page of the preferences dialog.
1502 class AdvancedPage : public Page
1505 AdvancedPage (wxSize panel_size, int border)
1506 : Page (panel_size, border)
1509 wxString GetName () const override
1511 return _("Advanced");
1514 #ifdef DCPOMATIC_OSX
1515 wxBitmap GetLargeIcon () const override
1517 return wxBitmap(icon_path("advanced"), wxBITMAP_TYPE_PNG);
1522 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1524 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1526 flags |= wxALIGN_RIGHT;
1529 wxStaticText* m = new StaticText (parent, text);
1530 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1533 void setup () override
1535 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1536 table->AddGrowableCol (1, 1);
1537 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1539 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1540 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1541 table->Add (_video_display_mode);
1543 auto restart = add_label_to_sizer(table, _panel, variant::wx::insert_dcpomatic(_("(restart %s to change display mode)")), false);
1544 auto font = restart->GetFont();
1545 font.SetStyle (wxFONTSTYLE_ITALIC);
1546 font.SetPointSize (font.GetPointSize() - 1);
1547 restart->SetFont (font);
1548 table->AddSpacer (0);
1550 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1551 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1552 table->AddSpacer (0);
1554 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1555 table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1556 table->AddSpacer (0);
1559 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1560 auto s = new wxBoxSizer (wxHORIZONTAL);
1561 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1562 s->Add (_frames_in_memory_multiplier, 1);
1567 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1568 #ifdef DCPOMATIC_OSX
1569 auto align = new wxBoxSizer (wxHORIZONTAL);
1570 align->Add (format, 0, wxTOP, 2);
1571 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1573 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1575 dcp::NameFormat::Map titles;
1576 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1577 dcp::NameFormat::Map examples;
1578 examples['t'] = "cpl";
1579 _dcp_metadata_filename_format = new NameFormatEditor (
1580 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1582 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1586 auto format = create_label (_panel, _("DCP asset filename format"), true);
1587 #ifdef DCPOMATIC_OSX
1588 auto align = new wxBoxSizer (wxHORIZONTAL);
1589 align->Add (format, 0, wxTOP, 2);
1590 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1592 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1594 dcp::NameFormat::Map titles;
1595 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1596 titles['r'] = wx_to_std (_("reel number"));
1597 titles['n'] = wx_to_std (_("number of reels"));
1598 titles['c'] = wx_to_std (_("content filename"));
1599 dcp::NameFormat::Map examples;
1600 examples['t'] = "j2c";
1601 examples['r'] = "1";
1602 examples['n'] = "4";
1603 examples['c'] = "myfile.mp4";
1604 _dcp_asset_filename_format = new NameFormatEditor (
1605 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1607 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1611 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1612 auto t = new wxFlexGridSizer (2);
1613 _log_general = new CheckBox (_panel, _("General"));
1614 t->Add (_log_general, 1, wxEXPAND | wxALL);
1615 _log_warning = new CheckBox (_panel, _("Warnings"));
1616 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1617 _log_error = new CheckBox (_panel, _("Errors"));
1618 t->Add (_log_error, 1, wxEXPAND | wxALL);
1619 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1620 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1621 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1622 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1623 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1624 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1625 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1626 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1627 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1628 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1629 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1630 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1631 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1632 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1633 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1634 table->Add (t, 0, wxALL, 6);
1637 #ifdef DCPOMATIC_WINDOWS
1638 _win32_console = new CheckBox (_panel, _("Open console window"));
1639 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1640 table->AddSpacer (0);
1643 _video_display_mode->Append (_("Simple (safer)"));
1644 #if wxCHECK_VERSION(3, 1, 0)
1645 _video_display_mode->Append (_("OpenGL (faster)"));
1647 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1648 _show_experimental_audio_processors->bind(&AdvancedPage::show_experimental_audio_processors_changed, this);
1649 _only_servers_encode->bind(&AdvancedPage::only_servers_encode_changed, this);
1650 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1651 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1652 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1653 _log_general->bind(&AdvancedPage::log_changed, this);
1654 _log_warning->bind(&AdvancedPage::log_changed, this);
1655 _log_error->bind(&AdvancedPage::log_changed, this);
1656 _log_timing->bind(&AdvancedPage::log_changed, this);
1657 _log_debug_threed->bind(&AdvancedPage::log_changed, this);
1658 _log_debug_encode->bind(&AdvancedPage::log_changed, this);
1659 _log_debug_email->bind(&AdvancedPage::log_changed, this);
1660 _log_debug_video_view->bind(&AdvancedPage::log_changed, this);
1661 _log_debug_player->bind(&AdvancedPage::log_changed, this);
1662 _log_debug_audio_analysis->bind(&AdvancedPage::log_changed, this);
1663 #ifdef DCPOMATIC_WINDOWS
1664 _win32_console->bind(&AdvancedPage::win32_console_changed, this);
1668 void config_changed () override
1670 auto config = Config::instance ();
1672 switch (config->video_view_type()) {
1673 case Config::VIDEO_VIEW_SIMPLE:
1674 checked_set (_video_display_mode, 0);
1676 case Config::VIDEO_VIEW_OPENGL:
1677 checked_set (_video_display_mode, 1);
1680 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1681 checked_set (_only_servers_encode, config->only_servers_encode ());
1682 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1683 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1684 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1685 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1686 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1687 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1688 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1689 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1690 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1691 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1692 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1693 #ifdef DCPOMATIC_WINDOWS
1694 checked_set (_win32_console, config->win32_console());
1698 void video_display_mode_changed ()
1700 if (_video_display_mode->GetSelection() == 0) {
1701 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1703 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1707 void frames_in_memory_multiplier_changed ()
1709 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1712 void show_experimental_audio_processors_changed ()
1714 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1717 void only_servers_encode_changed ()
1719 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1722 void dcp_metadata_filename_format_changed ()
1724 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1727 void dcp_asset_filename_format_changed ()
1729 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1735 if (_log_general->GetValue ()) {
1736 types |= LogEntry::TYPE_GENERAL;
1738 if (_log_warning->GetValue ()) {
1739 types |= LogEntry::TYPE_WARNING;
1741 if (_log_error->GetValue ()) {
1742 types |= LogEntry::TYPE_ERROR;
1744 if (_log_timing->GetValue ()) {
1745 types |= LogEntry::TYPE_TIMING;
1747 if (_log_debug_threed->GetValue ()) {
1748 types |= LogEntry::TYPE_DEBUG_THREE_D;
1750 if (_log_debug_encode->GetValue ()) {
1751 types |= LogEntry::TYPE_DEBUG_ENCODE;
1753 if (_log_debug_email->GetValue ()) {
1754 types |= LogEntry::TYPE_DEBUG_EMAIL;
1756 if (_log_debug_video_view->GetValue()) {
1757 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1759 if (_log_debug_player->GetValue()) {
1760 types |= LogEntry::TYPE_DEBUG_PLAYER;
1762 if (_log_debug_audio_analysis->GetValue()) {
1763 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1765 Config::instance()->set_log_types (types);
1768 #ifdef DCPOMATIC_WINDOWS
1769 void win32_console_changed ()
1771 Config::instance()->set_win32_console(_win32_console->GetValue());
1775 wxChoice* _video_display_mode = nullptr;
1776 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1777 CheckBox* _show_experimental_audio_processors = nullptr;
1778 CheckBox* _only_servers_encode = nullptr;
1779 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1780 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1781 CheckBox* _log_general = nullptr;
1782 CheckBox* _log_warning = nullptr;
1783 CheckBox* _log_error = nullptr;
1784 CheckBox* _log_timing = nullptr;
1785 CheckBox* _log_debug_threed = nullptr;
1786 CheckBox* _log_debug_encode = nullptr;
1787 CheckBox* _log_debug_email = nullptr;
1788 CheckBox* _log_debug_video_view = nullptr;
1789 CheckBox* _log_debug_player = nullptr;
1790 CheckBox* _log_debug_audio_analysis = nullptr;
1791 #ifdef DCPOMATIC_WINDOWS
1792 CheckBox* _win32_console = nullptr;
1797 wxPreferencesEditor*
1798 create_full_config_dialog ()
1800 auto e = new wxPreferencesEditor ();
1802 #ifdef DCPOMATIC_OSX
1803 /* Width that we force some of the config panels to be on OSX so that
1804 the containing window doesn't shrink too much when we select those panels.
1805 This is obviously an unpleasant hack.
1807 wxSize ps = wxSize(900, -1);
1808 int const border = 16;
1810 wxSize ps = wxSize (-1, -1);
1811 int const border = 8;
1814 e->AddPage (new FullGeneralPage (ps, border));
1815 e->AddPage (new SoundPage (ps, border));
1816 e->AddPage (new DefaultsPage (ps, border));
1817 e->AddPage (new EncodingServersPage(ps, border));
1818 #ifdef DCPOMATIC_GROK
1819 e->AddPage (new GPUPage (ps, border));
1821 e->AddPage (new KeysPage (ps, border));
1822 e->AddPage (new TMSPage (ps, border));
1823 e->AddPage (new EmailPage (ps, border));
1824 e->AddPage (new KDMEmailPage (ps, border));
1825 e->AddPage (new NotificationsPage (ps, border));
1826 e->AddPage (new CoverSheetPage (ps, border));
1827 e->AddPage (new IdentifiersPage (ps, border));
1828 e->AddPage (new NonStandardPage (ps, border));
1829 e->AddPage (new AdvancedPage (ps, border));