2 Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/config_dialog.cc
21 * @brief A dialogue to edit DCP-o-matic configuration.
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/colour_matrix.h>
32 #include <dcp/exceptions.h>
33 #include <dcp/signer.h>
34 #include "lib/config.h"
35 #include "lib/ratio.h"
36 #include "lib/scaler.h"
37 #include "lib/filter.h"
38 #include "lib/dcp_content_type.h"
39 #include "lib/colour_conversion.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include "config_dialog.h"
46 #include "editable_list.h"
47 #include "filter_dialog.h"
48 #include "dir_picker_ctrl.h"
49 #include "isdcf_metadata_dialog.h"
50 #include "preset_colour_conversion_dialog.h"
51 #include "server_dialog.h"
52 #include "make_signer_chain_dialog.h"
59 using boost::shared_ptr;
60 using boost::lexical_cast;
65 Page (wxSize panel_size, int border)
66 : _panel_size (panel_size)
71 wxPanel* make_panel (wxWindow* parent)
73 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
74 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
83 class GeneralPage : public wxStockPreferencesPage, public Page
86 GeneralPage (wxSize panel_size, int border)
87 : wxStockPreferencesPage (Kind_General)
88 , Page (panel_size, border)
91 wxWindow* CreateWindow (wxWindow* parent)
93 wxPanel* panel = make_panel (parent);
95 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
96 table->AddGrowableCol (1, 1);
97 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
99 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
100 table->Add (_set_language, 1);
101 _language = new wxChoice (panel, wxID_ANY);
102 _language->Append (wxT ("Deutsch"));
103 _language->Append (wxT ("English"));
104 _language->Append (wxT ("Español"));
105 _language->Append (wxT ("Français"));
106 _language->Append (wxT ("Italiano"));
107 _language->Append (wxT ("Nederlands"));
108 _language->Append (wxT ("Svenska"));
109 table->Add (_language);
111 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
112 wxFont font = restart->GetFont();
113 font.SetStyle (wxFONTSTYLE_ITALIC);
114 font.SetPointSize (font.GetPointSize() - 1);
115 restart->SetFont (font);
116 table->AddSpacer (0);
118 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
119 _num_local_encoding_threads = new wxSpinCtrl (panel);
120 table->Add (_num_local_encoding_threads, 1);
122 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
123 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
124 table->AddSpacer (0);
126 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
127 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
128 table->AddSpacer (0);
130 Config* config = Config::instance ();
132 _set_language->SetValue (config->language ());
134 if (config->language().get_value_or ("") == "fr") {
135 _language->SetSelection (3);
136 } else if (config->language().get_value_or ("") == "it") {
137 _language->SetSelection (4);
138 } else if (config->language().get_value_or ("") == "es") {
139 _language->SetSelection (2);
140 } else if (config->language().get_value_or ("") == "sv") {
141 _language->SetSelection (6);
142 } else if (config->language().get_value_or ("") == "de") {
143 _language->SetSelection (0);
144 } else if (config->language().get_value_or ("") == "nl") {
145 _language->SetSelection (5);
147 _language->SetSelection (1);
150 setup_language_sensitivity ();
152 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
153 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
155 _num_local_encoding_threads->SetRange (1, 128);
156 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
157 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
159 _check_for_updates->SetValue (config->check_for_updates ());
160 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
161 _check_for_test_updates->SetValue (config->check_for_test_updates ());
162 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
168 void setup_language_sensitivity ()
170 _language->Enable (_set_language->GetValue ());
173 void set_language_changed ()
175 setup_language_sensitivity ();
176 if (_set_language->GetValue ()) {
179 Config::instance()->unset_language ();
183 void language_changed ()
185 switch (_language->GetSelection ()) {
187 Config::instance()->set_language ("de");
190 Config::instance()->set_language ("en");
193 Config::instance()->set_language ("es");
196 Config::instance()->set_language ("fr");
199 Config::instance()->set_language ("it");
202 Config::instance()->set_language ("nl");
205 Config::instance()->set_language ("sv");
210 void check_for_updates_changed ()
212 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
215 void check_for_test_updates_changed ()
217 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
220 void num_local_encoding_threads_changed ()
222 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
225 wxCheckBox* _set_language;
227 wxSpinCtrl* _num_local_encoding_threads;
228 wxCheckBox* _check_for_updates;
229 wxCheckBox* _check_for_test_updates;
232 class DefaultsPage : public wxPreferencesPage, public Page
235 DefaultsPage (wxSize panel_size, int border)
236 : Page (panel_size, border)
239 wxString GetName () const
241 return _("Defaults");
245 wxBitmap GetLargeIcon () const
247 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
251 wxWindow* CreateWindow (wxWindow* parent)
253 wxPanel* panel = make_panel (parent);
255 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
256 table->AddGrowableCol (1, 1);
257 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
260 add_label_to_sizer (table, panel, _("Default duration of still images"), true);
261 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
262 _still_length = new wxSpinCtrl (panel);
263 s->Add (_still_length);
264 add_label_to_sizer (s, panel, _("s"), false);
268 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
269 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
270 _directory = new DirPickerCtrl (panel);
272 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
274 table->Add (_directory, 1, wxEXPAND);
276 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
277 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
278 table->Add (_isdcf_metadata_button);
280 add_label_to_sizer (table, panel, _("Default scale to"), true);
281 _scale = new wxChoice (panel, wxID_ANY);
284 add_label_to_sizer (table, panel, _("Default container"), true);
285 _container = new wxChoice (panel, wxID_ANY);
286 table->Add (_container);
288 add_label_to_sizer (table, panel, _("Default content type"), true);
289 _dcp_content_type = new wxChoice (panel, wxID_ANY);
290 table->Add (_dcp_content_type);
293 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
294 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
295 _j2k_bandwidth = new wxSpinCtrl (panel);
296 s->Add (_j2k_bandwidth);
297 add_label_to_sizer (s, panel, _("Mbit/s"), false);
302 add_label_to_sizer (table, panel, _("Default audio delay"), true);
303 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
304 _audio_delay = new wxSpinCtrl (panel);
305 s->Add (_audio_delay);
306 add_label_to_sizer (s, panel, _("ms"), false);
310 add_label_to_sizer (table, panel, _("Default issuer"), true);
311 _issuer = new wxTextCtrl (panel, wxID_ANY);
312 table->Add (_issuer, 1, wxEXPAND);
314 Config* config = Config::instance ();
316 _still_length->SetRange (1, 3600);
317 _still_length->SetValue (config->default_still_length ());
318 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
320 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
321 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
323 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
325 vector<VideoContentScale> scales = VideoContentScale::all ();
326 for (size_t i = 0; i < scales.size(); ++i) {
327 _scale->Append (std_to_wx (scales[i].name ()));
328 if (scales[i] == config->default_scale ()) {
329 _scale->SetSelection (i);
333 vector<Ratio const *> ratios = Ratio::all ();
334 for (size_t i = 0; i < ratios.size(); ++i) {
335 _container->Append (std_to_wx (ratios[i]->nickname ()));
336 if (ratios[i] == config->default_container ()) {
337 _container->SetSelection (i);
341 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
342 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
344 vector<DCPContentType const *> const ct = DCPContentType::all ();
345 for (size_t i = 0; i < ct.size(); ++i) {
346 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
347 if (ct[i] == config->default_dcp_content_type ()) {
348 _dcp_content_type->SetSelection (i);
352 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
354 _j2k_bandwidth->SetRange (50, 250);
355 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
356 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
358 _audio_delay->SetRange (-1000, 1000);
359 _audio_delay->SetValue (config->default_audio_delay ());
360 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
362 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
363 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
369 void j2k_bandwidth_changed ()
371 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
374 void audio_delay_changed ()
376 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
379 void directory_changed ()
381 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
384 void edit_isdcf_metadata_clicked (wxWindow* parent)
386 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
388 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
392 void still_length_changed ()
394 Config::instance()->set_default_still_length (_still_length->GetValue ());
397 void scale_changed ()
399 vector<VideoContentScale> scale = VideoContentScale::all ();
400 Config::instance()->set_default_scale (scale[_scale->GetSelection()]);
403 void container_changed ()
405 vector<Ratio const *> ratio = Ratio::all ();
406 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
409 void dcp_content_type_changed ()
411 vector<DCPContentType const *> ct = DCPContentType::all ();
412 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
415 void issuer_changed ()
417 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
420 wxSpinCtrl* _j2k_bandwidth;
421 wxSpinCtrl* _audio_delay;
422 wxButton* _isdcf_metadata_button;
423 wxSpinCtrl* _still_length;
424 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
425 DirPickerCtrl* _directory;
427 wxDirPickerCtrl* _directory;
430 wxChoice* _container;
431 wxChoice* _dcp_content_type;
435 class EncodingServersPage : public wxPreferencesPage, public Page
438 EncodingServersPage (wxSize panel_size, int border)
439 : Page (panel_size, border)
442 wxString GetName () const
448 wxBitmap GetLargeIcon () const
450 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
454 wxWindow* CreateWindow (wxWindow* parent)
456 wxPanel* panel = make_panel (parent);
458 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
459 panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
461 vector<string> columns;
462 columns.push_back (wx_to_std (_("IP address / host name")));
463 _servers_list = new EditableList<string, ServerDialog> (
466 boost::bind (&Config::servers, Config::instance()),
467 boost::bind (&Config::set_servers, Config::instance(), _1),
468 boost::bind (&EncodingServersPage::server_column, this, _1)
471 panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
473 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
474 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
481 void use_any_servers_changed ()
483 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
486 string server_column (string s)
491 wxCheckBox* _use_any_servers;
492 EditableList<string, ServerDialog>* _servers_list;
495 class ColourConversionsPage : public wxPreferencesPage, public Page
498 ColourConversionsPage (wxSize panel_size, int border)
499 : Page (panel_size, border)
502 wxString GetName () const
504 return _("Colour Conversions");
508 wxBitmap GetLargeIcon () const
510 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
513 wxWindow* CreateWindow (wxWindow* parent)
515 wxPanel* panel = make_panel (parent);
517 vector<string> columns;
518 columns.push_back (wx_to_std (_("Name")));
519 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
522 boost::bind (&Config::colour_conversions, Config::instance()),
523 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
524 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
528 panel->GetSizer()->Add (list, 1, wxEXPAND | wxALL, _border);
533 string colour_conversion_column (PresetColourConversion c)
539 class KeysPage : public wxPreferencesPage, public Page
542 KeysPage (wxSize panel_size, int border)
543 : Page (panel_size, border)
546 wxString GetName () const
552 wxBitmap GetLargeIcon () const
554 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
558 wxWindow* CreateWindow (wxWindow* parent)
560 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
561 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
562 _panel->SetSizer (overall_sizer);
564 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
565 overall_sizer->Add (m, 0, wxALL, _border);
567 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
568 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
570 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
575 ip.SetText (_("Type"));
577 _certificates->InsertColumn (0, ip);
583 ip.SetText (_("Thumbprint"));
586 wxFont font = ip.GetFont ();
587 font.SetFamily (wxFONTFAMILY_TELETYPE);
590 _certificates->InsertColumn (1, ip);
593 certificates_sizer->Add (_certificates, 1, wxEXPAND);
596 wxSizer* s = new wxBoxSizer (wxVERTICAL);
597 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
598 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
599 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
600 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
601 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
604 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
605 table->AddGrowableCol (1, 1);
606 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
608 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
609 table->Add (_remake_certificates, 0);
610 table->AddSpacer (0);
612 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
614 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
615 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
616 wxFont font = _signer_private_key->GetFont ();
617 font.SetFamily (wxFONTFAMILY_TELETYPE);
618 _signer_private_key->SetFont (font);
619 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
620 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
621 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
625 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
627 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
628 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
629 wxFont font = _decryption_certificate->GetFont ();
630 font.SetFamily (wxFONTFAMILY_TELETYPE);
631 _decryption_certificate->SetFont (font);
632 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
633 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
634 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
638 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
640 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
641 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
642 wxFont font = _decryption_private_key->GetFont ();
643 font.SetFamily (wxFONTFAMILY_TELETYPE);
644 _decryption_private_key->SetFont (font);
645 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
646 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
647 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
651 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
652 table->Add (_export_decryption_certificate);
653 table->AddSpacer (0);
655 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
656 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
657 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
658 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
659 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
660 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
661 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
662 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
663 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
665 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
667 update_certificate_list ();
668 update_signer_private_key ();
669 update_decryption_certificate ();
670 update_decryption_private_key ();
671 update_sensitivity ();
677 void add_certificate ()
679 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
681 if (d->ShowModal() == wxID_OK) {
683 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
684 _signer->certificates().add (c);
685 Config::instance()->set_signer (_signer);
686 update_certificate_list ();
687 } catch (dcp::MiscError& e) {
688 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
694 update_sensitivity ();
697 void remove_certificate ()
699 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
704 _certificates->DeleteItem (i);
705 _signer->certificates().remove (i);
706 Config::instance()->set_signer (_signer);
708 update_sensitivity ();
711 void update_certificate_list ()
713 _certificates->DeleteAllItems ();
714 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
716 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
719 _certificates->InsertItem (item);
720 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
723 _certificates->SetItem (n, 0, _("Root"));
724 } else if (n == (certs.size() - 1)) {
725 _certificates->SetItem (n, 0, _("Leaf"));
727 _certificates->SetItem (n, 0, _("Intermediate"));
734 void remake_certificates ()
736 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
737 if (d->ShowModal () == wxID_OK) {
742 d->organisational_unit (),
743 d->root_common_name (),
744 d->intermediate_common_name (),
745 d->leaf_common_name ()
749 Config::instance()->set_signer (_signer);
750 update_certificate_list ();
751 update_signer_private_key ();
757 void update_sensitivity ()
759 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
762 void update_signer_private_key ()
764 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
767 void load_signer_private_key ()
769 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
771 if (d->ShowModal() == wxID_OK) {
773 boost::filesystem::path p (wx_to_std (d->GetPath ()));
774 if (boost::filesystem::file_size (p) > 1024) {
775 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
779 _signer->set_key (dcp::file_to_string (p));
780 Config::instance()->set_signer (_signer);
781 update_signer_private_key ();
782 } catch (dcp::MiscError& e) {
783 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
789 update_sensitivity ();
793 void load_decryption_certificate ()
795 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
797 if (d->ShowModal() == wxID_OK) {
799 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
800 Config::instance()->set_decryption_certificate (c);
801 update_decryption_certificate ();
802 } catch (dcp::MiscError& e) {
803 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
810 void update_decryption_certificate ()
812 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
815 void load_decryption_private_key ()
817 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
819 if (d->ShowModal() == wxID_OK) {
821 boost::filesystem::path p (wx_to_std (d->GetPath ()));
822 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
823 update_decryption_private_key ();
824 } catch (dcp::MiscError& e) {
825 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
832 void update_decryption_private_key ()
834 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
837 void export_decryption_certificate ()
839 wxFileDialog* d = new wxFileDialog (
840 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
841 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
844 if (d->ShowModal () == wxID_OK) {
845 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
847 throw OpenFileError (wx_to_std (d->GetPath ()));
850 string const s = Config::instance()->decryption_certificate().certificate (true);
851 fwrite (s.c_str(), 1, s.length(), f);
858 wxListCtrl* _certificates;
859 wxButton* _add_certificate;
860 wxButton* _remove_certificate;
861 wxButton* _remake_certificates;
862 wxStaticText* _signer_private_key;
863 wxButton* _load_signer_private_key;
864 wxStaticText* _decryption_certificate;
865 wxButton* _load_decryption_certificate;
866 wxStaticText* _decryption_private_key;
867 wxButton* _load_decryption_private_key;
868 wxButton* _export_decryption_certificate;
869 shared_ptr<dcp::Signer> _signer;
872 class TMSPage : public wxPreferencesPage, public Page
875 TMSPage (wxSize panel_size, int border)
876 : Page (panel_size, border)
879 wxString GetName () const
885 wxBitmap GetLargeIcon () const
887 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
891 wxWindow* CreateWindow (wxWindow* parent)
893 wxPanel* panel = make_panel (parent);
895 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
896 table->AddGrowableCol (1, 1);
897 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
899 add_label_to_sizer (table, panel, _("IP address"), true);
900 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
901 table->Add (_tms_ip, 1, wxEXPAND);
903 add_label_to_sizer (table, panel, _("Target path"), true);
904 _tms_path = new wxTextCtrl (panel, wxID_ANY);
905 table->Add (_tms_path, 1, wxEXPAND);
907 add_label_to_sizer (table, panel, _("User name"), true);
908 _tms_user = new wxTextCtrl (panel, wxID_ANY);
909 table->Add (_tms_user, 1, wxEXPAND);
911 add_label_to_sizer (table, panel, _("Password"), true);
912 _tms_password = new wxTextCtrl (panel, wxID_ANY);
913 table->Add (_tms_password, 1, wxEXPAND);
915 Config* config = Config::instance ();
917 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
918 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
919 _tms_path->SetValue (std_to_wx (config->tms_path ()));
920 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
921 _tms_user->SetValue (std_to_wx (config->tms_user ()));
922 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
923 _tms_password->SetValue (std_to_wx (config->tms_password ()));
924 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
930 void tms_ip_changed ()
932 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
935 void tms_path_changed ()
937 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
940 void tms_user_changed ()
942 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
945 void tms_password_changed ()
947 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
951 wxTextCtrl* _tms_path;
952 wxTextCtrl* _tms_user;
953 wxTextCtrl* _tms_password;
956 class KDMEmailPage : public wxPreferencesPage, public Page
960 KDMEmailPage (wxSize panel_size, int border)
961 : Page (panel_size, border)
964 wxString GetName () const
966 return _("KDM Email");
970 wxBitmap GetLargeIcon () const
972 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
976 wxWindow* CreateWindow (wxWindow* parent)
979 /* We have to force both width and height of this one */
980 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
982 wxPanel* panel = new wxPanel (parent);
984 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
987 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
988 table->AddGrowableCol (1, 1);
989 panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
991 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
992 _mail_server = new wxTextCtrl (panel, wxID_ANY);
993 table->Add (_mail_server, 1, wxEXPAND | wxALL);
995 add_label_to_sizer (table, panel, _("Mail user name"), true);
996 _mail_user = new wxTextCtrl (panel, wxID_ANY);
997 table->Add (_mail_user, 1, wxEXPAND | wxALL);
999 add_label_to_sizer (table, panel, _("Mail password"), true);
1000 _mail_password = new wxTextCtrl (panel, wxID_ANY);
1001 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1003 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
1004 wxFont font = plain->GetFont();
1005 font.SetStyle (wxFONTSTYLE_ITALIC);
1006 font.SetPointSize (font.GetPointSize() - 1);
1007 plain->SetFont (font);
1008 table->AddSpacer (0);
1010 add_label_to_sizer (table, panel, _("Subject"), true);
1011 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
1012 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1014 add_label_to_sizer (table, panel, _("From address"), true);
1015 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
1016 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1018 add_label_to_sizer (table, panel, _("CC address"), true);
1019 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
1020 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1022 add_label_to_sizer (table, panel, _("BCC address"), true);
1023 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
1024 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1026 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1027 panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1029 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1030 panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1032 Config* config = Config::instance ();
1033 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1034 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1035 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1036 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1037 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1038 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1039 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1040 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1041 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1042 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1043 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1044 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1045 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1046 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1047 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1048 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1049 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1055 void mail_server_changed ()
1057 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1060 void mail_user_changed ()
1062 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1065 void mail_password_changed ()
1067 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1070 void kdm_subject_changed ()
1072 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1075 void kdm_from_changed ()
1077 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1080 void kdm_cc_changed ()
1082 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1085 void kdm_bcc_changed ()
1087 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1090 void kdm_email_changed ()
1092 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1095 void reset_kdm_email ()
1097 Config::instance()->reset_kdm_email ();
1098 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1101 wxTextCtrl* _mail_server;
1102 wxTextCtrl* _mail_user;
1103 wxTextCtrl* _mail_password;
1104 wxTextCtrl* _kdm_subject;
1105 wxTextCtrl* _kdm_from;
1106 wxTextCtrl* _kdm_cc;
1107 wxTextCtrl* _kdm_bcc;
1108 wxTextCtrl* _kdm_email;
1109 wxButton* _reset_kdm_email;
1112 /** @class AdvancedPage
1113 * @brief Advanced page of the preferences dialog.
1115 class AdvancedPage : public wxStockPreferencesPage, public Page
1119 AdvancedPage (wxSize panel_size, int border)
1120 : wxStockPreferencesPage (Kind_Advanced)
1121 , Page (panel_size, border)
1124 wxWindow* CreateWindow (wxWindow* parent)
1126 wxPanel* panel = make_panel (parent);
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);
1133 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1134 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1135 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1136 s->Add (_maximum_j2k_bandwidth, 1);
1137 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1141 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1142 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1143 table->AddSpacer (0);
1146 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log:"));
1147 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1149 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1150 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1154 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1155 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1156 t->Add (_log_general, 1, wxEXPAND | wxALL);
1157 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1158 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1159 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1160 t->Add (_log_error, 1, wxEXPAND | wxALL);
1161 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1162 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1163 table->Add (t, 0, wxALL, 6);
1166 Config* config = Config::instance ();
1168 _maximum_j2k_bandwidth->SetRange (1, 500);
1169 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1170 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1171 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1172 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1173 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1174 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1175 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1176 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1177 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1178 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1179 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1180 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1187 void maximum_j2k_bandwidth_changed ()
1189 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1192 void allow_any_dcp_frame_rate_changed ()
1194 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1200 if (_log_general->GetValue ()) {
1201 types |= Log::TYPE_GENERAL;
1203 if (_log_warning->GetValue ()) {
1204 types |= Log::TYPE_WARNING;
1206 if (_log_error->GetValue ()) {
1207 types |= Log::TYPE_ERROR;
1209 if (_log_timing->GetValue ()) {
1210 types |= Log::TYPE_TIMING;
1212 Config::instance()->set_log_types (types);
1215 wxSpinCtrl* _maximum_j2k_bandwidth;
1216 wxCheckBox* _allow_any_dcp_frame_rate;
1217 wxCheckBox* _log_general;
1218 wxCheckBox* _log_warning;
1219 wxCheckBox* _log_error;
1220 wxCheckBox* _log_timing;
1223 wxPreferencesEditor*
1224 create_config_dialog ()
1226 wxPreferencesEditor* e = new wxPreferencesEditor ();
1228 #ifdef DCPOMATIC_OSX
1229 /* Width that we force some of the config panels to be on OSX so that
1230 the containing window doesn't shrink too much when we select those panels.
1231 This is obviously an unpleasant hack.
1233 wxSize ps = wxSize (520, -1);
1234 int const border = 16;
1236 wxSize ps = wxSize (-1, -1);
1237 int const border = 8;
1240 e->AddPage (new GeneralPage (ps, border));
1241 e->AddPage (new DefaultsPage (ps, border));
1242 e->AddPage (new EncodingServersPage (ps, border));
1243 e->AddPage (new ColourConversionsPage (ps, border));
1244 e->AddPage (new KeysPage (ps, border));
1245 e->AddPage (new TMSPage (ps, border));
1246 e->AddPage (new KDMEmailPage (ps, border));
1247 e->AddPage (new AdvancedPage (ps, border));