2 Copyright (C) 2012-2016 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/>.
21 /** @file src/config_dialog.cc
22 * @brief A dialogue to edit DCP-o-matic configuration.
25 #include "config_dialog.h"
27 #include "editable_list.h"
28 #include "filter_dialog.h"
29 #include "dir_picker_ctrl.h"
30 #include "file_picker_ctrl.h"
31 #include "isdcf_metadata_dialog.h"
32 #include "server_dialog.h"
33 #include "make_chain_dialog.h"
34 #include "email_dialog.h"
35 #include "name_format_editor.h"
36 #include "lib/config.h"
37 #include "lib/ratio.h"
38 #include "lib/filter.h"
39 #include "lib/dcp_content_type.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include <dcp/locale_convert.h>
45 #include <dcp/exceptions.h>
46 #include <dcp/certificate_chain.h>
47 #include <wx/stdpaths.h>
48 #include <wx/preferences.h>
49 #include <wx/spinctrl.h>
50 #include <wx/filepicker.h>
51 #include <boost/filesystem.hpp>
52 #include <boost/foreach.hpp>
63 using boost::shared_ptr;
64 using boost::function;
65 using boost::optional;
66 using dcp::locale_convert;
71 Page (wxSize panel_size, int border)
74 , _panel_size (panel_size)
75 , _window_exists (false)
77 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
83 wxWindow* create_window (wxWindow* parent)
85 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
86 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
90 _window_exists = true;
93 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
102 virtual void config_changed () = 0;
103 virtual void setup () = 0;
105 void config_changed_wrapper ()
107 if (_window_exists) {
112 void window_destroyed ()
114 _window_exists = false;
118 boost::signals2::scoped_connection _config_connection;
122 class StockPage : public wxStockPreferencesPage, public Page
125 StockPage (Kind kind, wxSize panel_size, int border)
126 : wxStockPreferencesPage (kind)
127 , Page (panel_size, border)
130 wxWindow* CreateWindow (wxWindow* parent)
132 return create_window (parent);
136 class StandardPage : public wxPreferencesPage, public Page
139 StandardPage (wxSize panel_size, int border)
140 : Page (panel_size, border)
143 wxWindow* CreateWindow (wxWindow* parent)
145 return create_window (parent);
149 class GeneralPage : public StockPage
152 GeneralPage (wxSize panel_size, int border)
153 : StockPage (Kind_General, panel_size, border)
159 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
160 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
163 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
164 table->Add (_set_language, wxGBPosition (r, 0));
165 _language = new wxChoice (_panel, wxID_ANY);
166 vector<pair<string, string> > languages;
167 languages.push_back (make_pair ("Čeština", "cs_CZ"));
168 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
169 languages.push_back (make_pair ("Dansk", "da_DK"));
170 languages.push_back (make_pair ("Deutsch", "de_DE"));
171 languages.push_back (make_pair ("English", "en_GB"));
172 languages.push_back (make_pair ("Español", "es_ES"));
173 languages.push_back (make_pair ("Français", "fr_FR"));
174 languages.push_back (make_pair ("Italiano", "it_IT"));
175 languages.push_back (make_pair ("Nederlands", "nl_NL"));
176 languages.push_back (make_pair ("Русский", "ru_RU"));
177 languages.push_back (make_pair ("Polski", "pl_PL"));
178 languages.push_back (make_pair ("Português europeu", "pt_PT"));
179 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
180 languages.push_back (make_pair ("Svenska", "sv_SE"));
181 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
182 languages.push_back (make_pair ("українська мова", "uk_UA"));
183 checked_set (_language, languages);
184 table->Add (_language, wxGBPosition (r, 1));
187 wxStaticText* restart = add_label_to_sizer (
188 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
190 wxFont font = restart->GetFont();
191 font.SetStyle (wxFONTSTYLE_ITALIC);
192 font.SetPointSize (font.GetPointSize() - 1);
193 restart->SetFont (font);
196 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
197 _num_local_encoding_threads = new wxSpinCtrl (_panel);
198 table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
201 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
202 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml");
203 table->Add (_cinemas_file, wxGBPosition (r, 1));
206 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
207 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
208 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
212 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
213 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
216 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
217 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
220 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
221 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
224 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
225 bottom_table->AddGrowableCol (1, 1);
227 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
228 _issuer = new wxTextCtrl (_panel, wxID_ANY);
229 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
231 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
232 _creator = new wxTextCtrl (_panel, wxID_ANY);
233 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
235 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
238 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
239 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
240 _cinemas_file->Bind (wxEVT_COMMAND_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
242 _num_local_encoding_threads->SetRange (1, 128);
243 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
245 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
246 _analyse_ebur128->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
248 _automatic_audio_analysis->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
249 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
250 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
252 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::issuer_changed, this));
253 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::creator_changed, this));
256 void config_changed ()
258 Config* config = Config::instance ();
260 checked_set (_set_language, static_cast<bool>(config->language()));
262 /* Backwards compatibility of config file */
264 map<string, string> compat_map;
265 compat_map["fr"] = "fr_FR";
266 compat_map["it"] = "it_IT";
267 compat_map["es"] = "es_ES";
268 compat_map["sv"] = "sv_SE";
269 compat_map["de"] = "de_DE";
270 compat_map["nl"] = "nl_NL";
271 compat_map["ru"] = "ru_RU";
272 compat_map["pl"] = "pl_PL";
273 compat_map["da"] = "da_DK";
274 compat_map["pt"] = "pt_PT";
275 compat_map["sk"] = "sk_SK";
276 compat_map["cs"] = "cs_CZ";
277 compat_map["uk"] = "uk_UA";
279 string lang = config->language().get_value_or ("en_GB");
280 if (compat_map.find (lang) != compat_map.end ()) {
281 lang = compat_map[lang];
284 checked_set (_language, lang);
286 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
287 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
288 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
290 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
291 checked_set (_check_for_updates, config->check_for_updates ());
292 checked_set (_check_for_test_updates, config->check_for_test_updates ());
293 checked_set (_issuer, config->dcp_issuer ());
294 checked_set (_creator, config->dcp_creator ());
295 checked_set (_cinemas_file, config->cinemas_file());
297 setup_sensitivity ();
300 void setup_sensitivity ()
302 _language->Enable (_set_language->GetValue ());
303 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
306 void set_language_changed ()
308 setup_sensitivity ();
309 if (_set_language->GetValue ()) {
312 Config::instance()->unset_language ();
316 void language_changed ()
318 int const sel = _language->GetSelection ();
320 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
322 Config::instance()->unset_language ();
326 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
327 void analyse_ebur128_changed ()
329 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
333 void automatic_audio_analysis_changed ()
335 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
338 void check_for_updates_changed ()
340 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
343 void check_for_test_updates_changed ()
345 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
348 void num_local_encoding_threads_changed ()
350 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
353 void issuer_changed ()
355 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
358 void creator_changed ()
360 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
363 void cinemas_file_changed ()
365 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
368 wxCheckBox* _set_language;
370 wxSpinCtrl* _num_local_encoding_threads;
371 FilePickerCtrl* _cinemas_file;
372 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
373 wxCheckBox* _analyse_ebur128;
375 wxCheckBox* _automatic_audio_analysis;
376 wxCheckBox* _check_for_updates;
377 wxCheckBox* _check_for_test_updates;
379 wxTextCtrl* _creator;
382 class DefaultsPage : public StandardPage
385 DefaultsPage (wxSize panel_size, int border)
386 : StandardPage (panel_size, border)
389 wxString GetName () const
391 return _("Defaults");
395 wxBitmap GetLargeIcon () const
397 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
404 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
405 table->AddGrowableCol (1, 1);
406 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
409 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
410 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
411 _still_length = new wxSpinCtrl (_panel);
412 s->Add (_still_length);
413 add_label_to_sizer (s, _panel, _("s"), false);
417 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
418 #ifdef DCPOMATIC_USE_OWN_PICKER
419 _directory = new DirPickerCtrl (_panel);
421 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
423 table->Add (_directory, 1, wxEXPAND);
425 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
426 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
427 table->Add (_isdcf_metadata_button);
429 add_label_to_sizer (table, _panel, _("Default container"), true);
430 _container = new wxChoice (_panel, wxID_ANY);
431 table->Add (_container);
433 add_label_to_sizer (table, _panel, _("Default content type"), true);
434 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
435 table->Add (_dcp_content_type);
437 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
438 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
439 table->Add (_dcp_audio_channels);
442 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
443 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
444 _j2k_bandwidth = new wxSpinCtrl (_panel);
445 s->Add (_j2k_bandwidth);
446 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
451 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
452 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
453 _audio_delay = new wxSpinCtrl (_panel);
454 s->Add (_audio_delay);
455 add_label_to_sizer (s, _panel, _("ms"), false);
459 add_label_to_sizer (table, _panel, _("Default standard"), true);
460 _standard = new wxChoice (_panel, wxID_ANY);
461 table->Add (_standard);
463 _still_length->SetRange (1, 3600);
464 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
466 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
468 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
470 vector<Ratio const *> ratios = Ratio::all ();
471 for (size_t i = 0; i < ratios.size(); ++i) {
472 _container->Append (std_to_wx (ratios[i]->nickname ()));
475 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
477 vector<DCPContentType const *> const ct = DCPContentType::all ();
478 for (size_t i = 0; i < ct.size(); ++i) {
479 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
482 setup_audio_channels_choice (_dcp_audio_channels, 2);
484 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
485 _dcp_audio_channels->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
487 _j2k_bandwidth->SetRange (50, 250);
488 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
490 _audio_delay->SetRange (-1000, 1000);
491 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
493 _standard->Append (_("SMPTE"));
494 _standard->Append (_("Interop"));
495 _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::standard_changed, this));
498 void config_changed ()
500 Config* config = Config::instance ();
502 vector<Ratio const *> ratios = Ratio::all ();
503 for (size_t i = 0; i < ratios.size(); ++i) {
504 if (ratios[i] == config->default_container ()) {
505 _container->SetSelection (i);
509 vector<DCPContentType const *> const ct = DCPContentType::all ();
510 for (size_t i = 0; i < ct.size(); ++i) {
511 if (ct[i] == config->default_dcp_content_type ()) {
512 _dcp_content_type->SetSelection (i);
516 checked_set (_still_length, config->default_still_length ());
517 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
518 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
519 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
520 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
521 checked_set (_audio_delay, config->default_audio_delay ());
522 checked_set (_standard, config->default_interop() ? 1 : 0);
525 void j2k_bandwidth_changed ()
527 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
530 void audio_delay_changed ()
532 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
535 void dcp_audio_channels_changed ()
537 int const s = _dcp_audio_channels->GetSelection ();
538 if (s != wxNOT_FOUND) {
539 Config::instance()->set_default_dcp_audio_channels (
540 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
545 void directory_changed ()
547 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
550 void edit_isdcf_metadata_clicked ()
552 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
554 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
558 void still_length_changed ()
560 Config::instance()->set_default_still_length (_still_length->GetValue ());
563 void container_changed ()
565 vector<Ratio const *> ratio = Ratio::all ();
566 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
569 void dcp_content_type_changed ()
571 vector<DCPContentType const *> ct = DCPContentType::all ();
572 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
575 void standard_changed ()
577 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
580 wxSpinCtrl* _j2k_bandwidth;
581 wxSpinCtrl* _audio_delay;
582 wxButton* _isdcf_metadata_button;
583 wxSpinCtrl* _still_length;
584 #ifdef DCPOMATIC_USE_OWN_PICKER
585 DirPickerCtrl* _directory;
587 wxDirPickerCtrl* _directory;
589 wxChoice* _container;
590 wxChoice* _dcp_content_type;
591 wxChoice* _dcp_audio_channels;
595 class EncodingServersPage : public StandardPage
598 EncodingServersPage (wxSize panel_size, int border)
599 : StandardPage (panel_size, border)
602 wxString GetName () const
608 wxBitmap GetLargeIcon () const
610 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
617 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
618 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
620 vector<string> columns;
621 columns.push_back (wx_to_std (_("IP address / host name")));
622 _servers_list = new EditableList<string, ServerDialog> (
625 boost::bind (&Config::servers, Config::instance()),
626 boost::bind (&Config::set_servers, Config::instance(), _1),
627 boost::bind (&always_valid),
628 boost::bind (&EncodingServersPage::server_column, this, _1)
631 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
633 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
636 void config_changed ()
638 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
639 _servers_list->refresh ();
642 void use_any_servers_changed ()
644 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
647 string server_column (string s)
652 wxCheckBox* _use_any_servers;
653 EditableList<string, ServerDialog>* _servers_list;
656 class CertificateChainEditor : public wxPanel
659 CertificateChainEditor (
663 function<void (shared_ptr<dcp::CertificateChain>)> set,
664 function<shared_ptr<const dcp::CertificateChain> (void)> get
670 wxFont subheading_font (*wxNORMAL_FONT);
671 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
673 _sizer = new wxBoxSizer (wxVERTICAL);
676 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
677 m->SetFont (subheading_font);
678 _sizer->Add (m, 0, wxALL, border);
681 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
682 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
684 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
689 ip.SetText (_("Type"));
691 _certificates->InsertColumn (0, ip);
697 ip.SetText (_("Thumbprint"));
700 wxFont font = ip.GetFont ();
701 font.SetFamily (wxFONTFAMILY_TELETYPE);
704 _certificates->InsertColumn (1, ip);
707 certificates_sizer->Add (_certificates, 1, wxEXPAND);
710 wxSizer* s = new wxBoxSizer (wxVERTICAL);
711 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
712 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
713 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
714 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
715 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
716 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
717 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
720 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
721 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
724 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
725 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
726 wxFont font = _private_key->GetFont ();
727 font.SetFamily (wxFONTFAMILY_TELETYPE);
728 _private_key->SetFont (font);
729 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
730 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
731 table->Add (_load_private_key, wxGBPosition (r, 2));
732 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
733 table->Add (_export_private_key, wxGBPosition (r, 3));
736 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
737 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
738 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
739 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
742 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::add_certificate, this));
743 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remove_certificate, this));
744 _export_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::export_certificate, this));
745 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
746 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
747 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remake_certificates, this));
748 _load_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::load_private_key, this));
749 _export_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::export_private_key, this));
751 SetSizerAndFit (_sizer);
754 void config_changed ()
756 _chain.reset (new dcp::CertificateChain (*_get().get ()));
758 update_certificate_list ();
759 update_private_key ();
760 update_sensitivity ();
763 void add_button (wxWindow* button)
765 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
770 void add_certificate ()
772 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
774 if (d->ShowModal() == wxID_OK) {
777 string const extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
778 if (!extra.empty ()) {
781 _("This file contains other certificates (or other data) after its first certificate. "
782 "Only the first certificate will be used.")
787 update_certificate_list ();
788 } catch (dcp::MiscError& e) {
789 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
795 update_sensitivity ();
798 void remove_certificate ()
800 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
805 _certificates->DeleteItem (i);
809 update_sensitivity ();
812 void export_certificate ()
814 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
819 wxFileDialog* d = new wxFileDialog (
820 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
821 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
824 dcp::CertificateChain::List all = _chain->root_to_leaf ();
825 dcp::CertificateChain::List::iterator j = all.begin ();
826 for (int k = 0; k < i; ++k) {
830 if (d->ShowModal () == wxID_OK) {
831 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
833 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
836 string const s = j->certificate (true);
837 fwrite (s.c_str(), 1, s.length(), f);
843 void update_certificate_list ()
845 _certificates->DeleteAllItems ();
847 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
848 BOOST_FOREACH (dcp::Certificate const & i, certs) {
851 _certificates->InsertItem (item);
852 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
855 _certificates->SetItem (n, 0, _("Root"));
856 } else if (n == (certs.size() - 1)) {
857 _certificates->SetItem (n, 0, _("Leaf"));
859 _certificates->SetItem (n, 0, _("Intermediate"));
866 void remake_certificates ()
868 shared_ptr<const dcp::CertificateChain> chain = _get ();
870 string subject_organization_name;
871 string subject_organizational_unit_name;
872 string root_common_name;
873 string intermediate_common_name;
874 string leaf_common_name;
876 dcp::CertificateChain::List all = chain->root_to_leaf ();
878 if (all.size() >= 1) {
880 subject_organization_name = chain->root().subject_organization_name ();
881 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
882 root_common_name = chain->root().subject_common_name ();
885 if (all.size() >= 2) {
887 leaf_common_name = chain->leaf().subject_common_name ();
890 if (all.size() >= 3) {
891 /* Have an intermediate */
892 dcp::CertificateChain::List::iterator i = all.begin ();
894 intermediate_common_name = i->subject_common_name ();
897 MakeChainDialog* d = new MakeChainDialog (
899 subject_organization_name,
900 subject_organizational_unit_name,
902 intermediate_common_name,
906 if (d->ShowModal () == wxID_OK) {
908 new dcp::CertificateChain (
911 d->organisational_unit (),
912 d->root_common_name (),
913 d->intermediate_common_name (),
914 d->leaf_common_name ()
919 update_certificate_list ();
920 update_private_key ();
926 void update_sensitivity ()
928 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
929 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
932 void update_private_key ()
934 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
938 void load_private_key ()
940 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
942 if (d->ShowModal() == wxID_OK) {
944 boost::filesystem::path p (wx_to_std (d->GetPath ()));
945 if (boost::filesystem::file_size (p) > 8192) {
948 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
953 _chain->set_key (dcp::file_to_string (p));
955 update_private_key ();
956 } catch (dcp::MiscError& e) {
957 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
963 update_sensitivity ();
966 void export_private_key ()
968 optional<string> key = _chain->key ();
973 wxFileDialog* d = new wxFileDialog (
974 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
975 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
978 if (d->ShowModal () == wxID_OK) {
979 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
981 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
984 string const s = _chain->key().get ();
985 fwrite (s.c_str(), 1, s.length(), f);
991 wxListCtrl* _certificates;
992 wxButton* _add_certificate;
993 wxButton* _export_certificate;
994 wxButton* _remove_certificate;
995 wxButton* _remake_certificates;
996 wxStaticText* _private_key;
997 wxButton* _load_private_key;
998 wxButton* _export_private_key;
1000 wxBoxSizer* _button_sizer;
1001 shared_ptr<dcp::CertificateChain> _chain;
1002 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1003 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1006 class KeysPage : public StandardPage
1009 KeysPage (wxSize panel_size, int border)
1010 : StandardPage (panel_size, border)
1013 wxString GetName () const
1018 #ifdef DCPOMATIC_OSX
1019 wxBitmap GetLargeIcon () const
1021 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1029 _signer = new CertificateChainEditor (
1030 _panel, _("Signing DCPs and KDMs"), _border,
1031 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1032 boost::bind (&Config::signer_chain, Config::instance ())
1035 _panel->GetSizer()->Add (_signer);
1037 _decryption = new CertificateChainEditor (
1038 _panel, _("Decrypting DCPs"), _border,
1039 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1040 boost::bind (&Config::decryption_chain, Config::instance ())
1043 _panel->GetSizer()->Add (_decryption);
1045 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\ncertificate..."));
1046 _decryption->add_button (_export_decryption_certificate);
1047 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\nchain..."));
1048 _decryption->add_button (_export_decryption_chain);
1050 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
1051 _export_decryption_chain->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_chain, this));
1054 void export_decryption_certificate ()
1056 wxFileDialog* d = new wxFileDialog (
1057 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1058 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1061 if (d->ShowModal () == wxID_OK) {
1062 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1064 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1067 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1068 fwrite (s.c_str(), 1, s.length(), f);
1074 void export_decryption_chain ()
1076 wxFileDialog* d = new wxFileDialog (
1077 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1078 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1081 if (d->ShowModal () == wxID_OK) {
1082 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1084 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1087 string const s = Config::instance()->decryption_chain()->chain();
1088 fwrite (s.c_str(), 1, s.length(), f);
1094 void config_changed ()
1096 _signer->config_changed ();
1097 _decryption->config_changed ();
1100 CertificateChainEditor* _signer;
1101 CertificateChainEditor* _decryption;
1102 wxButton* _export_decryption_certificate;
1103 wxButton* _export_decryption_chain;
1106 class TMSPage : public StandardPage
1109 TMSPage (wxSize panel_size, int border)
1110 : StandardPage (panel_size, border)
1113 wxString GetName () const
1118 #ifdef DCPOMATIC_OSX
1119 wxBitmap GetLargeIcon () const
1121 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1128 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1129 table->AddGrowableCol (1, 1);
1130 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1132 add_label_to_sizer (table, _panel, _("Protocol"), true);
1133 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1134 table->Add (_tms_protocol, 1, wxEXPAND);
1136 add_label_to_sizer (table, _panel, _("IP address"), true);
1137 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1138 table->Add (_tms_ip, 1, wxEXPAND);
1140 add_label_to_sizer (table, _panel, _("Target path"), true);
1141 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1142 table->Add (_tms_path, 1, wxEXPAND);
1144 add_label_to_sizer (table, _panel, _("User name"), true);
1145 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1146 table->Add (_tms_user, 1, wxEXPAND);
1148 add_label_to_sizer (table, _panel, _("Password"), true);
1149 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1150 table->Add (_tms_password, 1, wxEXPAND);
1152 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1153 _tms_protocol->Append (_("FTP (for Dolby)"));
1155 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
1156 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
1157 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
1158 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
1159 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
1162 void config_changed ()
1164 Config* config = Config::instance ();
1166 checked_set (_tms_protocol, config->tms_protocol ());
1167 checked_set (_tms_ip, config->tms_ip ());
1168 checked_set (_tms_path, config->tms_path ());
1169 checked_set (_tms_user, config->tms_user ());
1170 checked_set (_tms_password, config->tms_password ());
1173 void tms_protocol_changed ()
1175 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1178 void tms_ip_changed ()
1180 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1183 void tms_path_changed ()
1185 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1188 void tms_user_changed ()
1190 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1193 void tms_password_changed ()
1195 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1198 wxChoice* _tms_protocol;
1199 wxTextCtrl* _tms_ip;
1200 wxTextCtrl* _tms_path;
1201 wxTextCtrl* _tms_user;
1202 wxTextCtrl* _tms_password;
1211 class KDMEmailPage : public StandardPage
1215 KDMEmailPage (wxSize panel_size, int border)
1216 #ifdef DCPOMATIC_OSX
1217 /* We have to force both width and height of this one */
1218 : StandardPage (wxSize (480, 128), border)
1220 : StandardPage (panel_size, border)
1224 wxString GetName () const
1226 return _("KDM Email");
1229 #ifdef DCPOMATIC_OSX
1230 wxBitmap GetLargeIcon () const
1232 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1239 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1240 table->AddGrowableCol (1, 1);
1241 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1243 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1245 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1246 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1247 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1248 add_label_to_sizer (s, _panel, _("port"), false);
1249 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1250 _mail_port->SetRange (0, 65535);
1251 s->Add (_mail_port);
1252 table->Add (s, 1, wxEXPAND | wxALL);
1255 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1256 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1257 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1259 add_label_to_sizer (table, _panel, _("Mail password"), true);
1260 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1261 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1263 add_label_to_sizer (table, _panel, _("Subject"), true);
1264 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1265 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1267 add_label_to_sizer (table, _panel, _("From address"), true);
1268 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1269 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1271 vector<string> columns;
1272 columns.push_back (wx_to_std (_("Address")));
1273 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1274 _kdm_cc = new EditableList<string, EmailDialog> (
1277 bind (&Config::kdm_cc, Config::instance()),
1278 bind (&Config::set_kdm_cc, Config::instance(), _1),
1279 bind (&string_not_empty, _1),
1282 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1284 add_label_to_sizer (table, _panel, _("BCC address"), true);
1285 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1286 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1288 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1289 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1291 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1292 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1296 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1297 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1298 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1299 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1300 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1301 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1302 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1303 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1304 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1307 void config_changed ()
1309 Config* config = Config::instance ();
1311 checked_set (_mail_server, config->mail_server ());
1312 checked_set (_mail_port, config->mail_port ());
1313 checked_set (_mail_user, config->mail_user ());
1314 checked_set (_mail_password, config->mail_password ());
1315 checked_set (_kdm_subject, config->kdm_subject ());
1316 checked_set (_kdm_from, config->kdm_from ());
1317 checked_set (_kdm_bcc, config->kdm_bcc ());
1318 checked_set (_kdm_email, Config::instance()->kdm_email ());
1321 void mail_server_changed ()
1323 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1326 void mail_port_changed ()
1328 Config::instance()->set_mail_port (_mail_port->GetValue ());
1331 void mail_user_changed ()
1333 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1336 void mail_password_changed ()
1338 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1341 void kdm_subject_changed ()
1343 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1346 void kdm_from_changed ()
1348 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1351 void kdm_bcc_changed ()
1353 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1356 void kdm_email_changed ()
1358 if (_kdm_email->GetValue().IsEmpty ()) {
1359 /* Sometimes we get sent an erroneous notification that the email
1360 is empty; I don't know why.
1364 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1367 void reset_kdm_email ()
1369 Config::instance()->reset_kdm_email ();
1370 checked_set (_kdm_email, Config::instance()->kdm_email ());
1373 wxTextCtrl* _mail_server;
1374 wxSpinCtrl* _mail_port;
1375 wxTextCtrl* _mail_user;
1376 wxTextCtrl* _mail_password;
1377 wxTextCtrl* _kdm_subject;
1378 wxTextCtrl* _kdm_from;
1379 EditableList<string, EmailDialog>* _kdm_cc;
1380 wxTextCtrl* _kdm_bcc;
1381 wxTextCtrl* _kdm_email;
1382 wxButton* _reset_kdm_email;
1385 /** @class AdvancedPage
1386 * @brief Advanced page of the preferences dialog.
1388 class AdvancedPage : public StockPage
1391 AdvancedPage (wxSize panel_size, int border)
1392 : StockPage (Kind_Advanced, panel_size, border)
1393 , _maximum_j2k_bandwidth (0)
1394 , _allow_any_dcp_frame_rate (0)
1395 , _only_servers_encode (0)
1400 , _log_debug_decode (0)
1401 , _log_debug_encode (0)
1402 , _log_debug_email (0)
1406 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1408 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1410 flags |= wxALIGN_RIGHT;
1413 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1414 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1419 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1420 table->AddGrowableCol (1, 1);
1421 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1424 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1425 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1426 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1427 s->Add (_maximum_j2k_bandwidth, 1);
1428 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1432 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1433 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1434 table->AddSpacer (0);
1436 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1437 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1438 table->AddSpacer (0);
1441 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1442 dcp::NameFormat::Map titles;
1443 titles['t'] = "type (cpl/pkl)";
1444 dcp::NameFormat::Map examples;
1445 examples['t'] = "cpl";
1446 _dcp_metadata_filename_format = new NameFormatEditor (
1447 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1449 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1453 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1454 dcp::NameFormat::Map titles;
1455 titles['t'] = "type (j2c/pcm/sub)";
1456 titles['r'] = "reel number";
1457 titles['n'] = "number of reels";
1458 titles['c'] = "content filename";
1459 dcp::NameFormat::Map examples;
1460 examples['t'] = "j2c";
1461 examples['r'] = "1";
1462 examples['n'] = "4";
1463 examples['c'] = "myfile.mp4";
1464 _dcp_asset_filename_format = new NameFormatEditor (
1465 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1467 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1471 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1472 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1473 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1474 t->Add (_log_general, 1, wxEXPAND | wxALL);
1475 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1476 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1477 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1478 t->Add (_log_error, 1, wxEXPAND | wxALL);
1479 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1480 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1481 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1482 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1483 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1484 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1485 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1486 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1487 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1488 table->Add (t, 0, wxALL, 6);
1491 #ifdef DCPOMATIC_WINDOWS
1492 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1493 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1494 table->AddSpacer (0);
1497 _maximum_j2k_bandwidth->SetRange (1, 1000);
1498 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1499 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1500 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1501 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1502 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1503 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1504 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1505 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1506 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1507 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1508 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1509 _log_debug_email->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1510 #ifdef DCPOMATIC_WINDOWS
1511 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1515 void config_changed ()
1517 Config* config = Config::instance ();
1519 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1520 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1521 checked_set (_only_servers_encode, config->only_servers_encode ());
1522 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1523 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1524 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1525 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1526 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1527 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1528 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1529 #ifdef DCPOMATIC_WINDOWS
1530 checked_set (_win32_console, config->win32_console());
1534 void maximum_j2k_bandwidth_changed ()
1536 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1539 void allow_any_dcp_frame_rate_changed ()
1541 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1544 void only_servers_encode_changed ()
1546 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1549 void dcp_metadata_filename_format_changed ()
1551 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1554 void dcp_asset_filename_format_changed ()
1556 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1562 if (_log_general->GetValue ()) {
1563 types |= LogEntry::TYPE_GENERAL;
1565 if (_log_warning->GetValue ()) {
1566 types |= LogEntry::TYPE_WARNING;
1568 if (_log_error->GetValue ()) {
1569 types |= LogEntry::TYPE_ERROR;
1571 if (_log_timing->GetValue ()) {
1572 types |= LogEntry::TYPE_TIMING;
1574 if (_log_debug_decode->GetValue ()) {
1575 types |= LogEntry::TYPE_DEBUG_DECODE;
1577 if (_log_debug_encode->GetValue ()) {
1578 types |= LogEntry::TYPE_DEBUG_ENCODE;
1580 if (_log_debug_email->GetValue ()) {
1581 types |= LogEntry::TYPE_DEBUG_EMAIL;
1583 Config::instance()->set_log_types (types);
1586 #ifdef DCPOMATIC_WINDOWS
1587 void win32_console_changed ()
1589 Config::instance()->set_win32_console (_win32_console->GetValue ());
1593 wxSpinCtrl* _maximum_j2k_bandwidth;
1594 wxCheckBox* _allow_any_dcp_frame_rate;
1595 wxCheckBox* _only_servers_encode;
1596 NameFormatEditor* _dcp_metadata_filename_format;
1597 NameFormatEditor* _dcp_asset_filename_format;
1598 wxCheckBox* _log_general;
1599 wxCheckBox* _log_warning;
1600 wxCheckBox* _log_error;
1601 wxCheckBox* _log_timing;
1602 wxCheckBox* _log_debug_decode;
1603 wxCheckBox* _log_debug_encode;
1604 wxCheckBox* _log_debug_email;
1605 #ifdef DCPOMATIC_WINDOWS
1606 wxCheckBox* _win32_console;
1610 wxPreferencesEditor*
1611 create_config_dialog ()
1613 wxPreferencesEditor* e = new wxPreferencesEditor ();
1615 #ifdef DCPOMATIC_OSX
1616 /* Width that we force some of the config panels to be on OSX so that
1617 the containing window doesn't shrink too much when we select those panels.
1618 This is obviously an unpleasant hack.
1620 wxSize ps = wxSize (520, -1);
1621 int const border = 16;
1623 wxSize ps = wxSize (-1, -1);
1624 int const border = 8;
1627 e->AddPage (new GeneralPage (ps, border));
1628 e->AddPage (new DefaultsPage (ps, border));
1629 e->AddPage (new EncodingServersPage (ps, border));
1630 e->AddPage (new KeysPage (ps, border));
1631 e->AddPage (new TMSPage (ps, border));
1632 e->AddPage (new KDMEmailPage (ps, border));
1633 e->AddPage (new AdvancedPage (ps, border));