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 container"), true);
281 _container = new wxChoice (panel, wxID_ANY);
282 table->Add (_container);
284 add_label_to_sizer (table, panel, _("Default content type"), true);
285 _dcp_content_type = new wxChoice (panel, wxID_ANY);
286 table->Add (_dcp_content_type);
289 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
290 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
291 _j2k_bandwidth = new wxSpinCtrl (panel);
292 s->Add (_j2k_bandwidth);
293 add_label_to_sizer (s, panel, _("Mbit/s"), false);
298 add_label_to_sizer (table, panel, _("Default audio delay"), true);
299 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
300 _audio_delay = new wxSpinCtrl (panel);
301 s->Add (_audio_delay);
302 add_label_to_sizer (s, panel, _("ms"), false);
306 add_label_to_sizer (table, panel, _("Default issuer"), true);
307 _issuer = new wxTextCtrl (panel, wxID_ANY);
308 table->Add (_issuer, 1, wxEXPAND);
310 Config* config = Config::instance ();
312 _still_length->SetRange (1, 3600);
313 _still_length->SetValue (config->default_still_length ());
314 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
316 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
317 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
319 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
321 vector<Ratio const *> ratios = Ratio::all ();
322 for (size_t i = 0; i < ratios.size(); ++i) {
323 _container->Append (std_to_wx (ratios[i]->nickname ()));
324 if (ratios[i] == config->default_container ()) {
325 _container->SetSelection (i);
329 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
331 vector<DCPContentType const *> const ct = DCPContentType::all ();
332 for (size_t i = 0; i < ct.size(); ++i) {
333 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
334 if (ct[i] == config->default_dcp_content_type ()) {
335 _dcp_content_type->SetSelection (i);
339 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
341 _j2k_bandwidth->SetRange (50, 250);
342 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
343 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
345 _audio_delay->SetRange (-1000, 1000);
346 _audio_delay->SetValue (config->default_audio_delay ());
347 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
349 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
350 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
352 config->Changed.connect (boost::bind (&DefaultsPage::config_changed, this));
358 void config_changed ()
360 _j2k_bandwidth->SetRange (50, Config::instance()->maximum_j2k_bandwidth() / 1000000);
363 void j2k_bandwidth_changed ()
365 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
368 void audio_delay_changed ()
370 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
373 void directory_changed ()
375 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
378 void edit_isdcf_metadata_clicked (wxWindow* parent)
380 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
382 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
386 void still_length_changed ()
388 Config::instance()->set_default_still_length (_still_length->GetValue ());
391 void container_changed ()
393 vector<Ratio const *> ratio = Ratio::all ();
394 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
397 void dcp_content_type_changed ()
399 vector<DCPContentType const *> ct = DCPContentType::all ();
400 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
403 void issuer_changed ()
405 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
408 wxSpinCtrl* _j2k_bandwidth;
409 wxSpinCtrl* _audio_delay;
410 wxButton* _isdcf_metadata_button;
411 wxSpinCtrl* _still_length;
412 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
413 DirPickerCtrl* _directory;
415 wxDirPickerCtrl* _directory;
417 wxChoice* _container;
418 wxChoice* _dcp_content_type;
422 class EncodingServersPage : public wxPreferencesPage, public Page
425 EncodingServersPage (wxSize panel_size, int border)
426 : Page (panel_size, border)
429 wxString GetName () const
435 wxBitmap GetLargeIcon () const
437 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
441 wxWindow* CreateWindow (wxWindow* parent)
443 wxPanel* panel = make_panel (parent);
445 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
446 panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
448 vector<string> columns;
449 columns.push_back (wx_to_std (_("IP address / host name")));
450 _servers_list = new EditableList<string, ServerDialog> (
453 boost::bind (&Config::servers, Config::instance()),
454 boost::bind (&Config::set_servers, Config::instance(), _1),
455 boost::bind (&EncodingServersPage::server_column, this, _1)
458 panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
460 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
461 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
468 void use_any_servers_changed ()
470 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
473 string server_column (string s)
478 wxCheckBox* _use_any_servers;
479 EditableList<string, ServerDialog>* _servers_list;
482 class ColourConversionsPage : public wxPreferencesPage, public Page
485 ColourConversionsPage (wxSize panel_size, int border)
486 : Page (panel_size, border)
489 wxString GetName () const
491 return _("Colour Conversions");
495 wxBitmap GetLargeIcon () const
497 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
500 wxWindow* CreateWindow (wxWindow* parent)
502 wxPanel* panel = make_panel (parent);
504 vector<string> columns;
505 columns.push_back (wx_to_std (_("Name")));
506 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
509 boost::bind (&Config::colour_conversions, Config::instance()),
510 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
511 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
515 panel->GetSizer()->Add (list, 1, wxEXPAND | wxALL, _border);
520 string colour_conversion_column (PresetColourConversion c)
526 class KeysPage : public wxPreferencesPage, public Page
529 KeysPage (wxSize panel_size, int border)
530 : Page (panel_size, border)
533 wxString GetName () const
539 wxBitmap GetLargeIcon () const
541 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
545 wxWindow* CreateWindow (wxWindow* parent)
547 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
548 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
549 _panel->SetSizer (overall_sizer);
551 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
552 overall_sizer->Add (m, 0, wxALL, _border);
554 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
555 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
557 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
562 ip.SetText (_("Type"));
564 _certificates->InsertColumn (0, ip);
570 ip.SetText (_("Thumbprint"));
573 wxFont font = ip.GetFont ();
574 font.SetFamily (wxFONTFAMILY_TELETYPE);
577 _certificates->InsertColumn (1, ip);
580 certificates_sizer->Add (_certificates, 1, wxEXPAND);
583 wxSizer* s = new wxBoxSizer (wxVERTICAL);
584 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
585 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
586 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
587 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
588 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
591 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
592 table->AddGrowableCol (1, 1);
593 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
595 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
596 table->Add (_remake_certificates, 0);
597 table->AddSpacer (0);
599 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
601 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
602 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
603 wxFont font = _signer_private_key->GetFont ();
604 font.SetFamily (wxFONTFAMILY_TELETYPE);
605 _signer_private_key->SetFont (font);
606 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
607 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
608 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
612 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
614 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
615 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
616 wxFont font = _decryption_certificate->GetFont ();
617 font.SetFamily (wxFONTFAMILY_TELETYPE);
618 _decryption_certificate->SetFont (font);
619 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
620 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
621 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
625 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
627 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
628 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
629 wxFont font = _decryption_private_key->GetFont ();
630 font.SetFamily (wxFONTFAMILY_TELETYPE);
631 _decryption_private_key->SetFont (font);
632 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
633 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
634 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
638 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
639 table->Add (_export_decryption_certificate);
640 table->AddSpacer (0);
642 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
643 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
644 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
645 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
646 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
647 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
648 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
649 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
650 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
652 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
654 update_certificate_list ();
655 update_signer_private_key ();
656 update_decryption_certificate ();
657 update_decryption_private_key ();
658 update_sensitivity ();
664 void add_certificate ()
666 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
668 if (d->ShowModal() == wxID_OK) {
670 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
671 _signer->certificates().add (c);
672 Config::instance()->set_signer (_signer);
673 update_certificate_list ();
674 } catch (dcp::MiscError& e) {
675 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
681 update_sensitivity ();
684 void remove_certificate ()
686 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
691 _certificates->DeleteItem (i);
692 _signer->certificates().remove (i);
693 Config::instance()->set_signer (_signer);
695 update_sensitivity ();
698 void update_certificate_list ()
700 _certificates->DeleteAllItems ();
701 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
703 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
706 _certificates->InsertItem (item);
707 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
710 _certificates->SetItem (n, 0, _("Root"));
711 } else if (n == (certs.size() - 1)) {
712 _certificates->SetItem (n, 0, _("Leaf"));
714 _certificates->SetItem (n, 0, _("Intermediate"));
721 void remake_certificates ()
723 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
724 if (d->ShowModal () == wxID_OK) {
729 d->organisational_unit (),
730 d->root_common_name (),
731 d->intermediate_common_name (),
732 d->leaf_common_name ()
736 Config::instance()->set_signer (_signer);
737 update_certificate_list ();
738 update_signer_private_key ();
744 void update_sensitivity ()
746 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
749 void update_signer_private_key ()
751 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
754 void load_signer_private_key ()
756 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
758 if (d->ShowModal() == wxID_OK) {
760 boost::filesystem::path p (wx_to_std (d->GetPath ()));
761 if (boost::filesystem::file_size (p) > 1024) {
762 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
766 _signer->set_key (dcp::file_to_string (p));
767 Config::instance()->set_signer (_signer);
768 update_signer_private_key ();
769 } catch (dcp::MiscError& e) {
770 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
776 update_sensitivity ();
780 void load_decryption_certificate ()
782 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
784 if (d->ShowModal() == wxID_OK) {
786 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
787 Config::instance()->set_decryption_certificate (c);
788 update_decryption_certificate ();
789 } catch (dcp::MiscError& e) {
790 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
797 void update_decryption_certificate ()
799 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
802 void load_decryption_private_key ()
804 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
806 if (d->ShowModal() == wxID_OK) {
808 boost::filesystem::path p (wx_to_std (d->GetPath ()));
809 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
810 update_decryption_private_key ();
811 } catch (dcp::MiscError& e) {
812 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
819 void update_decryption_private_key ()
821 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
824 void export_decryption_certificate ()
826 wxFileDialog* d = new wxFileDialog (
827 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
828 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
831 if (d->ShowModal () == wxID_OK) {
832 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
834 throw OpenFileError (wx_to_std (d->GetPath ()));
837 string const s = Config::instance()->decryption_certificate().certificate (true);
838 fwrite (s.c_str(), 1, s.length(), f);
845 wxListCtrl* _certificates;
846 wxButton* _add_certificate;
847 wxButton* _remove_certificate;
848 wxButton* _remake_certificates;
849 wxStaticText* _signer_private_key;
850 wxButton* _load_signer_private_key;
851 wxStaticText* _decryption_certificate;
852 wxButton* _load_decryption_certificate;
853 wxStaticText* _decryption_private_key;
854 wxButton* _load_decryption_private_key;
855 wxButton* _export_decryption_certificate;
856 shared_ptr<dcp::Signer> _signer;
859 class TMSPage : public wxPreferencesPage, public Page
862 TMSPage (wxSize panel_size, int border)
863 : Page (panel_size, border)
866 wxString GetName () const
872 wxBitmap GetLargeIcon () const
874 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
878 wxWindow* CreateWindow (wxWindow* parent)
880 wxPanel* panel = make_panel (parent);
882 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
883 table->AddGrowableCol (1, 1);
884 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
886 add_label_to_sizer (table, panel, _("IP address"), true);
887 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
888 table->Add (_tms_ip, 1, wxEXPAND);
890 add_label_to_sizer (table, panel, _("Target path"), true);
891 _tms_path = new wxTextCtrl (panel, wxID_ANY);
892 table->Add (_tms_path, 1, wxEXPAND);
894 add_label_to_sizer (table, panel, _("User name"), true);
895 _tms_user = new wxTextCtrl (panel, wxID_ANY);
896 table->Add (_tms_user, 1, wxEXPAND);
898 add_label_to_sizer (table, panel, _("Password"), true);
899 _tms_password = new wxTextCtrl (panel, wxID_ANY);
900 table->Add (_tms_password, 1, wxEXPAND);
902 Config* config = Config::instance ();
904 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
905 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
906 _tms_path->SetValue (std_to_wx (config->tms_path ()));
907 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
908 _tms_user->SetValue (std_to_wx (config->tms_user ()));
909 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
910 _tms_password->SetValue (std_to_wx (config->tms_password ()));
911 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
917 void tms_ip_changed ()
919 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
922 void tms_path_changed ()
924 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
927 void tms_user_changed ()
929 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
932 void tms_password_changed ()
934 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
938 wxTextCtrl* _tms_path;
939 wxTextCtrl* _tms_user;
940 wxTextCtrl* _tms_password;
943 class KDMEmailPage : public wxPreferencesPage, public Page
947 KDMEmailPage (wxSize panel_size, int border)
948 : Page (panel_size, border)
951 wxString GetName () const
953 return _("KDM Email");
957 wxBitmap GetLargeIcon () const
959 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
963 wxWindow* CreateWindow (wxWindow* parent)
966 /* We have to force both width and height of this one */
967 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
969 wxPanel* panel = new wxPanel (parent);
971 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
974 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
975 table->AddGrowableCol (1, 1);
976 panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
978 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
979 _mail_server = new wxTextCtrl (panel, wxID_ANY);
980 table->Add (_mail_server, 1, wxEXPAND | wxALL);
982 add_label_to_sizer (table, panel, _("Mail user name"), true);
983 _mail_user = new wxTextCtrl (panel, wxID_ANY);
984 table->Add (_mail_user, 1, wxEXPAND | wxALL);
986 add_label_to_sizer (table, panel, _("Mail password"), true);
987 _mail_password = new wxTextCtrl (panel, wxID_ANY);
988 table->Add (_mail_password, 1, wxEXPAND | wxALL);
990 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
991 wxFont font = plain->GetFont();
992 font.SetStyle (wxFONTSTYLE_ITALIC);
993 font.SetPointSize (font.GetPointSize() - 1);
994 plain->SetFont (font);
995 table->AddSpacer (0);
997 add_label_to_sizer (table, panel, _("Subject"), true);
998 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
999 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1001 add_label_to_sizer (table, panel, _("From address"), true);
1002 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
1003 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1005 add_label_to_sizer (table, panel, _("CC address"), true);
1006 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
1007 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1009 add_label_to_sizer (table, panel, _("BCC address"), true);
1010 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
1011 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1013 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1014 panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1016 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1017 panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1019 Config* config = Config::instance ();
1020 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1021 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1022 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1023 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1024 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1025 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1026 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1027 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1028 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1029 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1030 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1031 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1032 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1033 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1034 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1035 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1036 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1042 void mail_server_changed ()
1044 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1047 void mail_user_changed ()
1049 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1052 void mail_password_changed ()
1054 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1057 void kdm_subject_changed ()
1059 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1062 void kdm_from_changed ()
1064 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1067 void kdm_cc_changed ()
1069 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1072 void kdm_bcc_changed ()
1074 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1077 void kdm_email_changed ()
1079 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1082 void reset_kdm_email ()
1084 Config::instance()->reset_kdm_email ();
1085 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1088 wxTextCtrl* _mail_server;
1089 wxTextCtrl* _mail_user;
1090 wxTextCtrl* _mail_password;
1091 wxTextCtrl* _kdm_subject;
1092 wxTextCtrl* _kdm_from;
1093 wxTextCtrl* _kdm_cc;
1094 wxTextCtrl* _kdm_bcc;
1095 wxTextCtrl* _kdm_email;
1096 wxButton* _reset_kdm_email;
1099 /** @class AdvancedPage
1100 * @brief Advanced page of the preferences dialog.
1102 class AdvancedPage : public wxStockPreferencesPage, public Page
1106 AdvancedPage (wxSize panel_size, int border)
1107 : wxStockPreferencesPage (Kind_Advanced)
1108 , Page (panel_size, border)
1111 wxWindow* CreateWindow (wxWindow* parent)
1113 wxPanel* panel = make_panel (parent);
1115 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1116 table->AddGrowableCol (1, 1);
1117 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1120 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1121 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1122 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1123 s->Add (_maximum_j2k_bandwidth, 1);
1124 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1128 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1129 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1130 table->AddSpacer (0);
1133 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log:"));
1134 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1136 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1137 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1141 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1142 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1143 t->Add (_log_general, 1, wxEXPAND | wxALL);
1144 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1145 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1146 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1147 t->Add (_log_error, 1, wxEXPAND | wxALL);
1148 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1149 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1150 table->Add (t, 0, wxALL, 6);
1153 #ifdef DCPOMATIC_WINDOWS
1154 _win32_console = new wxCheckBox (panel, wxID_ANY, _("Open console window"));
1155 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1156 table->AddSpacer (0);
1159 Config* config = Config::instance ();
1161 _maximum_j2k_bandwidth->SetRange (1, 1000);
1162 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1163 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1164 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1165 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1166 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1167 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1168 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1169 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1170 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1171 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1172 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1173 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1174 #ifdef DCPOMATIC_WINDOWS
1175 _win32_console->SetValue (config->win32_console());
1176 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1184 void maximum_j2k_bandwidth_changed ()
1186 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1189 void allow_any_dcp_frame_rate_changed ()
1191 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1197 if (_log_general->GetValue ()) {
1198 types |= Log::TYPE_GENERAL;
1200 if (_log_warning->GetValue ()) {
1201 types |= Log::TYPE_WARNING;
1203 if (_log_error->GetValue ()) {
1204 types |= Log::TYPE_ERROR;
1206 if (_log_timing->GetValue ()) {
1207 types |= Log::TYPE_TIMING;
1209 Config::instance()->set_log_types (types);
1212 #ifdef DCPOMATIC_WINDOWS
1213 void win32_console_changed ()
1215 Config::instance()->set_win32_console (_win32_console->GetValue ());
1219 wxSpinCtrl* _maximum_j2k_bandwidth;
1220 wxCheckBox* _allow_any_dcp_frame_rate;
1221 wxCheckBox* _log_general;
1222 wxCheckBox* _log_warning;
1223 wxCheckBox* _log_error;
1224 wxCheckBox* _log_timing;
1225 #ifdef DCPOMATIC_WINDOWS
1226 wxCheckBox* _win32_console;
1230 wxPreferencesEditor*
1231 create_config_dialog ()
1233 wxPreferencesEditor* e = new wxPreferencesEditor ();
1235 #ifdef DCPOMATIC_OSX
1236 /* Width that we force some of the config panels to be on OSX so that
1237 the containing window doesn't shrink too much when we select those panels.
1238 This is obviously an unpleasant hack.
1240 wxSize ps = wxSize (520, -1);
1241 int const border = 16;
1243 wxSize ps = wxSize (-1, -1);
1244 int const border = 8;
1247 e->AddPage (new GeneralPage (ps, border));
1248 e->AddPage (new DefaultsPage (ps, border));
1249 e->AddPage (new EncodingServersPage (ps, border));
1250 e->AddPage (new ColourConversionsPage (ps, border));
1251 e->AddPage (new KeysPage (ps, border));
1252 e->AddPage (new TMSPage (ps, border));
1253 e->AddPage (new KDMEmailPage (ps, border));
1254 e->AddPage (new AdvancedPage (ps, border));