2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/full_config_dialog.cc
23 * @brief A dialogue to edit all DCP-o-matic configuration.
27 #include "check_box.h"
28 #include "config_dialog.h"
29 #include "config_move_dialog.h"
30 #include "dcpomatic_button.h"
31 #include "dir_picker_ctrl.h"
32 #include "editable_list.h"
33 #include "email_dialog.h"
34 #include "file_picker_ctrl.h"
35 #include "filter_dialog.h"
36 #include "full_config_dialog.h"
37 #include "kdm_choice.h"
38 #include "make_chain_dialog.h"
39 #include "nag_dialog.h"
40 #include "name_format_editor.h"
41 #include "password_entry.h"
42 #include "send_test_email_dialog.h"
43 #include "server_dialog.h"
44 #include "static_text.h"
46 #include "lib/config.h"
47 #include "lib/cross.h"
48 #include "lib/dcp_content_type.h"
49 #include "lib/emailer.h"
50 #include "lib/exceptions.h"
51 #include "lib/filter.h"
53 #include "lib/ratio.h"
55 #include <dcp/certificate_chain.h>
56 #include <dcp/exceptions.h>
57 #include <dcp/locale_convert.h>
58 #include <dcp/warnings.h>
59 LIBDCP_DISABLE_WARNINGS
60 #include <wx/filepicker.h>
61 #include <wx/preferences.h>
62 #include <wx/spinctrl.h>
63 #include <wx/stdpaths.h>
64 LIBDCP_ENABLE_WARNINGS
66 #include <boost/filesystem.hpp>
76 using std::shared_ptr;
80 using boost::optional;
81 #if BOOST_VERSION >= 106100
82 using namespace boost::placeholders;
84 using dcp::locale_convert;
87 class FullGeneralPage : public GeneralPage
90 FullGeneralPage (wxSize panel_size, int border)
91 : GeneralPage (panel_size, border)
95 void setup () override
97 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
98 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
101 add_language_controls (table, r);
103 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
104 _master_encoding_threads = new wxSpinCtrl (_panel);
105 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
108 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
109 _server_encoding_threads = new wxSpinCtrl (_panel);
110 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
113 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
114 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
115 table->Add (_config_file, wxGBPosition (r, 1));
118 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
119 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
120 table->Add (_cinemas_file, wxGBPosition (r, 1));
121 auto export_cinemas = new Button (_panel, _("Export..."));
122 table->Add (export_cinemas, wxGBPosition (r, 2));
125 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
126 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
127 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
131 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
132 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
135 add_update_controls (table, r);
137 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
138 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
140 _master_encoding_threads->SetRange (1, 128);
141 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
142 _server_encoding_threads->SetRange (1, 128);
143 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
144 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
146 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
147 _analyse_ebur128->bind(&FullGeneralPage::analyse_ebur128_changed, this);
149 _automatic_audio_analysis->bind(&FullGeneralPage::automatic_audio_analysis_changed, this);
152 void config_changed () override
154 auto config = Config::instance ();
156 checked_set (_master_encoding_threads, config->master_encoding_threads ());
157 checked_set (_server_encoding_threads, config->server_encoding_threads ());
158 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
159 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
161 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
162 checked_set (_config_file, config->config_read_file());
163 checked_set (_cinemas_file, config->cinemas_file());
165 GeneralPage::config_changed ();
168 void export_cinemas_file ()
170 auto d = new wxFileDialog (
171 _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
172 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
175 if (d->ShowModal () == wxID_OK) {
176 boost::filesystem::copy_file(Config::instance()->cinemas_file(), wx_to_std(d->GetPath()), boost::filesystem::copy_option::overwrite_if_exists);
181 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
182 void analyse_ebur128_changed ()
184 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
188 void automatic_audio_analysis_changed ()
190 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
193 void master_encoding_threads_changed ()
195 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
198 void server_encoding_threads_changed ()
200 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
203 void config_file_changed ()
205 auto config = Config::instance();
206 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
207 if (new_file == config->config_read_file()) {
210 bool copy_and_link = true;
211 if (boost::filesystem::exists(new_file)) {
212 auto d = new ConfigMoveDialog (_panel, new_file);
213 if (d->ShowModal() == wxID_OK) {
214 copy_and_link = false;
221 if (new_file != config->config_read_file()) {
222 config->copy_and_link (new_file);
225 config->link (new_file);
229 void cinemas_file_changed ()
231 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
234 wxSpinCtrl* _master_encoding_threads;
235 wxSpinCtrl* _server_encoding_threads;
236 FilePickerCtrl* _config_file;
237 FilePickerCtrl* _cinemas_file;
238 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
239 CheckBox* _analyse_ebur128;
241 CheckBox* _automatic_audio_analysis;
245 class DefaultsPage : public Page
248 DefaultsPage (wxSize panel_size, int border)
249 : Page (panel_size, border)
252 wxString GetName () const override
254 return _("Defaults");
258 wxBitmap GetLargeIcon () const override
260 return wxBitmap(icon_path("defaults"), wxBITMAP_TYPE_PNG);
265 void setup () override
267 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
268 table->AddGrowableCol (1, 1);
269 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
272 add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
273 auto s = new wxBoxSizer (wxHORIZONTAL);
274 _still_length = new wxSpinCtrl (_panel);
275 s->Add (_still_length);
276 add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
280 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
281 #ifdef DCPOMATIC_USE_OWN_PICKER
282 _directory = new DirPickerCtrl (_panel);
284 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
286 table->Add (_directory, 1, wxEXPAND);
288 add_label_to_sizer (table, _panel, _("Default content type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
289 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
290 table->Add (_dcp_content_type);
292 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
293 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
294 table->Add (_dcp_audio_channels);
297 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
298 auto s = new wxBoxSizer (wxHORIZONTAL);
299 _j2k_bandwidth = new wxSpinCtrl (_panel);
300 s->Add (_j2k_bandwidth);
301 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
306 add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
307 auto s = new wxBoxSizer (wxHORIZONTAL);
308 _audio_delay = new wxSpinCtrl (_panel);
309 s->Add (_audio_delay);
310 add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
314 add_label_to_sizer (table, _panel, _("Default standard"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
315 _standard = new wxChoice (_panel, wxID_ANY);
316 table->Add (_standard);
318 table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
319 table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
321 table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
322 table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
324 table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
325 table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
327 table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
328 table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
330 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
331 #ifdef DCPOMATIC_USE_OWN_PICKER
332 _kdm_directory = new DirPickerCtrl (_panel);
334 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
336 table->Add (_kdm_directory, 1, wxEXPAND);
338 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
339 _kdm_type = new KDMChoice (_panel);
340 table->Add (_kdm_type, 1, wxEXPAND);
342 add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
343 _kdm_duration = new wxSpinCtrl (_panel);
344 _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
345 auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
346 kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
347 kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
348 table->Add (kdm_duration_sizer, 1, wxEXPAND);
350 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
352 _still_length->SetRange (1, 3600);
353 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
355 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
356 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
357 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
358 _kdm_duration_unit->Append (_("days"));
359 _kdm_duration_unit->Append (_("weeks"));
360 _kdm_duration_unit->Append (_("months"));
361 _kdm_duration_unit->Append (_("years"));
363 _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
364 _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
366 _use_isdcf_name_by_default->bind(&DefaultsPage::use_isdcf_name_by_default_changed, this);
368 for (auto i: DCPContentType::all()) {
369 _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
372 setup_audio_channels_choice (_dcp_audio_channels, 2);
374 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
375 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
377 _j2k_bandwidth->SetRange (50, 250);
378 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
380 _audio_delay->SetRange (-1000, 1000);
381 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
383 _standard->Append (_("SMPTE"));
384 _standard->Append (_("Interop"));
385 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
387 for (auto const& i: _enable_metadata) {
388 i.second->bind(&DefaultsPage::metadata_changed, this);
391 for (auto const& i: _metadata) {
392 i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
396 void config_changed () override
398 auto config = Config::instance ();
400 auto const ct = DCPContentType::all ();
401 for (size_t i = 0; i < ct.size(); ++i) {
402 if (ct[i] == config->default_dcp_content_type()) {
403 _dcp_content_type->SetSelection (i);
407 checked_set (_still_length, config->default_still_length ());
408 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
409 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
410 _kdm_type->set (config->default_kdm_type());
411 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
412 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
413 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
414 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
415 checked_set (_audio_delay, config->default_audio_delay ());
416 checked_set (_standard, config->default_interop() ? 1 : 0);
418 auto metadata = config->default_metadata();
420 for (auto const& i: metadata) {
421 _enable_metadata[i.first]->SetValue(true);
422 checked_set (_metadata[i.first], i.second);
425 for (auto const& i: _enable_metadata) {
426 if (metadata.find(i.first) == metadata.end()) {
427 checked_set (i.second, false);
431 for (auto const& i: _metadata) {
432 if (metadata.find(i.first) == metadata.end()) {
433 checked_set (i.second, wxT(""));
437 checked_set (_kdm_duration, config->default_kdm_duration().duration);
438 switch (config->default_kdm_duration().unit) {
439 case RoughDuration::Unit::DAYS:
440 _kdm_duration->SetRange(1, 365);
441 checked_set (_kdm_duration_unit, 0);
443 case RoughDuration::Unit::WEEKS:
444 _kdm_duration->SetRange(1, 52);
445 checked_set (_kdm_duration_unit, 1);
447 case RoughDuration::Unit::MONTHS:
448 _kdm_duration->SetRange(1, 12);
449 checked_set (_kdm_duration_unit, 2);
451 case RoughDuration::Unit::YEARS:
452 _kdm_duration->SetRange(1, 40);
453 checked_set (_kdm_duration_unit, 3);
457 setup_sensitivity ();
460 void kdm_duration_changed ()
462 auto config = Config::instance();
463 auto duration = _kdm_duration->GetValue();
464 RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
465 switch (_kdm_duration_unit->GetSelection()) {
467 unit = RoughDuration::Unit::DAYS;
470 unit = RoughDuration::Unit::WEEKS;
473 unit = RoughDuration::Unit::MONTHS;
476 unit = RoughDuration::Unit::YEARS;
479 config->set_default_kdm_duration (RoughDuration(duration, unit));
482 void j2k_bandwidth_changed ()
484 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
487 void audio_delay_changed ()
489 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
492 void dcp_audio_channels_changed ()
494 int const s = _dcp_audio_channels->GetSelection ();
495 if (s != wxNOT_FOUND) {
496 Config::instance()->set_default_dcp_audio_channels (
497 locale_convert<int>(string_client_data(_dcp_audio_channels->GetClientObject(s)))
502 void directory_changed ()
504 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
507 void kdm_directory_changed ()
509 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
512 void kdm_type_changed ()
514 Config::instance()->set_default_kdm_type(_kdm_type->get());
517 void use_isdcf_name_by_default_changed ()
519 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
522 void still_length_changed ()
524 Config::instance()->set_default_still_length (_still_length->GetValue ());
527 void dcp_content_type_changed ()
529 auto ct = DCPContentType::all ();
530 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
533 void standard_changed ()
535 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
538 void metadata_changed ()
540 map<string, string> metadata;
541 for (auto const& i: _enable_metadata) {
542 if (i.second->GetValue()) {
543 metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
546 Config::instance()->set_default_metadata (metadata);
547 setup_sensitivity ();
550 void setup_sensitivity ()
552 for (auto const& i: _enable_metadata) {
553 _metadata[i.first]->Enable(i.second->GetValue());
557 wxSpinCtrl* _j2k_bandwidth;
558 wxSpinCtrl* _audio_delay;
559 wxSpinCtrl* _still_length;
560 #ifdef DCPOMATIC_USE_OWN_PICKER
561 DirPickerCtrl* _directory;
562 DirPickerCtrl* _kdm_directory;
564 wxDirPickerCtrl* _directory;
565 wxDirPickerCtrl* _kdm_directory;
567 KDMChoice* _kdm_type;
568 wxSpinCtrl* _kdm_duration;
569 wxChoice* _kdm_duration_unit;
570 CheckBox* _use_isdcf_name_by_default;
571 wxChoice* _dcp_content_type;
572 wxChoice* _dcp_audio_channels;
574 map<string, CheckBox*> _enable_metadata;
575 map<string, wxTextCtrl*> _metadata;
579 class EncodingServersPage : public Page
582 EncodingServersPage (wxSize panel_size, int border)
583 : Page (panel_size, border)
586 wxString GetName () const override
592 wxBitmap GetLargeIcon () const override
594 return wxBitmap(icon_path("servers"), wxBITMAP_TYPE_PNG);
599 void setup () override
601 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
602 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
604 vector<EditableListColumn> columns;
605 columns.push_back (EditableListColumn(_("IP address / host name")));
606 _servers_list = new EditableList<string, ServerDialog> (
609 boost::bind (&Config::servers, Config::instance()),
610 boost::bind (&Config::set_servers, Config::instance(), _1),
611 boost::bind (&EncodingServersPage::server_column, this, _1),
612 EditableListTitle::INVISIBLE,
613 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
616 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
618 _use_any_servers->bind(&EncodingServersPage::use_any_servers_changed, this);
621 void config_changed () override
623 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
624 _servers_list->refresh ();
627 void use_any_servers_changed ()
629 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
632 string server_column (string s)
637 CheckBox* _use_any_servers;
638 EditableList<string, ServerDialog>* _servers_list;
642 class TMSPage : public Page
645 TMSPage (wxSize panel_size, int border)
646 : Page (panel_size, border)
649 wxString GetName () const override
655 wxBitmap GetLargeIcon () const override
657 return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
662 void setup () override
664 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
665 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
667 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
668 table->AddGrowableCol (1, 1);
669 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
671 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
672 _tms_protocol = new wxChoice (_panel, wxID_ANY);
673 table->Add (_tms_protocol, 1, wxEXPAND);
675 _tms_passive = new CheckBox(_panel, _("Passive mode"));
676 table->Add(_tms_passive, 1, wxEXPAND);
679 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
680 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
681 table->Add (_tms_ip, 1, wxEXPAND);
683 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
684 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
685 table->Add (_tms_path, 1, wxEXPAND);
687 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
688 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
689 table->Add (_tms_user, 1, wxEXPAND);
691 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
692 _tms_password = new PasswordEntry (_panel);
693 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
695 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
696 _tms_protocol->Append (_("FTP (for Dolby)"));
698 _upload->bind(&TMSPage::upload_changed, this);
699 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
700 _tms_passive->bind(&TMSPage::tms_passive_changed, this);
702 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
703 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
704 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
705 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
708 void config_changed () override
710 auto config = Config::instance ();
712 checked_set (_upload, config->upload_after_make_dcp());
713 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
714 checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
715 checked_set (_tms_ip, config->tms_ip ());
716 checked_set (_tms_path, config->tms_path ());
717 checked_set (_tms_user, config->tms_user ());
718 checked_set (_tms_password, config->tms_password ());
720 _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
723 void upload_changed ()
725 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
728 void tms_protocol_changed ()
730 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
733 void tms_passive_changed()
735 Config::instance()->set_tms_passive(_tms_passive->get());
738 void tms_ip_changed ()
740 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
743 void tms_path_changed ()
745 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
748 void tms_user_changed ()
750 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
753 void tms_password_changed ()
755 Config::instance()->set_tms_password (_tms_password->get());
759 CheckBox* _tms_passive;
760 wxChoice* _tms_protocol;
762 wxTextCtrl* _tms_path;
763 wxTextCtrl* _tms_user;
764 PasswordEntry* _tms_password;
768 class EmailPage : public Page
771 EmailPage (wxSize panel_size, int border)
772 : Page (panel_size, border)
775 wxString GetName () const override
781 wxBitmap GetLargeIcon () const override
783 return wxBitmap(icon_path("email"), wxBITMAP_TYPE_PNG);
788 void setup () override
790 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
791 table->AddGrowableCol (1, 1);
792 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
794 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
796 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
797 _server = new wxTextCtrl (_panel, wxID_ANY);
798 s->Add (_server, 1, wxEXPAND | wxALL);
799 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
800 _port = new wxSpinCtrl (_panel, wxID_ANY);
801 _port->SetRange (0, 65535);
803 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
804 _protocol = new wxChoice (_panel, wxID_ANY);
805 /* Make sure this matches the switches in config_changed and port_changed below */
806 _protocol->Append (_("Auto"));
807 _protocol->Append (_("Plain"));
808 _protocol->Append (_("STARTTLS"));
809 _protocol->Append (_("SSL"));
810 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
811 table->Add (s, 1, wxEXPAND | wxALL);
814 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
815 _user = new wxTextCtrl (_panel, wxID_ANY);
816 table->Add (_user, 1, wxEXPAND | wxALL);
818 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
819 _password = new PasswordEntry (_panel);
820 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
822 table->AddSpacer (0);
823 _send_test_email = new Button (_panel, _("Send test email..."));
824 table->Add (_send_test_email);
826 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
827 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
828 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
829 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
830 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
831 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
834 void config_changed () override
836 auto config = Config::instance ();
838 checked_set (_server, config->mail_server());
839 checked_set (_port, config->mail_port());
840 switch (config->mail_protocol()) {
841 case EmailProtocol::AUTO:
842 checked_set (_protocol, 0);
844 case EmailProtocol::PLAIN:
845 checked_set (_protocol, 1);
847 case EmailProtocol::STARTTLS:
848 checked_set (_protocol, 2);
850 case EmailProtocol::SSL:
851 checked_set (_protocol, 3);
854 checked_set (_user, config->mail_user());
855 checked_set (_password, config->mail_password());
858 void server_changed ()
860 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
865 Config::instance()->set_mail_port(_port->GetValue());
868 void protocol_changed ()
870 switch (_protocol->GetSelection()) {
872 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
875 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
878 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
881 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
888 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
891 void password_changed ()
893 Config::instance()->set_mail_password(_password->get());
896 void send_test_email_clicked ()
898 auto dialog = new SendTestEmailDialog(_panel);
899 auto result = dialog->ShowModal();
901 if (result == wxID_OK) {
903 wx_to_std(dialog->from()),
904 { wx_to_std(dialog->to()) },
905 wx_to_std(_("DCP-o-matic test email")),
906 wx_to_std(_("This is a test email from DCP-o-matic."))
908 auto config = Config::instance();
910 emailer.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
911 } catch (NetworkError& e) {
912 error_dialog (_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or("")));
914 } catch (std::exception& e) {
915 error_dialog (_panel, _("Test email sending failed."), std_to_wx(e.what()));
918 error_dialog (_panel, _("Test email sending failed."));
921 message_dialog (_panel, _("Test email sent."));
929 PasswordEntry* _password;
930 Button* _send_test_email;
934 class KDMEmailPage : public Page
938 KDMEmailPage (wxSize panel_size, int border)
940 /* We have to force both width and height of this one */
941 : Page (wxSize (panel_size.GetWidth(), 128), border)
943 : Page (panel_size, border)
947 wxString GetName () const override
949 return _("KDM Email");
953 wxBitmap GetLargeIcon () const override
955 return wxBitmap(icon_path("kdm_email"), wxBITMAP_TYPE_PNG);
960 void setup () override
962 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
963 table->AddGrowableCol (1, 1);
964 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
966 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
967 _subject = new wxTextCtrl (_panel, wxID_ANY);
968 table->Add (_subject, 1, wxEXPAND | wxALL);
970 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
971 _from = new wxTextCtrl (_panel, wxID_ANY);
972 table->Add (_from, 1, wxEXPAND | wxALL);
974 vector<EditableListColumn> columns;
975 columns.push_back (EditableListColumn(_("Address")));
976 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
977 _cc = new EditableList<string, EmailDialog> (
980 bind (&Config::kdm_cc, Config::instance()),
981 bind (&Config::set_kdm_cc, Config::instance(), _1),
985 EditableListTitle::VISIBLE,
986 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
988 table->Add (_cc, 1, wxEXPAND | wxALL);
990 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
991 _bcc = new wxTextCtrl (_panel, wxID_ANY);
992 table->Add (_bcc, 1, wxEXPAND | wxALL);
994 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
995 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
997 _reset_email = new Button (_panel, _("Reset to default subject and text"));
998 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1002 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1003 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1004 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1005 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1006 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
1009 void config_changed () override
1011 auto config = Config::instance ();
1013 checked_set (_subject, config->kdm_subject ());
1014 checked_set (_from, config->kdm_from ());
1015 checked_set (_bcc, config->kdm_bcc ());
1016 checked_set (_email, Config::instance()->kdm_email ());
1019 void kdm_subject_changed ()
1021 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
1024 void kdm_from_changed ()
1026 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
1029 void kdm_bcc_changed ()
1031 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
1034 void kdm_email_changed ()
1036 if (_email->GetValue().IsEmpty ()) {
1037 /* Sometimes we get sent an erroneous notification that the email
1038 is empty; I don't know why.
1042 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
1047 Config::instance()->reset_kdm_email ();
1048 checked_set (_email, Config::instance()->kdm_email ());
1051 wxTextCtrl* _subject;
1053 EditableList<string, EmailDialog>* _cc;
1056 wxButton* _reset_email;
1060 class NotificationsPage : public Page
1063 NotificationsPage (wxSize panel_size, int border)
1064 #ifdef DCPOMATIC_OSX
1065 /* We have to force both width and height of this one */
1066 : Page (wxSize (panel_size.GetWidth(), 128), border)
1068 : Page (panel_size, border)
1072 wxString GetName () const override
1074 return _("Notifications");
1077 #ifdef DCPOMATIC_OSX
1078 wxBitmap GetLargeIcon () const override
1080 return wxBitmap(icon_path("notifications"), wxBITMAP_TYPE_PNG);
1085 void setup () override
1087 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1088 table->AddGrowableCol (1, 1);
1089 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1091 _enable_message_box = new CheckBox (_panel, _("Message box"));
1092 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1093 table->AddSpacer (0);
1095 _enable_email = new CheckBox (_panel, _("Email"));
1096 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1097 table->AddSpacer (0);
1099 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1100 _subject = new wxTextCtrl (_panel, wxID_ANY);
1101 table->Add (_subject, 1, wxEXPAND | wxALL);
1103 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1104 _from = new wxTextCtrl (_panel, wxID_ANY);
1105 table->Add (_from, 1, wxEXPAND | wxALL);
1107 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1108 _to = new wxTextCtrl (_panel, wxID_ANY);
1109 table->Add (_to, 1, wxEXPAND | wxALL);
1111 vector<EditableListColumn> columns;
1112 columns.push_back (EditableListColumn(_("Address")));
1113 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1114 _cc = new EditableList<string, EmailDialog> (
1117 bind (&Config::notification_cc, Config::instance()),
1118 bind (&Config::set_notification_cc, Config::instance(), _1),
1119 [] (string s, int) {
1122 EditableListTitle::VISIBLE,
1123 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1125 table->Add (_cc, 1, wxEXPAND | wxALL);
1127 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1128 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1129 table->Add (_bcc, 1, wxEXPAND | wxALL);
1131 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1132 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1134 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1135 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1139 _enable_message_box->bind(&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX);
1140 _enable_email->bind(&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL);
1142 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1143 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1144 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1145 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1146 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1147 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1149 setup_sensitivity ();
1152 void setup_sensitivity ()
1154 bool const s = _enable_email->GetValue();
1155 _subject->Enable(s);
1161 _reset_email->Enable(s);
1164 void config_changed () override
1166 auto config = Config::instance ();
1168 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1169 checked_set (_enable_email, config->notification(Config::EMAIL));
1170 checked_set (_subject, config->notification_subject ());
1171 checked_set (_from, config->notification_from ());
1172 checked_set (_to, config->notification_to ());
1173 checked_set (_bcc, config->notification_bcc ());
1174 checked_set (_email, Config::instance()->notification_email ());
1176 setup_sensitivity ();
1179 void notification_subject_changed ()
1181 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1184 void notification_from_changed ()
1186 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1189 void notification_to_changed ()
1191 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1194 void notification_bcc_changed ()
1196 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1199 void notification_email_changed ()
1201 if (_email->GetValue().IsEmpty()) {
1202 /* Sometimes we get sent an erroneous notification that the email
1203 is empty; I don't know why.
1207 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1212 Config::instance()->reset_notification_email();
1213 checked_set (_email, Config::instance()->notification_email());
1216 void type_changed (CheckBox* b, Config::Notification n)
1218 Config::instance()->set_notification(n, b->GetValue());
1219 setup_sensitivity ();
1222 CheckBox* _enable_message_box;
1223 CheckBox* _enable_email;
1225 wxTextCtrl* _subject;
1228 EditableList<string, EmailDialog>* _cc;
1231 wxButton* _reset_email;
1235 class CoverSheetPage : public Page
1239 CoverSheetPage (wxSize panel_size, int border)
1240 #ifdef DCPOMATIC_OSX
1241 /* We have to force both width and height of this one */
1242 : Page (wxSize (panel_size.GetWidth(), 128), border)
1244 : Page (panel_size, border)
1248 wxString GetName () const override
1250 return _("Cover Sheet");
1253 #ifdef DCPOMATIC_OSX
1254 wxBitmap GetLargeIcon () const override
1256 return wxBitmap(icon_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1261 void setup () override
1263 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1264 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1266 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1267 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1269 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1270 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1273 void config_changed () override
1275 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1278 void cover_sheet_changed ()
1280 if (_cover_sheet->GetValue().IsEmpty ()) {
1281 /* Sometimes we get sent an erroneous notification that the cover sheet
1282 is empty; I don't know why.
1286 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1289 void reset_cover_sheet ()
1291 Config::instance()->reset_cover_sheet();
1292 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1295 wxTextCtrl* _cover_sheet;
1296 wxButton* _reset_cover_sheet;
1300 class IdentifiersPage : public Page
1303 IdentifiersPage (wxSize panel_size, int border)
1304 : Page (panel_size, border)
1307 wxString GetName () const override
1309 return _("Identifiers");
1312 #ifdef DCPOMATIC_OSX
1313 wxBitmap GetLargeIcon () const override
1315 return wxBitmap(icon_path("identifiers"), wxBITMAP_TYPE_PNG);
1320 void setup () override
1322 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1323 table->AddGrowableCol (1, 1);
1325 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1326 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1327 _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."));
1328 table->Add (_issuer, 1, wxALL | wxEXPAND);
1330 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1331 _creator = new wxTextCtrl (_panel, wxID_ANY);
1332 _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."));
1333 table->Add (_creator, 1, wxALL | wxEXPAND);
1335 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1336 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1337 _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."));
1338 table->Add (_company_name, 1, wxALL | wxEXPAND);
1340 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1341 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1342 _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."));
1343 table->Add (_product_name, 1, wxALL | wxEXPAND);
1345 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1346 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1347 _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."));
1348 table->Add (_product_version, 1, wxALL | wxEXPAND);
1350 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1351 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1352 _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."));
1353 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1355 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1357 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1358 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1359 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1360 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1361 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1362 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1365 void config_changed () override
1367 auto config = Config::instance ();
1368 checked_set (_issuer, config->dcp_issuer ());
1369 checked_set (_creator, config->dcp_creator ());
1370 checked_set (_company_name, config->dcp_company_name ());
1371 checked_set (_product_name, config->dcp_product_name ());
1372 checked_set (_product_version, config->dcp_product_version ());
1373 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1376 void issuer_changed ()
1378 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1381 void creator_changed ()
1383 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1386 void company_name_changed ()
1388 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1391 void product_name_changed ()
1393 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1396 void product_version_changed ()
1398 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1401 void j2k_comment_changed ()
1403 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1406 wxTextCtrl* _issuer;
1407 wxTextCtrl* _creator;
1408 wxTextCtrl* _company_name;
1409 wxTextCtrl* _product_name;
1410 wxTextCtrl* _product_version;
1411 wxTextCtrl* _j2k_comment;
1415 /** @class AdvancedPage
1416 * @brief Advanced page of the preferences dialog.
1418 class AdvancedPage : public Page
1421 AdvancedPage (wxSize panel_size, int border)
1422 : Page (panel_size, border)
1425 wxString GetName () const override
1427 return _("Advanced");
1430 #ifdef DCPOMATIC_OSX
1431 wxBitmap GetLargeIcon () const override
1433 return wxBitmap(icon_path("advanced"), wxBITMAP_TYPE_PNG);
1438 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1440 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1442 flags |= wxALIGN_RIGHT;
1445 wxStaticText* m = new StaticText (parent, text);
1446 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1449 void setup () override
1451 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1452 table->AddGrowableCol (1, 1);
1453 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1456 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1457 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1458 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1459 s->Add (_maximum_j2k_bandwidth, 1);
1460 add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1464 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1465 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1466 table->Add (_video_display_mode);
1468 auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1469 auto font = restart->GetFont();
1470 font.SetStyle (wxFONTSTYLE_ITALIC);
1471 font.SetPointSize (font.GetPointSize() - 1);
1472 restart->SetFont (font);
1473 table->AddSpacer (0);
1475 _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
1476 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1477 table->AddSpacer (0);
1479 _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
1480 table->Add (_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1481 restart = new StaticText (_panel, _("(restart DCP-o-matic to see all ratios)"));
1482 table->Add (restart, 1, wxEXPAND | wxALL | wxALIGN_CENTRE_VERTICAL);
1483 restart->SetFont (font);
1485 _allow_96khz_audio = new CheckBox (_panel, _("Allow creation of DCPs with 96kHz audio"));
1486 table->Add (_allow_96khz_audio, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1487 table->AddSpacer (0);
1489 _use_all_audio_channels = new CheckBox(_panel, _("Allow mapping to all audio channels"));
1490 table->Add(_use_all_audio_channels, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1491 table->AddSpacer(0);
1493 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1494 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1495 table->AddSpacer (0);
1497 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1498 table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1499 table->AddSpacer (0);
1502 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1503 auto s = new wxBoxSizer (wxHORIZONTAL);
1504 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1505 s->Add (_frames_in_memory_multiplier, 1);
1510 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1511 #ifdef DCPOMATIC_OSX
1512 auto align = new wxBoxSizer (wxHORIZONTAL);
1513 align->Add (format, 0, wxTOP, 2);
1514 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1516 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1518 dcp::NameFormat::Map titles;
1519 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1520 dcp::NameFormat::Map examples;
1521 examples['t'] = "cpl";
1522 _dcp_metadata_filename_format = new NameFormatEditor (
1523 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1525 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1529 auto format = create_label (_panel, _("DCP asset filename format"), true);
1530 #ifdef DCPOMATIC_OSX
1531 auto align = new wxBoxSizer (wxHORIZONTAL);
1532 align->Add (format, 0, wxTOP, 2);
1533 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1535 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1537 dcp::NameFormat::Map titles;
1538 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1539 titles['r'] = wx_to_std (_("reel number"));
1540 titles['n'] = wx_to_std (_("number of reels"));
1541 titles['c'] = wx_to_std (_("content filename"));
1542 dcp::NameFormat::Map examples;
1543 examples['t'] = "j2c";
1544 examples['r'] = "1";
1545 examples['n'] = "4";
1546 examples['c'] = "myfile.mp4";
1547 _dcp_asset_filename_format = new NameFormatEditor (
1548 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1550 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1554 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1555 auto t = new wxFlexGridSizer (2);
1556 _log_general = new CheckBox (_panel, _("General"));
1557 t->Add (_log_general, 1, wxEXPAND | wxALL);
1558 _log_warning = new CheckBox (_panel, _("Warnings"));
1559 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1560 _log_error = new CheckBox (_panel, _("Errors"));
1561 t->Add (_log_error, 1, wxEXPAND | wxALL);
1562 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1563 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1564 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1565 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1566 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1567 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1568 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1569 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1570 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1571 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1572 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1573 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1574 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1575 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1576 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1577 table->Add (t, 0, wxALL, 6);
1580 #ifdef DCPOMATIC_WINDOWS
1581 _win32_console = new CheckBox (_panel, _("Open console window"));
1582 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1583 table->AddSpacer (0);
1586 _maximum_j2k_bandwidth->SetRange (1, 1000);
1587 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1588 _video_display_mode->Append (_("Simple (safer)"));
1589 #if wxCHECK_VERSION(3, 1, 0)
1590 _video_display_mode->Append (_("OpenGL (faster)"));
1592 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1593 _allow_any_dcp_frame_rate->bind(&AdvancedPage::allow_any_dcp_frame_rate_changed, this);
1594 _allow_any_container->bind(&AdvancedPage::allow_any_container_changed, this);
1595 _allow_96khz_audio->bind(&AdvancedPage::allow_96khz_audio_changed, this);
1596 _use_all_audio_channels->bind(&AdvancedPage::use_all_channels_changed, this);
1597 _show_experimental_audio_processors->bind(&AdvancedPage::show_experimental_audio_processors_changed, this);
1598 _only_servers_encode->bind(&AdvancedPage::only_servers_encode_changed, this);
1599 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1600 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1601 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1602 _log_general->bind(&AdvancedPage::log_changed, this);
1603 _log_warning->bind(&AdvancedPage::log_changed, this);
1604 _log_error->bind(&AdvancedPage::log_changed, this);
1605 _log_timing->bind(&AdvancedPage::log_changed, this);
1606 _log_debug_threed->bind(&AdvancedPage::log_changed, this);
1607 _log_debug_encode->bind(&AdvancedPage::log_changed, this);
1608 _log_debug_email->bind(&AdvancedPage::log_changed, this);
1609 _log_debug_video_view->bind(&AdvancedPage::log_changed, this);
1610 _log_debug_player->bind(&AdvancedPage::log_changed, this);
1611 _log_debug_audio_analysis->bind(&AdvancedPage::log_changed, this);
1612 #ifdef DCPOMATIC_WINDOWS
1613 _win32_console->bind(&AdvancedPage::win32_console_changed, this);
1617 void config_changed () override
1619 auto config = Config::instance ();
1621 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1622 switch (config->video_view_type()) {
1623 case Config::VIDEO_VIEW_SIMPLE:
1624 checked_set (_video_display_mode, 0);
1626 case Config::VIDEO_VIEW_OPENGL:
1627 checked_set (_video_display_mode, 1);
1630 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1631 checked_set (_allow_any_container, config->allow_any_container ());
1632 checked_set (_allow_96khz_audio, config->allow_96khz_audio());
1633 checked_set (_use_all_audio_channels, config->use_all_audio_channels());
1634 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1635 checked_set (_only_servers_encode, config->only_servers_encode ());
1636 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1637 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1638 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1639 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1640 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1641 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1642 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1643 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1644 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1645 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1646 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1647 #ifdef DCPOMATIC_WINDOWS
1648 checked_set (_win32_console, config->win32_console());
1652 void maximum_j2k_bandwidth_changed ()
1654 Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
1657 void video_display_mode_changed ()
1659 if (_video_display_mode->GetSelection() == 0) {
1660 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1662 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1666 void frames_in_memory_multiplier_changed ()
1668 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1671 void allow_any_dcp_frame_rate_changed ()
1673 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1676 void allow_any_container_changed ()
1678 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1681 void allow_96khz_audio_changed ()
1683 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1686 void use_all_channels_changed ()
1688 Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
1691 void show_experimental_audio_processors_changed ()
1693 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1696 void only_servers_encode_changed ()
1698 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1701 void dcp_metadata_filename_format_changed ()
1703 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1706 void dcp_asset_filename_format_changed ()
1708 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1714 if (_log_general->GetValue ()) {
1715 types |= LogEntry::TYPE_GENERAL;
1717 if (_log_warning->GetValue ()) {
1718 types |= LogEntry::TYPE_WARNING;
1720 if (_log_error->GetValue ()) {
1721 types |= LogEntry::TYPE_ERROR;
1723 if (_log_timing->GetValue ()) {
1724 types |= LogEntry::TYPE_TIMING;
1726 if (_log_debug_threed->GetValue ()) {
1727 types |= LogEntry::TYPE_DEBUG_THREE_D;
1729 if (_log_debug_encode->GetValue ()) {
1730 types |= LogEntry::TYPE_DEBUG_ENCODE;
1732 if (_log_debug_email->GetValue ()) {
1733 types |= LogEntry::TYPE_DEBUG_EMAIL;
1735 if (_log_debug_video_view->GetValue()) {
1736 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1738 if (_log_debug_player->GetValue()) {
1739 types |= LogEntry::TYPE_DEBUG_PLAYER;
1741 if (_log_debug_audio_analysis->GetValue()) {
1742 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1744 Config::instance()->set_log_types (types);
1747 #ifdef DCPOMATIC_WINDOWS
1748 void win32_console_changed ()
1750 Config::instance()->set_win32_console(_win32_console->GetValue());
1754 wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
1755 wxChoice* _video_display_mode = nullptr;
1756 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1757 CheckBox* _allow_any_dcp_frame_rate = nullptr;
1758 CheckBox* _allow_any_container = nullptr;
1759 CheckBox* _allow_96khz_audio = nullptr;
1760 CheckBox* _use_all_audio_channels = nullptr;
1761 CheckBox* _show_experimental_audio_processors = nullptr;
1762 CheckBox* _only_servers_encode = nullptr;
1763 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1764 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1765 CheckBox* _log_general = nullptr;
1766 CheckBox* _log_warning = nullptr;
1767 CheckBox* _log_error = nullptr;
1768 CheckBox* _log_timing = nullptr;
1769 CheckBox* _log_debug_threed = nullptr;
1770 CheckBox* _log_debug_encode = nullptr;
1771 CheckBox* _log_debug_email = nullptr;
1772 CheckBox* _log_debug_video_view = nullptr;
1773 CheckBox* _log_debug_player = nullptr;
1774 CheckBox* _log_debug_audio_analysis = nullptr;
1775 #ifdef DCPOMATIC_WINDOWS
1776 CheckBox* _win32_console = nullptr;
1781 wxPreferencesEditor*
1782 create_full_config_dialog ()
1784 auto e = new wxPreferencesEditor ();
1786 #ifdef DCPOMATIC_OSX
1787 /* Width that we force some of the config panels to be on OSX so that
1788 the containing window doesn't shrink too much when we select those panels.
1789 This is obviously an unpleasant hack.
1791 wxSize ps = wxSize (750, -1);
1792 int const border = 16;
1794 wxSize ps = wxSize (-1, -1);
1795 int const border = 8;
1798 e->AddPage (new FullGeneralPage (ps, border));
1799 e->AddPage (new SoundPage (ps, border));
1800 e->AddPage (new DefaultsPage (ps, border));
1801 e->AddPage (new EncodingServersPage(ps, border));
1802 e->AddPage (new KeysPage (ps, border));
1803 e->AddPage (new TMSPage (ps, border));
1804 e->AddPage (new EmailPage (ps, border));
1805 e->AddPage (new KDMEmailPage (ps, border));
1806 e->AddPage (new NotificationsPage (ps, border));
1807 e->AddPage (new CoverSheetPage (ps, border));
1808 e->AddPage (new IdentifiersPage (ps, border));
1809 e->AddPage (new AdvancedPage (ps, border));