2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/full_config_dialog.cc
23 * @brief A dialogue to edit all DCP-o-matic configuration.
27 #include "check_box.h"
28 #include "config_dialog.h"
29 #include "config_move_dialog.h"
30 #include "dcpomatic_button.h"
31 #include "dcpomatic_choice.h"
32 #include "dir_picker_ctrl.h"
33 #include "editable_list.h"
34 #include "email_dialog.h"
35 #include "file_picker_ctrl.h"
36 #include "filter_dialog.h"
37 #include "full_config_dialog.h"
38 #include "kdm_choice.h"
39 #include "language_tag_widget.h"
40 #include "make_chain_dialog.h"
41 #include "nag_dialog.h"
42 #include "name_format_editor.h"
43 #include "password_entry.h"
44 #include "region_subtag_widget.h"
45 #include "send_test_email_dialog.h"
46 #include "server_dialog.h"
47 #include "static_text.h"
49 #include "lib/config.h"
50 #include "lib/cross.h"
51 #include "lib/dcp_content_type.h"
52 #include "lib/emailer.h"
53 #include "lib/exceptions.h"
54 #include "lib/filter.h"
56 #include "lib/ratio.h"
58 #include <dcp/certificate_chain.h>
59 #include <dcp/exceptions.h>
60 #include <dcp/locale_convert.h>
61 #include <dcp/warnings.h>
62 LIBDCP_DISABLE_WARNINGS
63 #include <wx/filepicker.h>
64 #include <wx/preferences.h>
65 #include <wx/spinctrl.h>
66 #include <wx/stdpaths.h>
67 LIBDCP_ENABLE_WARNINGS
69 #include <boost/filesystem.hpp>
79 using std::shared_ptr;
83 using boost::optional;
84 #if BOOST_VERSION >= 106100
85 using namespace boost::placeholders;
87 using dcp::locale_convert;
90 class FullGeneralPage : public GeneralPage
93 FullGeneralPage (wxSize panel_size, int border)
94 : GeneralPage (panel_size, border)
98 void setup () override
100 auto table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
101 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
104 add_language_controls (table, r);
106 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
107 _master_encoding_threads = new wxSpinCtrl (_panel);
108 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
111 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
112 _server_encoding_threads = new wxSpinCtrl (_panel);
113 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
116 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
117 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, false);
118 table->Add (_config_file, wxGBPosition (r, 1));
121 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
122 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, false);
123 table->Add (_cinemas_file, wxGBPosition (r, 1));
124 auto export_cinemas = new Button (_panel, _("Export..."));
125 table->Add (export_cinemas, wxGBPosition (r, 2));
128 add_label_to_sizer(table, _panel, _("Default \"add file\" location"), true, wxGBPosition(r, 0));
129 _default_add_file_location = new Choice(_panel);
130 table->Add(_default_add_file_location, wxGBPosition(r, 1));
133 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
134 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
135 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
139 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
140 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
143 add_update_controls (table, r);
145 _default_add_file_location->add(_("Same place as last time"));
146 _default_add_file_location->add(_("Same place as project"));
147 _default_add_file_location->bind(&FullGeneralPage::default_add_file_location_changed, this);
149 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
150 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this));
152 _master_encoding_threads->SetRange (1, 128);
153 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
154 _server_encoding_threads->SetRange (1, 128);
155 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
156 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
158 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
159 _analyse_ebur128->bind(&FullGeneralPage::analyse_ebur128_changed, this);
161 _automatic_audio_analysis->bind(&FullGeneralPage::automatic_audio_analysis_changed, this);
164 void config_changed () override
166 auto config = Config::instance ();
168 checked_set (_master_encoding_threads, config->master_encoding_threads ());
169 checked_set (_server_encoding_threads, config->server_encoding_threads ());
170 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
171 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
173 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
174 checked_set (_config_file, config->config_read_file());
175 checked_set (_cinemas_file, config->cinemas_file());
176 checked_set(_default_add_file_location, config->default_add_file_location() == Config::DefaultAddFileLocation::SAME_AS_LAST_TIME ? 0 : 1);
178 GeneralPage::config_changed ();
181 void export_cinemas_file ()
184 _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"),
185 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
188 if (dialog.ShowModal() == wxID_OK) {
189 boost::filesystem::copy_file(Config::instance()->cinemas_file(), wx_to_std(dialog.GetPath()), boost::filesystem::copy_option::overwrite_if_exists);
193 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
194 void analyse_ebur128_changed ()
196 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue());
200 void automatic_audio_analysis_changed ()
202 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue());
205 void master_encoding_threads_changed ()
207 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue());
210 void server_encoding_threads_changed ()
212 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue());
215 void config_file_changed ()
217 auto config = Config::instance();
218 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
219 if (new_file == config->config_read_file()) {
222 bool copy_and_link = true;
223 if (boost::filesystem::exists(new_file)) {
224 ConfigMoveDialog dialog(_panel, new_file);
225 if (dialog.ShowModal() == wxID_OK) {
226 copy_and_link = false;
232 if (new_file != config->config_read_file()) {
233 config->copy_and_link (new_file);
236 config->link (new_file);
240 void cinemas_file_changed ()
242 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
245 void default_add_file_location_changed()
247 Config::instance()->set_default_add_file_location(
248 _default_add_file_location->get().get_value_or(0) == 0 ? Config::DefaultAddFileLocation::SAME_AS_LAST_TIME : Config::DefaultAddFileLocation::SAME_AS_PROJECT
252 Choice* _default_add_file_location;
253 wxSpinCtrl* _master_encoding_threads;
254 wxSpinCtrl* _server_encoding_threads;
255 FilePickerCtrl* _config_file;
256 FilePickerCtrl* _cinemas_file;
257 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
258 CheckBox* _analyse_ebur128;
260 CheckBox* _automatic_audio_analysis;
264 class DefaultsPage : public Page
267 DefaultsPage (wxSize panel_size, int border)
268 : Page (panel_size, border)
271 wxString GetName () const override
273 return _("Defaults");
277 wxBitmap GetLargeIcon () const override
279 return wxBitmap(icon_path("defaults"), wxBITMAP_TYPE_PNG);
284 void setup () override
286 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
287 table->AddGrowableCol (1, 1);
288 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
291 add_label_to_sizer (table, _panel, _("Default duration of still images"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
292 auto s = new wxBoxSizer (wxHORIZONTAL);
293 _still_length = new wxSpinCtrl (_panel);
294 s->Add (_still_length);
295 add_label_to_sizer (s, _panel, _("s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
299 add_label_to_sizer (table, _panel, _("Default directory for new films"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
300 #ifdef DCPOMATIC_USE_OWN_PICKER
301 _directory = new DirPickerCtrl (_panel);
303 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
305 table->Add (_directory, 1, wxEXPAND);
308 add_label_to_sizer (table, _panel, _("Default audio delay"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
309 auto s = new wxBoxSizer (wxHORIZONTAL);
310 _audio_delay = new wxSpinCtrl (_panel);
311 s->Add (_audio_delay);
312 add_label_to_sizer (s, _panel, _("ms"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
316 _enable_audio_language = new CheckBox(_panel, _("Default audio language"));
317 table->Add(_enable_audio_language, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL);
318 _audio_language = new LanguageTagWidget(_panel, _("Default audio language to use for new DCPs"), Config::instance()->default_audio_language(), wxString("cmnr-Hant-"));
319 table->Add(_audio_language->sizer());
321 _enable_territory = new CheckBox(_panel, _("Default territory"));
322 table->Add(_enable_territory, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL);
323 _territory = new RegionSubtagWidget(_panel, _("Default territory to use for new DCPs"), Config::instance()->default_territory(), wxString("cmnr-Hant-"));
324 table->Add(_territory->sizer());
326 table->Add (_enable_metadata["facility"] = new CheckBox (_panel, _("Default facility")), 0, wxALIGN_CENTRE_VERTICAL);
327 table->Add (_metadata["facility"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
329 table->Add (_enable_metadata["studio"] = new CheckBox (_panel, _("Default studio")), 0, wxALIGN_CENTRE_VERTICAL);
330 table->Add (_metadata["studio"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
332 table->Add (_enable_metadata["chain"] = new CheckBox (_panel, _("Default chain")), 0, wxALIGN_CENTRE_VERTICAL);
333 table->Add (_metadata["chain"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
335 table->Add (_enable_metadata["distributor"] = new CheckBox (_panel, _("Default distributor")), 0, wxALIGN_CENTRE_VERTICAL);
336 table->Add (_metadata["distributor"] = new wxTextCtrl (_panel, wxID_ANY, wxT("")), 0, wxEXPAND);
338 add_label_to_sizer (table, _panel, _("Default KDM directory"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
339 #ifdef DCPOMATIC_USE_OWN_PICKER
340 _kdm_directory = new DirPickerCtrl (_panel);
342 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
344 table->Add (_kdm_directory, 1, wxEXPAND);
346 add_label_to_sizer (table, _panel, _("Default KDM type"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
347 _kdm_type = new KDMChoice (_panel);
348 table->Add (_kdm_type, 1, wxEXPAND);
350 add_label_to_sizer (table, _panel, _("Default KDM duration"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
351 _kdm_duration = new wxSpinCtrl (_panel);
352 _kdm_duration_unit = new wxChoice (_panel, wxID_ANY);
353 auto kdm_duration_sizer = new wxBoxSizer (wxHORIZONTAL);
354 kdm_duration_sizer->Add (_kdm_duration, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
355 kdm_duration_sizer->Add (_kdm_duration_unit, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
356 table->Add (kdm_duration_sizer, 1, wxEXPAND);
358 table->Add (_use_isdcf_name_by_default = new CheckBox(_panel, _("Use ISDCF name by default")), 0, wxALIGN_CENTRE_VERTICAL);
360 _still_length->SetRange (1, 3600);
361 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
363 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
364 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
365 _kdm_type->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_type_changed, this));
366 _kdm_duration_unit->Append (_("days"));
367 _kdm_duration_unit->Append (_("weeks"));
368 _kdm_duration_unit->Append (_("months"));
369 _kdm_duration_unit->Append (_("years"));
371 _kdm_duration->Bind (wxEVT_SPINCTRL, boost::bind(&DefaultsPage::kdm_duration_changed, this));
372 _kdm_duration_unit->Bind (wxEVT_CHOICE, boost::bind(&DefaultsPage::kdm_duration_changed, this));
374 _use_isdcf_name_by_default->bind(&DefaultsPage::use_isdcf_name_by_default_changed, this);
376 _audio_delay->SetRange (-1000, 1000);
377 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
379 for (auto const& i: _enable_metadata) {
380 i.second->bind(&DefaultsPage::metadata_changed, this);
383 for (auto const& i: _metadata) {
384 i.second->Bind (wxEVT_TEXT, boost::bind(&DefaultsPage::metadata_changed, this));
387 _enable_audio_language->bind(&DefaultsPage::enable_audio_language_toggled, this);
388 _audio_language->Changed.connect(boost::bind(&DefaultsPage::audio_language_changed, this));
390 _enable_territory->bind(&DefaultsPage::enable_territory_toggled, this);
391 _territory->Changed.connect(boost::bind(&DefaultsPage::territory_changed, this));
394 void config_changed () override
396 auto config = Config::instance ();
398 checked_set (_still_length, config->default_still_length ());
399 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
400 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
401 _kdm_type->set (config->default_kdm_type());
402 checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default());
403 checked_set (_audio_delay, config->default_audio_delay ());
404 auto dal = config->default_audio_language();
405 checked_set(_enable_audio_language, static_cast<bool>(dal));
406 checked_set(_audio_language, dal ? dal : boost::none);
407 auto dt = config->default_territory();
408 checked_set(_enable_territory, static_cast<bool>(dt));
409 checked_set(_territory, dt ? dt : boost::none);
411 auto metadata = config->default_metadata();
413 for (auto const& i: metadata) {
414 _enable_metadata[i.first]->SetValue(true);
415 checked_set (_metadata[i.first], i.second);
418 for (auto const& i: _enable_metadata) {
419 if (metadata.find(i.first) == metadata.end()) {
420 checked_set (i.second, false);
424 for (auto const& i: _metadata) {
425 if (metadata.find(i.first) == metadata.end()) {
426 checked_set (i.second, wxT(""));
430 checked_set (_kdm_duration, config->default_kdm_duration().duration);
431 switch (config->default_kdm_duration().unit) {
432 case RoughDuration::Unit::DAYS:
433 _kdm_duration->SetRange(1, 365);
434 checked_set (_kdm_duration_unit, 0);
436 case RoughDuration::Unit::WEEKS:
437 _kdm_duration->SetRange(1, 52);
438 checked_set (_kdm_duration_unit, 1);
440 case RoughDuration::Unit::MONTHS:
441 _kdm_duration->SetRange(1, 12);
442 checked_set (_kdm_duration_unit, 2);
444 case RoughDuration::Unit::YEARS:
445 _kdm_duration->SetRange(1, 40);
446 checked_set (_kdm_duration_unit, 3);
450 setup_sensitivity ();
453 void kdm_duration_changed ()
455 auto config = Config::instance();
456 auto duration = _kdm_duration->GetValue();
457 RoughDuration::Unit unit = RoughDuration::Unit::DAYS;
458 switch (_kdm_duration_unit->GetSelection()) {
460 unit = RoughDuration::Unit::DAYS;
463 unit = RoughDuration::Unit::WEEKS;
466 unit = RoughDuration::Unit::MONTHS;
469 unit = RoughDuration::Unit::YEARS;
472 config->set_default_kdm_duration (RoughDuration(duration, unit));
475 void audio_delay_changed ()
477 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
480 void directory_changed ()
482 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
485 void kdm_directory_changed ()
487 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
490 void kdm_type_changed ()
492 Config::instance()->set_default_kdm_type(_kdm_type->get());
495 void use_isdcf_name_by_default_changed ()
497 Config::instance()->set_use_isdcf_name_by_default (_use_isdcf_name_by_default->GetValue());
500 void still_length_changed ()
502 Config::instance()->set_default_still_length (_still_length->GetValue ());
505 void metadata_changed ()
507 map<string, string> metadata;
508 for (auto const& i: _enable_metadata) {
509 if (i.second->GetValue()) {
510 metadata[i.first] = wx_to_std(_metadata[i.first]->GetValue());
513 Config::instance()->set_default_metadata (metadata);
514 setup_sensitivity ();
517 void enable_audio_language_toggled()
520 audio_language_changed();
523 void audio_language_changed()
525 if (_enable_audio_language->get()) {
526 Config::instance()->set_default_audio_language(_audio_language->get().get_value_or(dcp::LanguageTag("en-US")));
528 Config::instance()->unset_default_audio_language();
532 void enable_territory_toggled()
538 void territory_changed()
540 if (_enable_territory->get()) {
541 Config::instance()->set_default_territory(_territory->get().get_value_or(dcp::LanguageTag::RegionSubtag("US")));
543 Config::instance()->unset_default_territory();
547 void setup_sensitivity ()
549 _audio_language->enable(_enable_audio_language->get());
550 _territory->enable(_enable_territory->get());
551 for (auto const& i: _enable_metadata) {
552 _metadata[i.first]->Enable(i.second->GetValue());
556 wxSpinCtrl* _audio_delay;
557 wxSpinCtrl* _still_length;
558 #ifdef DCPOMATIC_USE_OWN_PICKER
559 DirPickerCtrl* _directory;
560 DirPickerCtrl* _kdm_directory;
562 wxDirPickerCtrl* _directory;
563 wxDirPickerCtrl* _kdm_directory;
565 KDMChoice* _kdm_type;
566 wxSpinCtrl* _kdm_duration;
567 wxChoice* _kdm_duration_unit;
568 CheckBox* _use_isdcf_name_by_default;
569 CheckBox* _enable_audio_language;
570 LanguageTagWidget* _audio_language;
571 CheckBox* _enable_territory;
572 RegionSubtagWidget* _territory;
573 map<string, CheckBox*> _enable_metadata;
574 map<string, wxTextCtrl*> _metadata;
578 class EncodingServersPage : public Page
581 EncodingServersPage (wxSize panel_size, int border)
582 : Page (panel_size, border)
585 wxString GetName () const override
591 wxBitmap GetLargeIcon () const override
593 return wxBitmap(icon_path("servers"), wxBITMAP_TYPE_PNG);
598 void setup () override
600 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
601 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
603 vector<EditableListColumn> columns;
604 columns.push_back (EditableListColumn(_("IP address / host name")));
605 _servers_list = new EditableList<string, ServerDialog> (
608 boost::bind (&Config::servers, Config::instance()),
609 boost::bind (&Config::set_servers, Config::instance(), _1),
610 boost::bind (&EncodingServersPage::server_column, this, _1),
611 EditableListTitle::INVISIBLE,
612 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
615 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
617 _use_any_servers->bind(&EncodingServersPage::use_any_servers_changed, this);
620 void config_changed () override
622 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
623 _servers_list->refresh ();
626 void use_any_servers_changed ()
628 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
631 string server_column (string s)
636 CheckBox* _use_any_servers;
637 EditableList<string, ServerDialog>* _servers_list;
641 class TMSPage : public Page
644 TMSPage (wxSize panel_size, int border)
645 : Page (panel_size, border)
648 wxString GetName () const override
654 wxBitmap GetLargeIcon () const override
656 return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
661 void setup () override
663 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
664 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
666 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
667 table->AddGrowableCol (1, 1);
668 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
670 add_label_to_sizer (table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
671 _tms_protocol = new wxChoice (_panel, wxID_ANY);
672 table->Add (_tms_protocol, 1, wxEXPAND);
674 _tms_passive = new CheckBox(_panel, _("Passive mode"));
675 table->Add(_tms_passive, 1, wxEXPAND);
678 add_label_to_sizer (table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
679 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
680 table->Add (_tms_ip, 1, wxEXPAND);
682 add_label_to_sizer (table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
683 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
684 table->Add (_tms_path, 1, wxEXPAND);
686 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
687 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
688 table->Add (_tms_user, 1, wxEXPAND);
690 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
691 _tms_password = new PasswordEntry (_panel);
692 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
694 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
695 _tms_protocol->Append (_("FTP (for Dolby)"));
697 _upload->bind(&TMSPage::upload_changed, this);
698 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
699 _tms_passive->bind(&TMSPage::tms_passive_changed, this);
701 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
702 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
703 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
704 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
707 void config_changed () override
709 auto config = Config::instance ();
711 checked_set (_upload, config->upload_after_make_dcp());
712 checked_set (_tms_protocol, static_cast<int>(config->tms_protocol()));
713 checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
714 checked_set (_tms_ip, config->tms_ip ());
715 checked_set (_tms_path, config->tms_path ());
716 checked_set (_tms_user, config->tms_user ());
717 checked_set (_tms_password, config->tms_password ());
719 _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
722 void upload_changed ()
724 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
727 void tms_protocol_changed ()
729 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
732 void tms_passive_changed()
734 Config::instance()->set_tms_passive(_tms_passive->get());
737 void tms_ip_changed ()
739 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
742 void tms_path_changed ()
744 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
747 void tms_user_changed ()
749 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
752 void tms_password_changed ()
754 Config::instance()->set_tms_password (_tms_password->get());
758 CheckBox* _tms_passive;
759 wxChoice* _tms_protocol;
761 wxTextCtrl* _tms_path;
762 wxTextCtrl* _tms_user;
763 PasswordEntry* _tms_password;
767 class EmailPage : public Page
770 EmailPage (wxSize panel_size, int border)
771 : Page (panel_size, border)
774 wxString GetName () const override
780 wxBitmap GetLargeIcon () const override
782 return wxBitmap(icon_path("email"), wxBITMAP_TYPE_PNG);
787 void setup () override
789 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
790 table->AddGrowableCol (1, 1);
791 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
793 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
795 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
796 _server = new wxTextCtrl (_panel, wxID_ANY);
797 s->Add (_server, 1, wxEXPAND | wxALL);
798 add_label_to_sizer (s, _panel, _("port"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
799 _port = new wxSpinCtrl (_panel, wxID_ANY);
800 _port->SetRange (0, 65535);
802 add_label_to_sizer (s, _panel, _("protocol"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
803 _protocol = new wxChoice (_panel, wxID_ANY);
804 /* Make sure this matches the switches in config_changed and port_changed below */
805 _protocol->Append (_("Auto"));
806 _protocol->Append (_("Plain"));
807 _protocol->Append (_("STARTTLS"));
808 _protocol->Append (_("SSL"));
809 s->Add (_protocol, 1, wxALIGN_CENTER_VERTICAL);
810 table->Add (s, 1, wxEXPAND | wxALL);
813 add_label_to_sizer (table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
814 _user = new wxTextCtrl (_panel, wxID_ANY);
815 table->Add (_user, 1, wxEXPAND | wxALL);
817 add_label_to_sizer (table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
818 _password = new PasswordEntry (_panel);
819 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
821 table->AddSpacer (0);
822 _send_test_email = new Button (_panel, _("Send test email..."));
823 table->Add (_send_test_email);
825 _server->Bind (wxEVT_TEXT, boost::bind(&EmailPage::server_changed, this));
826 _port->Bind (wxEVT_SPINCTRL, boost::bind(&EmailPage::port_changed, this));
827 _protocol->Bind (wxEVT_CHOICE, boost::bind(&EmailPage::protocol_changed, this));
828 _user->Bind (wxEVT_TEXT, boost::bind(&EmailPage::user_changed, this));
829 _password->Changed.connect (boost::bind(&EmailPage::password_changed, this));
830 _send_test_email->Bind (wxEVT_BUTTON, boost::bind(&EmailPage::send_test_email_clicked, this));
833 void config_changed () override
835 auto config = Config::instance ();
837 checked_set (_server, config->mail_server());
838 checked_set (_port, config->mail_port());
839 switch (config->mail_protocol()) {
840 case EmailProtocol::AUTO:
841 checked_set (_protocol, 0);
843 case EmailProtocol::PLAIN:
844 checked_set (_protocol, 1);
846 case EmailProtocol::STARTTLS:
847 checked_set (_protocol, 2);
849 case EmailProtocol::SSL:
850 checked_set (_protocol, 3);
853 checked_set (_user, config->mail_user());
854 checked_set (_password, config->mail_password());
857 void server_changed ()
859 Config::instance()->set_mail_server(wx_to_std(_server->GetValue()));
864 Config::instance()->set_mail_port(_port->GetValue());
867 void protocol_changed ()
869 switch (_protocol->GetSelection()) {
871 Config::instance()->set_mail_protocol(EmailProtocol::AUTO);
874 Config::instance()->set_mail_protocol(EmailProtocol::PLAIN);
877 Config::instance()->set_mail_protocol(EmailProtocol::STARTTLS);
880 Config::instance()->set_mail_protocol(EmailProtocol::SSL);
887 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
890 void password_changed ()
892 Config::instance()->set_mail_password(_password->get());
895 void send_test_email_clicked ()
897 SendTestEmailDialog dialog(_panel);
898 if (dialog.ShowModal() != 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."));
928 PasswordEntry* _password;
929 Button* _send_test_email;
933 class KDMEmailPage : public Page
937 KDMEmailPage (wxSize panel_size, int border)
939 /* We have to force both width and height of this one */
940 : Page (wxSize (panel_size.GetWidth(), 128), border)
942 : Page (panel_size, border)
946 wxString GetName () const override
948 return _("KDM Email");
952 wxBitmap GetLargeIcon () const override
954 return wxBitmap(icon_path("kdm_email"), wxBITMAP_TYPE_PNG);
959 void setup () override
961 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
962 table->AddGrowableCol (1, 1);
963 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
965 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
966 _subject = new wxTextCtrl (_panel, wxID_ANY);
967 table->Add (_subject, 1, wxEXPAND | wxALL);
969 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
970 _from = new wxTextCtrl (_panel, wxID_ANY);
971 table->Add (_from, 1, wxEXPAND | wxALL);
973 vector<EditableListColumn> columns;
974 columns.push_back (EditableListColumn(_("Address")));
975 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
976 _cc = new EditableList<string, EmailDialog> (
979 bind (&Config::kdm_cc, Config::instance()),
980 bind (&Config::set_kdm_cc, Config::instance(), _1),
984 EditableListTitle::VISIBLE,
985 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
987 table->Add (_cc, 1, wxEXPAND | wxALL);
989 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
990 _bcc = new wxTextCtrl (_panel, wxID_ANY);
991 table->Add (_bcc, 1, wxEXPAND | wxALL);
993 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
994 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
996 _reset_email = new Button (_panel, _("Reset to default subject and text"));
997 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1001 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1002 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1003 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1004 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1005 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
1008 void config_changed () override
1010 auto config = Config::instance ();
1012 checked_set (_subject, config->kdm_subject ());
1013 checked_set (_from, config->kdm_from ());
1014 checked_set (_bcc, config->kdm_bcc ());
1015 checked_set (_email, Config::instance()->kdm_email ());
1018 void kdm_subject_changed ()
1020 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
1023 void kdm_from_changed ()
1025 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
1028 void kdm_bcc_changed ()
1030 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
1033 void kdm_email_changed ()
1035 if (_email->GetValue().IsEmpty ()) {
1036 /* Sometimes we get sent an erroneous notification that the email
1037 is empty; I don't know why.
1041 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
1046 Config::instance()->reset_kdm_email ();
1047 checked_set (_email, Config::instance()->kdm_email ());
1050 wxTextCtrl* _subject;
1052 EditableList<string, EmailDialog>* _cc;
1055 wxButton* _reset_email;
1059 class NotificationsPage : public Page
1062 NotificationsPage (wxSize panel_size, int border)
1063 #ifdef DCPOMATIC_OSX
1064 /* We have to force both width and height of this one */
1065 : Page (wxSize (panel_size.GetWidth(), 128), border)
1067 : Page (panel_size, border)
1071 wxString GetName () const override
1073 return _("Notifications");
1076 #ifdef DCPOMATIC_OSX
1077 wxBitmap GetLargeIcon () const override
1079 return wxBitmap(icon_path("notifications"), wxBITMAP_TYPE_PNG);
1084 void setup () override
1086 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1087 table->AddGrowableCol (1, 1);
1088 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1090 _enable_message_box = new CheckBox (_panel, _("Message box"));
1091 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1092 table->AddSpacer (0);
1094 _enable_email = new CheckBox (_panel, _("Email"));
1095 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1096 table->AddSpacer (0);
1098 add_label_to_sizer (table, _panel, _("Subject"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1099 _subject = new wxTextCtrl (_panel, wxID_ANY);
1100 table->Add (_subject, 1, wxEXPAND | wxALL);
1102 add_label_to_sizer (table, _panel, _("From address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1103 _from = new wxTextCtrl (_panel, wxID_ANY);
1104 table->Add (_from, 1, wxEXPAND | wxALL);
1106 add_label_to_sizer (table, _panel, _("To address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1107 _to = new wxTextCtrl (_panel, wxID_ANY);
1108 table->Add (_to, 1, wxEXPAND | wxALL);
1110 vector<EditableListColumn> columns;
1111 columns.push_back (EditableListColumn(_("Address")));
1112 add_label_to_sizer (table, _panel, _("CC addresses"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1113 _cc = new EditableList<string, EmailDialog> (
1116 bind (&Config::notification_cc, Config::instance()),
1117 bind (&Config::set_notification_cc, Config::instance(), _1),
1118 [] (string s, int) {
1121 EditableListTitle::VISIBLE,
1122 EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
1124 table->Add (_cc, 1, wxEXPAND | wxALL);
1126 add_label_to_sizer (table, _panel, _("BCC address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1127 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1128 table->Add (_bcc, 1, wxEXPAND | wxALL);
1130 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1131 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1133 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1134 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1138 _enable_message_box->bind(&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX);
1139 _enable_email->bind(&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL);
1141 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1142 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1143 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1144 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1145 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1146 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1148 setup_sensitivity ();
1151 void setup_sensitivity ()
1153 bool const s = _enable_email->GetValue();
1154 _subject->Enable(s);
1160 _reset_email->Enable(s);
1163 void config_changed () override
1165 auto config = Config::instance ();
1167 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1168 checked_set (_enable_email, config->notification(Config::EMAIL));
1169 checked_set (_subject, config->notification_subject ());
1170 checked_set (_from, config->notification_from ());
1171 checked_set (_to, config->notification_to ());
1172 checked_set (_bcc, config->notification_bcc ());
1173 checked_set (_email, Config::instance()->notification_email ());
1175 setup_sensitivity ();
1178 void notification_subject_changed ()
1180 Config::instance()->set_notification_subject(wx_to_std(_subject->GetValue()));
1183 void notification_from_changed ()
1185 Config::instance()->set_notification_from(wx_to_std(_from->GetValue()));
1188 void notification_to_changed ()
1190 Config::instance()->set_notification_to(wx_to_std(_to->GetValue()));
1193 void notification_bcc_changed ()
1195 Config::instance()->set_notification_bcc(wx_to_std(_bcc->GetValue()));
1198 void notification_email_changed ()
1200 if (_email->GetValue().IsEmpty()) {
1201 /* Sometimes we get sent an erroneous notification that the email
1202 is empty; I don't know why.
1206 Config::instance()->set_notification_email(wx_to_std(_email->GetValue()));
1211 Config::instance()->reset_notification_email();
1212 checked_set (_email, Config::instance()->notification_email());
1215 void type_changed (CheckBox* b, Config::Notification n)
1217 Config::instance()->set_notification(n, b->GetValue());
1218 setup_sensitivity ();
1221 CheckBox* _enable_message_box;
1222 CheckBox* _enable_email;
1224 wxTextCtrl* _subject;
1227 EditableList<string, EmailDialog>* _cc;
1230 wxButton* _reset_email;
1234 class CoverSheetPage : public Page
1238 CoverSheetPage (wxSize panel_size, int border)
1239 #ifdef DCPOMATIC_OSX
1240 /* We have to force both width and height of this one */
1241 : Page (wxSize (panel_size.GetWidth(), 128), border)
1243 : Page (panel_size, border)
1247 wxString GetName () const override
1249 return _("Cover Sheet");
1252 #ifdef DCPOMATIC_OSX
1253 wxBitmap GetLargeIcon () const override
1255 return wxBitmap(icon_path("cover_sheet"), wxBITMAP_TYPE_PNG);
1260 void setup () override
1262 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1263 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1265 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1266 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1268 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1269 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1272 void config_changed () override
1274 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1277 void cover_sheet_changed ()
1279 if (_cover_sheet->GetValue().IsEmpty ()) {
1280 /* Sometimes we get sent an erroneous notification that the cover sheet
1281 is empty; I don't know why.
1285 Config::instance()->set_cover_sheet(wx_to_std(_cover_sheet->GetValue()));
1288 void reset_cover_sheet ()
1290 Config::instance()->reset_cover_sheet();
1291 checked_set (_cover_sheet, Config::instance()->cover_sheet());
1294 wxTextCtrl* _cover_sheet;
1295 wxButton* _reset_cover_sheet;
1299 class IdentifiersPage : public Page
1302 IdentifiersPage (wxSize panel_size, int border)
1303 : Page (panel_size, border)
1306 wxString GetName () const override
1308 return _("Identifiers");
1311 #ifdef DCPOMATIC_OSX
1312 wxBitmap GetLargeIcon () const override
1314 return wxBitmap(icon_path("identifiers"), wxBITMAP_TYPE_PNG);
1319 void setup () override
1321 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1322 table->AddGrowableCol (1, 1);
1324 add_label_to_sizer (table, _panel, _("Issuer"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1325 _issuer = new wxTextCtrl (_panel, wxID_ANY);
1326 _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."));
1327 table->Add (_issuer, 1, wxALL | wxEXPAND);
1329 add_label_to_sizer (table, _panel, _("Creator"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1330 _creator = new wxTextCtrl (_panel, wxID_ANY);
1331 _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."));
1332 table->Add (_creator, 1, wxALL | wxEXPAND);
1334 add_label_to_sizer (table, _panel, _("Company name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1335 _company_name = new wxTextCtrl (_panel, wxID_ANY);
1336 _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."));
1337 table->Add (_company_name, 1, wxALL | wxEXPAND);
1339 add_label_to_sizer (table, _panel, _("Product name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1340 _product_name = new wxTextCtrl (_panel, wxID_ANY);
1341 _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."));
1342 table->Add (_product_name, 1, wxALL | wxEXPAND);
1344 add_label_to_sizer (table, _panel, _("Product version"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1345 _product_version = new wxTextCtrl (_panel, wxID_ANY);
1346 _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."));
1347 table->Add (_product_version, 1, wxALL | wxEXPAND);
1349 add_label_to_sizer (table, _panel, _("JPEG2000 comment"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1350 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
1351 _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."));
1352 table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
1354 _panel->GetSizer()->Add (table, 0, wxEXPAND | wxALL, _border);
1356 _issuer->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::issuer_changed, this));
1357 _creator->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::creator_changed, this));
1358 _company_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::company_name_changed, this));
1359 _product_name->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_name_changed, this));
1360 _product_version->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::product_version_changed, this));
1361 _j2k_comment->Bind (wxEVT_TEXT, boost::bind(&IdentifiersPage::j2k_comment_changed, this));
1364 void config_changed () override
1366 auto config = Config::instance ();
1367 checked_set (_issuer, config->dcp_issuer ());
1368 checked_set (_creator, config->dcp_creator ());
1369 checked_set (_company_name, config->dcp_company_name ());
1370 checked_set (_product_name, config->dcp_product_name ());
1371 checked_set (_product_version, config->dcp_product_version ());
1372 checked_set (_j2k_comment, config->dcp_j2k_comment ());
1375 void issuer_changed ()
1377 Config::instance()->set_dcp_issuer(wx_to_std(_issuer->GetValue()));
1380 void creator_changed ()
1382 Config::instance()->set_dcp_creator(wx_to_std(_creator->GetValue()));
1385 void company_name_changed ()
1387 Config::instance()->set_dcp_company_name(wx_to_std(_company_name->GetValue()));
1390 void product_name_changed ()
1392 Config::instance()->set_dcp_product_name(wx_to_std(_product_name->GetValue()));
1395 void product_version_changed ()
1397 Config::instance()->set_dcp_product_version(wx_to_std(_product_version->GetValue()));
1400 void j2k_comment_changed ()
1402 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
1405 wxTextCtrl* _issuer;
1406 wxTextCtrl* _creator;
1407 wxTextCtrl* _company_name;
1408 wxTextCtrl* _product_name;
1409 wxTextCtrl* _product_version;
1410 wxTextCtrl* _j2k_comment;
1414 class NonStandardPage : public Page
1417 NonStandardPage(wxSize panel_size, int border)
1418 : Page(panel_size, border)
1421 wxString GetName() const override
1423 return _("Non-standard");
1426 #ifdef DCPOMATIC_OSX
1427 wxBitmap GetLargeIcon() const override
1429 return wxBitmap(icon_path("non_standard"), wxBITMAP_TYPE_PNG);
1434 void setup() override
1436 auto table = new wxFlexGridSizer(2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1437 table->AddGrowableCol(1, 1);
1438 _panel->GetSizer()->Add(table, 1, wxALL | wxEXPAND, _border);
1441 add_label_to_sizer(table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1442 auto s = new wxBoxSizer(wxHORIZONTAL);
1443 _maximum_j2k_bandwidth = new wxSpinCtrl(_panel);
1444 s->Add(_maximum_j2k_bandwidth, 1);
1445 add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL);
1449 auto checkbox = [this, table](wxString name, CheckBox*& variable) {
1450 variable = new CheckBox(_panel, name);
1451 table->Add(variable, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1452 table->AddSpacer(0);
1455 checkbox(_("Allow any DCP frame rate"), _allow_any_dcp_frame_rate);
1457 _allow_any_container = new CheckBox(_panel, _("Allow full-frame and non-standard container ratios"));
1458 table->Add(_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1459 auto restart = new StaticText(_panel, _("(restart DCP-o-matic to see all ratios)"));
1460 auto font = restart->GetFont();
1461 font.SetStyle(wxFONTSTYLE_ITALIC);
1462 font.SetPointSize(font.GetPointSize() - 1);
1463 restart->SetFont(font);
1464 table->Add(restart, 1, wxALIGN_CENTRE_VERTICAL | wxBOTTOM, DCPOMATIC_CHECKBOX_BOTTOM_PAD);
1465 restart->SetFont(font);
1467 checkbox(_("Allow creation of DCPs with 96kHz audio"), _allow_96khz_audio);
1468 checkbox(_("Allow mapping to all audio channels"), _use_all_audio_channels);
1469 checkbox(_("Allow use of SMPTE Bv2.0"), _allow_smpte_bv20);
1472 add_label_to_sizer(table, _panel, _("ISDCF name part length"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1473 auto s = new wxBoxSizer(wxHORIZONTAL);
1474 _isdcf_name_part_length = new wxSpinCtrl(_panel);
1475 s->Add(_isdcf_name_part_length, 1);
1479 _maximum_j2k_bandwidth->SetRange(1, 1000);
1480 _maximum_j2k_bandwidth->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_j2k_bandwidth_changed, this));
1481 _allow_any_dcp_frame_rate->bind(&NonStandardPage::allow_any_dcp_frame_rate_changed, this);
1482 _allow_any_container->bind(&NonStandardPage::allow_any_container_changed, this);
1483 _allow_96khz_audio->bind(&NonStandardPage::allow_96khz_audio_changed, this);
1484 _use_all_audio_channels->bind(&NonStandardPage::use_all_channels_changed, this);
1485 _allow_smpte_bv20->bind(&NonStandardPage::allow_smpte_bv20_changed, this);
1486 _isdcf_name_part_length->SetRange(14, 256);
1487 _isdcf_name_part_length->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::isdcf_name_part_length_changed, this));
1490 void config_changed() override
1492 auto config = Config::instance();
1494 checked_set(_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1495 checked_set(_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate());
1496 checked_set(_allow_any_container, config->allow_any_container());
1497 checked_set(_allow_96khz_audio, config->allow_96khz_audio());
1498 checked_set(_use_all_audio_channels, config->use_all_audio_channels());
1499 checked_set(_allow_smpte_bv20, config->allow_smpte_bv20());
1500 checked_set(_isdcf_name_part_length, config->isdcf_name_part_length());
1503 void maximum_j2k_bandwidth_changed()
1505 Config::instance()->set_maximum_j2k_bandwidth(_maximum_j2k_bandwidth->GetValue() * 1000000);
1508 void allow_any_dcp_frame_rate_changed()
1510 Config::instance()->set_allow_any_dcp_frame_rate(_allow_any_dcp_frame_rate->GetValue());
1513 void allow_any_container_changed()
1515 Config::instance()->set_allow_any_container(_allow_any_container->GetValue());
1518 void allow_96khz_audio_changed()
1520 Config::instance()->set_allow_96hhz_audio(_allow_96khz_audio->GetValue());
1523 void use_all_channels_changed()
1525 Config::instance()->set_use_all_audio_channels(_use_all_audio_channels->GetValue());
1528 void allow_smpte_bv20_changed()
1530 Config::instance()->set_allow_smpte_bv20(_allow_smpte_bv20->GetValue());
1533 void isdcf_name_part_length_changed()
1535 Config::instance()->set_isdcf_name_part_length(_isdcf_name_part_length->GetValue());
1538 wxSpinCtrl* _maximum_j2k_bandwidth = nullptr;
1539 CheckBox* _allow_any_dcp_frame_rate = nullptr;
1540 CheckBox* _allow_any_container = nullptr;
1541 CheckBox* _allow_96khz_audio = nullptr;
1542 CheckBox* _use_all_audio_channels = nullptr;
1543 CheckBox* _allow_smpte_bv20 = nullptr;
1544 wxSpinCtrl* _isdcf_name_part_length = nullptr;
1549 /** @class AdvancedPage
1550 * @brief Advanced page of the preferences dialog.
1552 class AdvancedPage : public Page
1555 AdvancedPage (wxSize panel_size, int border)
1556 : Page (panel_size, border)
1559 wxString GetName () const override
1561 return _("Advanced");
1564 #ifdef DCPOMATIC_OSX
1565 wxBitmap GetLargeIcon () const override
1567 return wxBitmap(icon_path("advanced"), wxBITMAP_TYPE_PNG);
1572 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1574 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1576 flags |= wxALIGN_RIGHT;
1579 wxStaticText* m = new StaticText (parent, text);
1580 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1583 void setup () override
1585 auto table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1586 table->AddGrowableCol (1, 1);
1587 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1589 add_label_to_sizer (table, _panel, _("Video display mode"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1590 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1591 table->Add (_video_display_mode);
1593 auto restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1594 auto font = restart->GetFont();
1595 font.SetStyle (wxFONTSTYLE_ITALIC);
1596 font.SetPointSize (font.GetPointSize() - 1);
1597 restart->SetFont (font);
1598 table->AddSpacer (0);
1600 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1601 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1602 table->AddSpacer (0);
1604 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1605 table->Add (_only_servers_encode, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
1606 table->AddSpacer (0);
1609 add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
1610 auto s = new wxBoxSizer (wxHORIZONTAL);
1611 _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1612 s->Add (_frames_in_memory_multiplier, 1);
1617 auto format = create_label (_panel, _("DCP metadata filename format"), true);
1618 #ifdef DCPOMATIC_OSX
1619 auto align = new wxBoxSizer (wxHORIZONTAL);
1620 align->Add (format, 0, wxTOP, 2);
1621 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1623 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1625 dcp::NameFormat::Map titles;
1626 titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1627 dcp::NameFormat::Map examples;
1628 examples['t'] = "cpl";
1629 _dcp_metadata_filename_format = new NameFormatEditor (
1630 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1632 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1636 auto format = create_label (_panel, _("DCP asset filename format"), true);
1637 #ifdef DCPOMATIC_OSX
1638 auto align = new wxBoxSizer (wxHORIZONTAL);
1639 align->Add (format, 0, wxTOP, 2);
1640 table->Add (align, 0, wxALIGN_RIGHT | wxRIGHT, DCPOMATIC_SIZER_GAP - 2);
1642 table->Add (format, 0, wxTOP | wxLEFT | wxRIGHT | wxALIGN_TOP, DCPOMATIC_SIZER_GAP);
1644 dcp::NameFormat::Map titles;
1645 titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1646 titles['r'] = wx_to_std (_("reel number"));
1647 titles['n'] = wx_to_std (_("number of reels"));
1648 titles['c'] = wx_to_std (_("content filename"));
1649 dcp::NameFormat::Map examples;
1650 examples['t'] = "j2c";
1651 examples['r'] = "1";
1652 examples['n'] = "4";
1653 examples['c'] = "myfile.mp4";
1654 _dcp_asset_filename_format = new NameFormatEditor (
1655 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1657 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1661 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1662 auto t = new wxFlexGridSizer (2);
1663 _log_general = new CheckBox (_panel, _("General"));
1664 t->Add (_log_general, 1, wxEXPAND | wxALL);
1665 _log_warning = new CheckBox (_panel, _("Warnings"));
1666 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1667 _log_error = new CheckBox (_panel, _("Errors"));
1668 t->Add (_log_error, 1, wxEXPAND | wxALL);
1669 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1670 _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1671 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1672 _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1673 t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1674 _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1675 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1676 _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1677 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1678 _log_debug_video_view = new CheckBox (_panel, _("Debug: video view"));
1679 t->Add (_log_debug_video_view, 1, wxEXPAND | wxALL);
1680 _log_debug_player = new CheckBox (_panel, _("Debug: player"));
1681 t->Add (_log_debug_player, 1, wxEXPAND | wxALL);
1682 _log_debug_audio_analysis = new CheckBox (_panel, _("Debug: audio analysis"));
1683 t->Add (_log_debug_audio_analysis, 1, wxEXPAND | wxALL);
1684 table->Add (t, 0, wxALL, 6);
1687 #ifdef DCPOMATIC_WINDOWS
1688 _win32_console = new CheckBox (_panel, _("Open console window"));
1689 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1690 table->AddSpacer (0);
1693 _video_display_mode->Append (_("Simple (safer)"));
1694 #if wxCHECK_VERSION(3, 1, 0)
1695 _video_display_mode->Append (_("OpenGL (faster)"));
1697 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1698 _show_experimental_audio_processors->bind(&AdvancedPage::show_experimental_audio_processors_changed, this);
1699 _only_servers_encode->bind(&AdvancedPage::only_servers_encode_changed, this);
1700 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1701 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1702 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1703 _log_general->bind(&AdvancedPage::log_changed, this);
1704 _log_warning->bind(&AdvancedPage::log_changed, this);
1705 _log_error->bind(&AdvancedPage::log_changed, this);
1706 _log_timing->bind(&AdvancedPage::log_changed, this);
1707 _log_debug_threed->bind(&AdvancedPage::log_changed, this);
1708 _log_debug_encode->bind(&AdvancedPage::log_changed, this);
1709 _log_debug_email->bind(&AdvancedPage::log_changed, this);
1710 _log_debug_video_view->bind(&AdvancedPage::log_changed, this);
1711 _log_debug_player->bind(&AdvancedPage::log_changed, this);
1712 _log_debug_audio_analysis->bind(&AdvancedPage::log_changed, this);
1713 #ifdef DCPOMATIC_WINDOWS
1714 _win32_console->bind(&AdvancedPage::win32_console_changed, this);
1718 void config_changed () override
1720 auto config = Config::instance ();
1722 switch (config->video_view_type()) {
1723 case Config::VIDEO_VIEW_SIMPLE:
1724 checked_set (_video_display_mode, 0);
1726 case Config::VIDEO_VIEW_OPENGL:
1727 checked_set (_video_display_mode, 1);
1730 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1731 checked_set (_only_servers_encode, config->only_servers_encode ());
1732 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1733 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1734 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1735 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1736 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREE_D);
1737 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1738 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1739 checked_set (_log_debug_video_view, config->log_types() & LogEntry::TYPE_DEBUG_VIDEO_VIEW);
1740 checked_set (_log_debug_player, config->log_types() & LogEntry::TYPE_DEBUG_PLAYER);
1741 checked_set (_log_debug_audio_analysis, config->log_types() & LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS);
1742 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1743 #ifdef DCPOMATIC_WINDOWS
1744 checked_set (_win32_console, config->win32_console());
1748 void video_display_mode_changed ()
1750 if (_video_display_mode->GetSelection() == 0) {
1751 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_SIMPLE);
1753 Config::instance()->set_video_view_type(Config::VIDEO_VIEW_OPENGL);
1757 void frames_in_memory_multiplier_changed ()
1759 Config::instance()->set_frames_in_memory_multiplier(_frames_in_memory_multiplier->GetValue());
1762 void show_experimental_audio_processors_changed ()
1764 Config::instance()->set_show_experimental_audio_processors(_show_experimental_audio_processors->GetValue());
1767 void only_servers_encode_changed ()
1769 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue());
1772 void dcp_metadata_filename_format_changed ()
1774 Config::instance()->set_dcp_metadata_filename_format(_dcp_metadata_filename_format->get());
1777 void dcp_asset_filename_format_changed ()
1779 Config::instance()->set_dcp_asset_filename_format(_dcp_asset_filename_format->get());
1785 if (_log_general->GetValue ()) {
1786 types |= LogEntry::TYPE_GENERAL;
1788 if (_log_warning->GetValue ()) {
1789 types |= LogEntry::TYPE_WARNING;
1791 if (_log_error->GetValue ()) {
1792 types |= LogEntry::TYPE_ERROR;
1794 if (_log_timing->GetValue ()) {
1795 types |= LogEntry::TYPE_TIMING;
1797 if (_log_debug_threed->GetValue ()) {
1798 types |= LogEntry::TYPE_DEBUG_THREE_D;
1800 if (_log_debug_encode->GetValue ()) {
1801 types |= LogEntry::TYPE_DEBUG_ENCODE;
1803 if (_log_debug_email->GetValue ()) {
1804 types |= LogEntry::TYPE_DEBUG_EMAIL;
1806 if (_log_debug_video_view->GetValue()) {
1807 types |= LogEntry::TYPE_DEBUG_VIDEO_VIEW;
1809 if (_log_debug_player->GetValue()) {
1810 types |= LogEntry::TYPE_DEBUG_PLAYER;
1812 if (_log_debug_audio_analysis->GetValue()) {
1813 types |= LogEntry::TYPE_DEBUG_AUDIO_ANALYSIS;
1815 Config::instance()->set_log_types (types);
1818 #ifdef DCPOMATIC_WINDOWS
1819 void win32_console_changed ()
1821 Config::instance()->set_win32_console(_win32_console->GetValue());
1825 wxChoice* _video_display_mode = nullptr;
1826 wxSpinCtrl* _frames_in_memory_multiplier = nullptr;
1827 CheckBox* _show_experimental_audio_processors = nullptr;
1828 CheckBox* _only_servers_encode = nullptr;
1829 NameFormatEditor* _dcp_metadata_filename_format = nullptr;
1830 NameFormatEditor* _dcp_asset_filename_format = nullptr;
1831 CheckBox* _log_general = nullptr;
1832 CheckBox* _log_warning = nullptr;
1833 CheckBox* _log_error = nullptr;
1834 CheckBox* _log_timing = nullptr;
1835 CheckBox* _log_debug_threed = nullptr;
1836 CheckBox* _log_debug_encode = nullptr;
1837 CheckBox* _log_debug_email = nullptr;
1838 CheckBox* _log_debug_video_view = nullptr;
1839 CheckBox* _log_debug_player = nullptr;
1840 CheckBox* _log_debug_audio_analysis = nullptr;
1841 #ifdef DCPOMATIC_WINDOWS
1842 CheckBox* _win32_console = nullptr;
1847 wxPreferencesEditor*
1848 create_full_config_dialog ()
1850 auto e = new wxPreferencesEditor ();
1852 #ifdef DCPOMATIC_OSX
1853 /* Width that we force some of the config panels to be on OSX so that
1854 the containing window doesn't shrink too much when we select those panels.
1855 This is obviously an unpleasant hack.
1857 wxSize ps = wxSize (750, -1);
1858 int const border = 16;
1860 wxSize ps = wxSize (-1, -1);
1861 int const border = 8;
1864 e->AddPage (new FullGeneralPage (ps, border));
1865 e->AddPage (new SoundPage (ps, border));
1866 e->AddPage (new DefaultsPage (ps, border));
1867 e->AddPage (new EncodingServersPage(ps, border));
1868 e->AddPage (new KeysPage (ps, border));
1869 e->AddPage (new TMSPage (ps, border));
1870 e->AddPage (new EmailPage (ps, border));
1871 e->AddPage (new KDMEmailPage (ps, border));
1872 e->AddPage (new NotificationsPage (ps, border));
1873 e->AddPage (new CoverSheetPage (ps, border));
1874 e->AddPage (new IdentifiersPage (ps, border));
1875 e->AddPage (new NonStandardPage (ps, border));
1876 e->AddPage (new AdvancedPage (ps, border));