2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/full_config_dialog.cc
23 * @brief A dialogue to edit all DCP-o-matic configuration.
27 #include "check_box.h"
28 #include "config_dialog.h"
29 #include "config_move_dialog.h"
30 #include "dcpomatic_button.h"
31 #include "dir_picker_ctrl.h"
32 #include "editable_list.h"
33 #include "email_dialog.h"
34 #include "file_picker_ctrl.h"
35 #include "filter_dialog.h"
36 #include "full_config_dialog.h"
37 #include "kdm_choice.h"
38 #include "make_chain_dialog.h"
39 #include "nag_dialog.h"
40 #include "name_format_editor.h"
41 #include "password_entry.h"
42 #include "send_test_email_dialog.h"
43 #include "server_dialog.h"
44 #include "static_text.h"
46 #include "lib/config.h"
47 #include "lib/cross.h"
48 #include "lib/dcp_content_type.h"
49 #include "lib/emailer.h"
50 #include "lib/exceptions.h"
51 #include "lib/filter.h"
53 #include "lib/ratio.h"
55 #include <dcp/certificate_chain.h>
56 #include <dcp/exceptions.h>
57 #include <dcp/locale_convert.h>
58 #include <dcp/warnings.h>
59 LIBDCP_DISABLE_WARNINGS
60 #include <wx/filepicker.h>
61 #include <wx/preferences.h>
62 #include <wx/spinctrl.h>
63 #include <wx/stdpaths.h>
64 LIBDCP_ENABLE_WARNINGS
66 #include <boost/filesystem.hpp>
76 using std::shared_ptr;
80 using boost::optional;
81 #if BOOST_VERSION >= 106100
82 using namespace boost::placeholders;
84 using dcp::locale_convert;
87 class FullGeneralPage : public GeneralPage
90 FullGeneralPage (wxSize panel_size, int border)
91 : GeneralPage (panel_size, border)
95 void setup () override
97 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
98 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
101 add_language_controls (table, r);
103 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
104 _master_encoding_threads = new wxSpinCtrl (_panel);
105 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
108 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
109 _server_encoding_threads = new wxSpinCtrl (_panel);
110 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
113 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
114 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
115 table->Add (_config_file, wxGBPosition (r, 1));
118 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
119 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
120 table->Add (_cinemas_file, wxGBPosition (r, 1));
121 Button* export_cinemas = new Button (_panel, _("Export..."));
122 table->Add (export_cinemas, wxGBPosition (r, 2));
125 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
126 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
127 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
131 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
132 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
135 add_update_controls (table, r);
137 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
138 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
140 _master_encoding_threads->SetRange (1, 128);
141 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
142 _server_encoding_threads->SetRange (1, 128);
143 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
144 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
146 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
147 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
149 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
152 void config_changed () override
154 auto config = Config::instance ();
156 checked_set (_master_encoding_threads, config->master_encoding_threads ());
157 checked_set (_server_encoding_threads, config->server_encoding_threads ());
158 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
159 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
161 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
162 checked_set (_config_file, config->config_read_file());
163 checked_set (_cinemas_file, config->cinemas_file());
165 GeneralPage::config_changed ();
168 void export_cinemas_file ()
170 auto d = new wxFileDialog (
171 _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
172 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
175 if (d->ShowModal () == wxID_OK) {
176 boost::filesystem::copy_file (Config::instance()->cinemas_file(), wx_to_std(d->GetPath()));
181 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
182 void analyse_ebur128_changed ()
184 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
188 void automatic_audio_analysis_changed ()
190 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
193 void master_encoding_threads_changed ()
195 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
198 void server_encoding_threads_changed ()
200 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
203 void config_file_changed ()
205 auto config = Config::instance();
206 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
207 if (new_file == config->config_read_file()) {
210 bool copy_and_link = true;
211 if (boost::filesystem::exists(new_file)) {
212 auto d = new ConfigMoveDialog (_panel, new_file);
213 if (d->ShowModal() == wxID_OK) {
214 copy_and_link = false;
221 if (new_file != config->config_read_file()) {
222 config->copy_and_link (new_file);
225 config->link (new_file);
229 void cinemas_file_changed ()
231 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
234 wxSpinCtrl* _master_encoding_threads;
235 wxSpinCtrl* _server_encoding_threads;
236 FilePickerCtrl* _config_file;
237 FilePickerCtrl* _cinemas_file;
238 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
239 wxCheckBox* _analyse_ebur128;
241 wxCheckBox* _automatic_audio_analysis;
245 class DefaultsPage : public Page
248 DefaultsPage (wxSize panel_size, int border)
249 : Page (panel_size, border)
252 wxString GetName () const override
254 return _("Defaults");
258 wxBitmap GetLargeIcon () const override
260 return wxBitmap(bitmap_path("defaults"), wxBITMAP_TYPE_PNG);
265 void setup () override
267 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
268 table->AddGrowableCol (1, 1);
269 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
272 add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
273 auto s = new wxBoxSizer (wxHORIZONTAL);
274 _still_length = new wxSpinCtrl (_panel);
275 s->Add (_still_length);
276 add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
280 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
281 #ifdef DCPOMATIC_USE_OWN_PICKER
282 _directory = new DirPickerCtrl (_panel);
284 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
286 table->Add (_directory, 1, wxEXPAND);
288 add_label_to_sizer (table, _panel, _("Default container"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
289 _container = new wxChoice (_panel, wxID_ANY);
290 table->Add (_container);
292 add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
293 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
294 table->Add (_dcp_content_type);
296 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
297 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
298 table->Add (_dcp_audio_channels);
301 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
302 auto s = new wxBoxSizer (wxHORIZONTAL);
303 _j2k_bandwidth = new wxSpinCtrl (_panel);
304 s->Add (_j2k_bandwidth);
305 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
310 add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
311 auto s = new wxBoxSizer (wxHORIZONTAL);
312 _audio_delay = new wxSpinCtrl (_panel);
313 s->Add (_audio_delay);
314 add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
318 add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
319 _standard = new wxChoice (_panel, wxID_ANY);
320 table->Add (_standard);
322 table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
323 table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
325 table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
326 table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
328 table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
329 table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
331 table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
332 table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
334 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
335 #ifdef DCPOMATIC_USE_OWN_PICKER
336 _kdm_directory = new DirPickerCtrl (_panel);
338 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
340 table->Add (_kdm_directory, 1, wxEXPAND);
342 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
343 _kdm_type = new KDMChoice (_panel);
344 table->Add (_kdm_type, 1, wxEXPAND);
346 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
348 _still_length->SetRange (1, 3600);
349 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
351 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
352 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
353 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
355 _use_isdcf_name_by_default->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::use_isdcf_name_by_default_changed, this));
357 for (auto i: Ratio::containers()) {
358 _container->Append (std_to_wx(i->container_nickname()));
361 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
363 for (auto i: DCPContentType::all()) {
364 _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
367 setup_audio_channels_choice (_dcp_audio_channels, 2);
369 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
370 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
372 _j2k_bandwidth->SetRange (50, 250);
373 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
375 _audio_delay->SetRange (-1000, 1000);
376 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
378 _standard->Append (_("SMPTE"));
379 _standard->Append (_("Interop"));
380 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
382 for (auto const& i: _enable_metadata) {
383 i.second->Bind (wxEVT_CHECKBOX, boost::bind(&DefaultsPage::metadata_changed, this));
386 for (auto const& i: _metadata) {
387 i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
391 void config_changed () override
393 auto config = Config::instance ();
395 auto containers = Ratio::containers ();
396 for (size_t i = 0; i < containers.size(); ++i) {
397 if (containers[i] == config->default_container()) {
398 _container->SetSelection (i);
402 auto const ct = DCPContentType::all ();
403 for (size_t i = 0; i < ct.size(); ++i) {
404 if (ct[i] == config->default_dcp_content_type()) {
405 _dcp_content_type->SetSelection (i);
409 checked_set (_still_length, config->default_still_length ());
410 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
411 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
412 _kdm_type->set (config->default_kdm_type());
413 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
414 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
415 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
416 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
417 checked_set (_audio_delay, config->default_audio_delay ());
418 checked_set (_standard, config->default_interop() ? 1 : 0);
420 auto metadata = config->default_metadata();
422 for (auto const& i: metadata) {
423 _enable_metadata[i.first]->SetValue(true);
424 checked_set (_metadata[i.first], i.second);
427 for (auto const& i: _enable_metadata) {
428 if (metadata.find(i.first) == metadata.end()) {
429 checked_set (i.second, false);
433 for (auto const& i: _metadata) {
434 if (metadata.find(i.first) == metadata.end()) {
435 checked_set (i.second, wxT(""));
439 setup_sensitivity ();
442 void j2k_bandwidth_changed ()
444 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
447 void audio_delay_changed ()
449 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
452 void dcp_audio_channels_changed ()
454 int const s = _dcp_audio_channels->GetSelection ();
455 if (s != wxNOT_FOUND) {
456 Config::instance()->set_default_dcp_audio_channels (
457 locale_convert<int>(string_client_data(_dcp_audio_channels->GetClientObject(s)))
462 void directory_changed ()
464 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
467 void kdm_directory_changed ()
469 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
472 void kdm_type_changed ()
474 Config::instance()->set_default_kdm_type(_kdm_type->get());
477 void use_isdcf_name_by_default_changed ()
479 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
482 void still_length_changed ()
484 Config::instance()->set_default_still_length (_still_length->GetValue ());
487 void container_changed ()
489 auto ratio = Ratio::containers ();
490 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
493 void dcp_content_type_changed ()
495 auto ct = DCPContentType::all ();
496 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
499 void standard_changed ()
501 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
504 void metadata_changed ()
506 map<string, string> metadata;
507 for (auto const& i: _enable_metadata) {
508 if (i.second->GetValue()) {
509 metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
512 Config::instance()->set_default_metadata (metadata);
513 setup_sensitivity ();
516 void setup_sensitivity ()
518 for (auto const& i: _enable_metadata) {
519 _metadata[i.first]->Enable(i.second->GetValue());
523 wxSpinCtrl* _j2k_bandwidth;
524 wxSpinCtrl* _audio_delay;
525 wxSpinCtrl* _still_length;
526 #ifdef DCPOMATIC_USE_OWN_PICKER
527 DirPickerCtrl* _directory;
528 DirPickerCtrl* _kdm_directory;
530 wxDirPickerCtrl* _directory;
531 wxDirPickerCtrl* _kdm_directory;
533 KDMChoice* _kdm_type;
534 wxCheckBox* _use_isdcf_name_by_default;
535 wxChoice* _container;
536 wxChoice* _dcp_content_type;
537 wxChoice* _dcp_audio_channels;
539 map<string, CheckBox*> _enable_metadata;
540 map<string, wxTextCtrl*> _metadata;
544 class EncodingServersPage : public Page
547 EncodingServersPage (wxSize panel_size, int border)
548 : Page (panel_size, border)
551 wxString GetName () const override
557 wxBitmap GetLargeIcon () const override
559 return wxBitmap(bitmap_path("servers"), wxBITMAP_TYPE_PNG);
564 void setup () override
566 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
567 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
569 vector<EditableListColumn> columns;
570 columns.push_back (EditableListColumn(_("IP address / host name")));
571 _servers_list = new EditableList<string, ServerDialog> (
574 boost::bind (&Config::servers, Config::instance()),
575 boost::bind (&Config::set_servers, Config::instance(), _1),
576 boost::bind (&EncodingServersPage::server_column, this, _1),
578 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
581 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
583 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind(&EncodingServersPage::use_any_servers_changed, this));
586 void config_changed () override
588 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
589 _servers_list->refresh ();
592 void use_any_servers_changed ()
594 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
597 string server_column (string s)
602 wxCheckBox* _use_any_servers;
603 EditableList<string, ServerDialog>* _servers_list;
607 class TMSPage : public Page
610 TMSPage (wxSize panel_size, int border)
611 : Page (panel_size, border)
614 wxString GetName () const override
620 wxBitmap GetLargeIcon () const override
622 return wxBitmap(bitmap_path("tms"), wxBITMAP_TYPE_PNG);
627 void setup () override
629 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
630 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
632 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
633 table->AddGrowableCol (1, 1);
634 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
636 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
637 _tms_protocol = new wxChoice (_panel, wxID_ANY);
638 table->Add (_tms_protocol, 1, wxEXPAND);
640 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
641 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
642 table->Add (_tms_ip, 1, wxEXPAND);
644 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
645 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
646 table->Add (_tms_path, 1, wxEXPAND);
648 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
649 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
650 table->Add (_tms_user, 1, wxEXPAND);
652 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
653 _tms_password = new PasswordEntry (_panel);
654 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
656 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
657 _tms_protocol->Append (_("FTP (for Dolby)"));
659 _upload->Bind (wxEVT_CHECKBOX, boost::bind(&TMSPage::upload_changed, this));
660 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
661 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
662 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
663 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
664 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
667 void config_changed () override
669 auto config = Config::instance ();
671 checked_set (_upload, config->upload_after_make_dcp());
672 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
673 checked_set (_tms_ip, config->tms_ip ());
674 checked_set (_tms_path, config->tms_path ());
675 checked_set (_tms_user, config->tms_user ());
676 checked_set (_tms_password, config->tms_password ());
679 void upload_changed ()
681 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
684 void tms_protocol_changed ()
686 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
689 void tms_ip_changed ()
691 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
694 void tms_path_changed ()
696 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
699 void tms_user_changed ()
701 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
704 void tms_password_changed ()
706 Config::instance()->set_tms_password (_tms_password->get());
710 wxChoice* _tms_protocol;
712 wxTextCtrl* _tms_path;
713 wxTextCtrl* _tms_user;
714 PasswordEntry* _tms_password;
718 class EmailPage : public Page
721 EmailPage (wxSize panel_size, int border)
722 : Page (panel_size, border)
725 wxString GetName () const override
731 wxBitmap GetLargeIcon () const override
733 return wxBitmap(bitmap_path("email"), wxBITMAP_TYPE_PNG);
738 void setup () override
740 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
741 table->AddGrowableCol (1, 1);
742 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
744 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
746 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
747 _server = new wxTextCtrl (_panel, wxID_ANY);
748 s->Add (_server, 1, wxEXPAND | wxALL);
749 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
750 _port = new wxSpinCtrl (_panel, wxID_ANY);
751 _port->SetRange (0, 65535);
753 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
754 _protocol = new wxChoice (_panel, wxID_ANY);
755 /* Make sure this matches the switches in config_changed and port_changed below */
756 _protocol->Append (_("Auto"));
757 _protocol->Append (_("Plain"));
758 _protocol->Append (_("STARTTLS"));
759 _protocol->Append (_("SSL"));
760 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
761 table->Add (s, 1, wxEXPAND | wxALL);
764 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
765 _user = new wxTextCtrl (_panel, wxID_ANY);
766 table->Add (_user, 1, wxEXPAND | wxALL);
768 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
769 _password = new PasswordEntry (_panel);
770 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
772 table->AddSpacer (0);
773 _send_test_email = new Button (_panel, _("Send test email..."));
774 table->Add (_send_test_email);
776 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
777 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
778 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
779 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
780 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
781 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
784 void config_changed () override
786 auto config = Config::instance ();
788 checked_set (_server, config->mail_server());
789 checked_set (_port, config->mail_port());
790 switch (config->mail_protocol()) {
791 case EmailProtocol::AUTO:
792 checked_set (_protocol, 0);
794 case EmailProtocol::PLAIN:
795 checked_set (_protocol, 1);
797 case EmailProtocol::STARTTLS:
798 checked_set (_protocol, 2);
800 case EmailProtocol::SSL:
801 checked_set (_protocol, 3);
804 checked_set (_user, config->mail_user());
805 checked_set (_password, config->mail_password());
808 void server_changed ()
810 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
815 Config::instance()->set_mail_port(_port->GetValue());
818 void protocol_changed ()
820 switch (_protocol->GetSelection()) {
822 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
825 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
828 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
831 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
838 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
841 void password_changed ()
843 Config::instance()->set_mail_password(_password->get());
846 void send_test_email_clicked ()
848 auto dialog = new SendTestEmailDialog(_panel);
849 auto result = dialog->ShowModal();
851 if (result == wxID_OK) {
853 wx_to_std(dialog->from()),
854 { wx_to_std(dialog->to()) },
855 wx_to_std(_("DCP-o-matic test email")),
856 wx_to_std(_("This is a test email from DCP-o-matic."))
858 auto config = Config::instance();
860 emailer.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
861 } catch (NetworkError& e) {
862 error_dialog (_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
864 } catch (std::exception& e) {
865 error_dialog (_panel, _("Test email sending failed."), std_to_wx(e.what()));
868 error_dialog (_panel, _("Test email sending failed."));
871 message_dialog (_panel, _("Test email sent."));
879 PasswordEntry* _password;
880 Button* _send_test_email;
884 class KDMEmailPage : public Page
888 KDMEmailPage (wxSize panel_size, int border)
890 /* We have to force both width and height of this one */
891 : Page (wxSize (panel_size.GetWidth(), 128), border)
893 : Page (panel_size, border)
897 wxString GetName () const override
899 return _("KDM Email");
903 wxBitmap GetLargeIcon () const override
905 return wxBitmap(bitmap_path("kdm_email"), wxBITMAP_TYPE_PNG);
910 void setup () override
912 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
913 table->AddGrowableCol (1, 1);
914 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
916 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
917 _subject = new wxTextCtrl (_panel, wxID_ANY);
918 table->Add (_subject, 1, wxEXPAND | wxALL);
920 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
921 _from = new wxTextCtrl (_panel, wxID_ANY);
922 table->Add (_from, 1, wxEXPAND | wxALL);
924 vector<EditableListColumn> columns;
925 columns.push_back (EditableListColumn(_("Address")));
926 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
927 _cc = new EditableList<string, EmailDialog> (
930 bind (&Config::kdm_cc, Config::instance()),
931 bind (&Config::set_kdm_cc, Config::instance(), _1),
936 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
938 table->Add (_cc, 1, wxEXPAND | wxALL);
940 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
941 _bcc = new wxTextCtrl (_panel, wxID_ANY);
942 table->Add (_bcc, 1, wxEXPAND | wxALL);
944 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
945 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
947 _reset_email = new Button (_panel, _("Reset to default subject and text"));
948 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
952 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
953 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
954 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
955 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
956 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
959 void config_changed () override
961 auto config = Config::instance ();
963 checked_set (_subject, config->kdm_subject ());
964 checked_set (_from, config->kdm_from ());
965 checked_set (_bcc, config->kdm_bcc ());
966 checked_set (_email, Config::instance()->kdm_email ());
969 void kdm_subject_changed ()
971 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
974 void kdm_from_changed ()
976 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
979 void kdm_bcc_changed ()
981 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
984 void kdm_email_changed ()
986 if (_email->GetValue().IsEmpty ()) {
987 /* Sometimes we get sent an erroneous notification that the email
988 is empty; I don't know why.
992 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
997 Config::instance()->reset_kdm_email ();
998 checked_set (_email, Config::instance()->kdm_email ());
1001 wxTextCtrl* _subject;
1003 EditableList<string, EmailDialog>* _cc;
1006 wxButton* _reset_email;
1010 class NotificationsPage : public Page
1013 NotificationsPage (wxSize panel_size, int border)
1014 #ifdef DCPOMATIC_OSX
1015 /* We have to force both width and height of this one */
1016 : Page (wxSize (panel_size.GetWidth(), 128), border)
1018 : Page (panel_size, border)
1022 wxString GetName () const override
1024 return _("Notifications");
1027 #ifdef DCPOMATIC_OSX
1028 wxBitmap GetLargeIcon () const override
1030 return wxBitmap(bitmap_path("notifications"), wxBITMAP_TYPE_PNG);
1035 void setup () override
1037 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1038 table->AddGrowableCol (1, 1);
1039 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1041 _enable_message_box = new CheckBox (_panel, _("Message box"));
1042 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1043 table->AddSpacer (0);
1045 _enable_email = new CheckBox (_panel, _("Email"));
1046 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1047 table->AddSpacer (0);
1049 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1050 _subject = new wxTextCtrl (_panel, wxID_ANY);
1051 table->Add (_subject, 1, wxEXPAND | wxALL);
1053 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1054 _from = new wxTextCtrl (_panel, wxID_ANY);
1055 table->Add (_from, 1, wxEXPAND | wxALL);
1057 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1058 _to = new wxTextCtrl (_panel, wxID_ANY);
1059 table->Add (_to, 1, wxEXPAND | wxALL);
1061 vector<EditableListColumn> columns;
1062 columns.push_back (EditableListColumn(_("Address")));
1063 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1064 _cc = new EditableList<string, EmailDialog> (
1067 bind (&Config::notification_cc, Config::instance()),
1068 bind (&Config::set_notification_cc, Config::instance(), _1),
1069 [] (string s, int) {
1073 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1075 table->Add (_cc, 1, wxEXPAND | wxALL);
1077 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1078 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1079 table->Add (_bcc, 1, wxEXPAND | wxALL);
1081 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1082 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1084 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1085 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1089 _enable_message_box->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX));
1090 _enable_email->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL));
1092 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1093 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1094 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1095 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1096 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1097 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1099 setup_sensitivity ();
1102 void setup_sensitivity ()
1104 bool const s = _enable_email->GetValue();
1105 _subject->Enable(s);
1111 _reset_email->Enable(s);
1114 void config_changed () override
1116 auto config = Config::instance ();
1118 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1119 checked_set (_enable_email, config->notification(Config::EMAIL));
1120 checked_set (_subject, config->notification_subject ());
1121 checked_set (_from, config->notification_from ());
1122 checked_set (_to, config->notification_to ());
1123 checked_set (_bcc, config->notification_bcc ());
1124 checked_set (_email, Config::instance()->notification_email ());
1126 setup_sensitivity ();
1129 void notification_subject_changed ()
1131 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1134 void notification_from_changed ()
1136 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1139 void notification_to_changed ()
1141 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1144 void notification_bcc_changed ()
1146 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1149 void notification_email_changed ()
1151 if (_email->GetValue().IsEmpty()) {
1152 /* Sometimes we get sent an erroneous notification that the email
1153 is empty; I don't know why.
1157 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1162 Config::instance()->reset_notification_email();
1163 checked_set (_email, Config::instance()->notification_email());
1166 void type_changed (wxCheckBox* b, Config::Notification n)
1168 Config::instance()->set_notification(n, b->GetValue());
1169 setup_sensitivity ();
1172 wxCheckBox* _enable_message_box;
1173 wxCheckBox* _enable_email;
1175 wxTextCtrl* _subject;
1178 EditableList<string, EmailDialog>* _cc;
1181 wxButton* _reset_email;
1185 class CoverSheetPage : public Page
1189 CoverSheetPage (wxSize panel_size, int border)
1190 #ifdef DCPOMATIC_OSX
1191 /* We have to force both width and height of this one */
1192 : Page (wxSize (panel_size.GetWidth(), 128), border)
1194 : Page (panel_size, border)
1198 wxString GetName () const override
1200 return _("Cover Sheet");
1203 #ifdef DCPOMATIC_OSX
1204 wxBitmap GetLargeIcon () const override
1206 return wxBitmap(bitmap_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1211 void setup () override
1213 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1214 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1216 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1217 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1219 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1220 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1223 void config_changed () override
1225 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1228 void cover_sheet_changed ()
1230 if (_cover_sheet->GetValue().IsEmpty ()) {
1231 /* Sometimes we get sent an erroneous notification that the cover sheet
1232 is empty; I don't know why.
1236 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1239 void reset_cover_sheet ()
1241 Config::instance()->reset_cover_sheet();
1242 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1245 wxTextCtrl* _cover_sheet;
1246 wxButton* _reset_cover_sheet;
1250 class IdentifiersPage : public Page
1253 IdentifiersPage (wxSize panel_size, int border)
1254 : Page (panel_size, border)
1257 wxString GetName () const override
1259 return _("Identifiers");
1262 #ifdef DCPOMATIC_OSX
1263 wxBitmap GetLargeIcon () const override
1265 return wxBitmap(bitmap_path("identifiers"), wxBITMAP_TYPE_PNG);
1270 void setup () override
1272 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1273 table->AddGrowableCol (1, 1);
1275 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1276 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1277 _issuer->SetToolTip (_("This will be written to the DCP's XML files as the <Issuer>. If it is blank, a default value mentioning DCP-o-matic will be used."));
1278 table->Add (_issuer, 1, wxALL | wxEXPAND);
1280 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1281 _creator = new wxTextCtrl (_panel, wxID_ANY);
1282 _creator->SetToolTip (_("This will be written to the DCP's XML files as the <Creator>. If it is blank, a default value mentioning DCP-o-matic will be used."));
1283 table->Add (_creator, 1, wxALL | wxEXPAND);
1285 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1286 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1287 _company_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'company name'. If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1288 table->Add (_company_name, 1, wxALL | wxEXPAND);
1290 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1291 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1292 _product_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'product name'. If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1293 table->Add (_product_name, 1, wxALL | wxEXPAND);
1295 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1296 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1297 _product_version->SetToolTip (_("This will be written to the DCP's MXF files as the 'product version'. If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1298 table->Add (_product_version, 1, wxALL | wxEXPAND);
1300 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1301 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1302 _j2k_comment->SetToolTip (_("This will be written to the DCP's JPEG2000 data as a comment. If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
1303 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1305 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1307 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1308 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1309 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1310 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1311 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1312 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1315 void config_changed () override
1317 auto config = Config::instance ();
1318 checked_set (_issuer, config->dcp_issuer ());
1319 checked_set (_creator, config->dcp_creator ());
1320 checked_set (_company_name, config->dcp_company_name ());
1321 checked_set (_product_name, config->dcp_product_name ());
1322 checked_set (_product_version, config->dcp_product_version ());
1323 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1326 void issuer_changed ()
1328 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1331 void creator_changed ()
1333 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1336 void company_name_changed ()
1338 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1341 void product_name_changed ()
1343 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1346 void product_version_changed ()
1348 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1351 void j2k_comment_changed ()
1353 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1356 wxTextCtrl* _issuer;
1357 wxTextCtrl* _creator;
1358 wxTextCtrl* _company_name;
1359 wxTextCtrl* _product_name;
1360 wxTextCtrl* _product_version;
1361 wxTextCtrl* _j2k_comment;
1365 /** @class AdvancedPage
1366 * @brief Advanced page of the preferences dialog.
1368 class AdvancedPage : public Page
1371 AdvancedPage (wxSize panel_size, int border)
1372 : Page (panel_size, border)
1375 wxString GetName () const override
1377 return _("Advanced");
1380 #ifdef DCPOMATIC_OSX
1381 wxBitmap GetLargeIcon () const override
1383 return wxBitmap(bitmap_path("advanced"), wxBITMAP_TYPE_PNG);
1388 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1390 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1392 flags |= wxALIGN_RIGHT;
1395 wxStaticText* m = new StaticText (parent, text);
1396 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1399 void setup () override
1401 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1402 table->AddGrowableCol (1, 1);
1403 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1406 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1407 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1408 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1409 s->Add (_maximum_j2k_bandwidth, 1);
1410 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1414 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1415 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1416 table->Add (_video_display_mode);
1418 auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1419 auto font = restart->GetFont();
1420 font.SetStyle (wxFONTSTYLE_ITALIC);
1421 font.SetPointSize (font.GetPointSize() - 1);
1422 restart->SetFont (font);
1423 table->AddSpacer (0);
1425 _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
1426 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1427 table->AddSpacer (0);
1429 _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
1430 table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
1431 restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
1432 restart->SetFont (font);
1434 _allow_96khz_audio = new CheckBox (_panel, _("Allow creation of DCPs with 96kHz audio"));
1435 table->Add (_allow_96khz_audio, 1, wxEXPAND | wxALL);
1436 table->AddSpacer (0);
1438 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1439 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
1440 table->AddSpacer (0);
1442 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1443 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1444 table->AddSpacer (0);
1447 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1448 auto s = new wxBoxSizer (wxHORIZONTAL);
1449 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1450 s->Add (_frames_in_memory_multiplier, 1);
1455 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1456 #ifdef DCPOMATIC_OSX
1457 auto align = new wxBoxSizer (wxHORIZONTAL);
1458 align->Add (format, 0, wxTOP, 2);
1459 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1461 table->Add (format, 0, wxTOP | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1463 dcp::NameFormat::Map titles;
1464 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1465 dcp::NameFormat::Map examples;
1466 examples['t'] = "cpl";
1467 _dcp_metadata_filename_format = new NameFormatEditor (
1468 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1470 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1474 auto format = create_label (_panel, _("DCP asset filename format"), true);
1475 #ifdef DCPOMATIC_OSX
1476 auto align = new wxBoxSizer (wxHORIZONTAL);
1477 align->Add (format, 0, wxTOP, 2);
1478 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1480 table->Add (format, 0, wxTOP | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1482 dcp::NameFormat::Map titles;
1483 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1484 titles['r'] = wx_to_std (_("reel number"));
1485 titles['n'] = wx_to_std (_("number of reels"));
1486 titles['c'] = wx_to_std (_("content filename"));
1487 dcp::NameFormat::Map examples;
1488 examples['t'] = "j2c";
1489 examples['r'] = "1";
1490 examples['n'] = "4";
1491 examples['c'] = "myfile.mp4";
1492 _dcp_asset_filename_format = new NameFormatEditor (
1493 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1495 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1499 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1500 auto t = new wxFlexGridSizer (2);
1501 _log_general = new CheckBox (_panel, _("General"));
1502 t->Add (_log_general, 1, wxEXPAND | wxALL);
1503 _log_warning = new CheckBox (_panel, _("Warnings"));
1504 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1505 _log_error = new CheckBox (_panel, _("Errors"));
1506 t->Add (_log_error, 1, wxEXPAND | wxALL);
1507 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1508 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1509 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1510 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1511 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1512 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1513 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1514 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1515 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1516 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1517 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1518 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1519 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1520 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1521 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1522 table->Add (t, 0, wxALL, 6);
1525 #ifdef DCPOMATIC_WINDOWS
1526 _win32_console = new CheckBox (_panel, _("Open console window"));
1527 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1528 table->AddSpacer (0);
1531 _maximum_j2k_bandwidth->SetRange (1, 1000);
1532 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1533 _video_display_mode->Append (_("Simple (safer)"));
1534 #if wxCHECK_VERSION(3, 1, 0)
1535 _video_display_mode->Append (_("OpenGL (faster)"));
1537 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1538 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1539 _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
1540 _allow_96khz_audio->Bind (wxEVT_CHECKBOX, boost::bind(&AdvancedPage::allow_96khz_audio_changed, this));
1541 _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
1542 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1543 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1544 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1545 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1546 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1547 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1548 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1549 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1550 _log_debug_threed->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1551 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1552 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1553 _log_debug_video_view->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1554 _log_debug_player->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1555 _log_debug_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1556 #ifdef DCPOMATIC_WINDOWS
1557 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1561 void config_changed () override
1563 auto config = Config::instance ();
1565 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1566 switch (config->video_view_type()) {
1567 case Config::VIDEO_VIEW_SIMPLE:
1568 checked_set (_video_display_mode, 0);
1570 case Config::VIDEO_VIEW_OPENGL:
1571 checked_set (_video_display_mode, 1);
1574 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1575 checked_set (_allow_any_container, config->allow_any_container ());
1576 checked_set (_allow_96khz_audio, config->allow_96khz_audio());
1577 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1578 checked_set (_only_servers_encode, config->only_servers_encode ());
1579 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1580 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1581 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1582 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1583 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1584 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1585 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1586 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1587 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1588 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1589 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1590 #ifdef DCPOMATIC_WINDOWS
1591 checked_set (_win32_console, config->win32_console());
1595 void maximum_j2k_bandwidth_changed ()
1597 Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
1600 void video_display_mode_changed ()
1602 if (_video_display_mode->GetSelection() == 0) {
1603 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1605 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1609 void frames_in_memory_multiplier_changed ()
1611 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1614 void allow_any_dcp_frame_rate_changed ()
1616 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1619 void allow_any_container_changed ()
1621 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1624 void allow_96khz_audio_changed ()
1626 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1629 void show_experimental_audio_processors_changed ()
1631 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1634 void only_servers_encode_changed ()
1636 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1639 void dcp_metadata_filename_format_changed ()
1641 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1644 void dcp_asset_filename_format_changed ()
1646 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1652 if (_log_general->GetValue ()) {
1653 types |= LogEntry::TYPE_GENERAL;
1655 if (_log_warning->GetValue ()) {
1656 types |= LogEntry::TYPE_WARNING;
1658 if (_log_error->GetValue ()) {
1659 types |= LogEntry::TYPE_ERROR;
1661 if (_log_timing->GetValue ()) {
1662 types |= LogEntry::TYPE_TIMING;
1664 if (_log_debug_threed->GetValue ()) {
1665 types |= LogEntry::TYPE_DEBUG_THREE_D;
1667 if (_log_debug_encode->GetValue ()) {
1668 types |= LogEntry::TYPE_DEBUG_ENCODE;
1670 if (_log_debug_email->GetValue ()) {
1671 types |= LogEntry::TYPE_DEBUG_EMAIL;
1673 if (_log_debug_video_view->GetValue()) {
1674 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1676 if (_log_debug_player->GetValue()) {
1677 types |= LogEntry::TYPE_DEBUG_PLAYER;
1679 if (_log_debug_audio_analysis->GetValue()) {
1680 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1682 Config::instance()->set_log_types (types);
1685 #ifdef DCPOMATIC_WINDOWS
1686 void win32_console_changed ()
1688 Config::instance()->set_win32_console(_win32_console->GetValue());
1692 wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
1693 wxChoice* _video_display_mode = nullptr;
1694 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1695 wxCheckBox* _allow_any_dcp_frame_rate = nullptr;
1696 wxCheckBox* _allow_any_container = nullptr;
1697 wxCheckBox* _allow_96khz_audio = nullptr;
1698 wxCheckBox* _show_experimental_audio_processors = nullptr;
1699 wxCheckBox* _only_servers_encode = nullptr;
1700 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1701 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1702 wxCheckBox* _log_general = nullptr;
1703 wxCheckBox* _log_warning = nullptr;
1704 wxCheckBox* _log_error = nullptr;
1705 wxCheckBox* _log_timing = nullptr;
1706 wxCheckBox* _log_debug_threed = nullptr;
1707 wxCheckBox* _log_debug_encode = nullptr;
1708 wxCheckBox* _log_debug_email = nullptr;
1709 wxCheckBox* _log_debug_video_view = nullptr;
1710 wxCheckBox* _log_debug_player = nullptr;
1711 wxCheckBox* _log_debug_audio_analysis = nullptr;
1712 #ifdef DCPOMATIC_WINDOWS
1713 wxCheckBox* _win32_console = nullptr;
1718 wxPreferencesEditor*
1719 create_full_config_dialog ()
1721 auto e = new wxPreferencesEditor ();
1723 #ifdef DCPOMATIC_OSX
1724 /* Width that we force some of the config panels to be on OSX so that
1725 the containing window doesn't shrink too much when we select those panels.
1726 This is obviously an unpleasant hack.
1728 wxSize ps = wxSize (750, -1);
1729 int const border = 16;
1731 wxSize ps = wxSize (-1, -1);
1732 int const border = 8;
1735 e->AddPage (new FullGeneralPage (ps, border));
1736 e->AddPage (new SoundPage (ps, border));
1737 e->AddPage (new DefaultsPage (ps, border));
1738 e->AddPage (new EncodingServersPage(ps, border));
1739 e->AddPage (new KeysPage (ps, border));
1740 e->AddPage (new TMSPage (ps, border));
1741 e->AddPage (new EmailPage (ps, border));
1742 e->AddPage (new KDMEmailPage (ps, border));
1743 e->AddPage (new NotificationsPage (ps, border));
1744 e->AddPage (new CoverSheetPage (ps, border));
1745 e->AddPage (new IdentifiersPage (ps, border));
1746 e->AddPage (new AdvancedPage (ps, border));