2 Copyright (C) 2012-2015 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/filter.h"
37 #include "lib/dcp_content_type.h"
38 #include "lib/colour_conversion.h"
41 #include "lib/cross.h"
42 #include "lib/exceptions.h"
43 #include "config_dialog.h"
45 #include "editable_list.h"
46 #include "filter_dialog.h"
47 #include "dir_picker_ctrl.h"
48 #include "isdcf_metadata_dialog.h"
49 #include "preset_colour_conversion_dialog.h"
50 #include "server_dialog.h"
51 #include "make_signer_chain_dialog.h"
58 using boost::shared_ptr;
59 using boost::lexical_cast;
64 Page (wxSize panel_size, int border)
67 , _panel_size (panel_size)
70 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
74 wxWindow* create_window (wxWindow* parent)
76 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
77 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
91 virtual void config_changed () = 0;
92 virtual void setup () = 0;
94 void config_changed_wrapper ()
102 boost::signals2::scoped_connection _config_connection;
106 class StockPage : public wxStockPreferencesPage, public Page
109 StockPage (Kind kind, wxSize panel_size, int border)
110 : wxStockPreferencesPage (kind)
111 , Page (panel_size, border)
114 wxWindow* CreateWindow (wxWindow* parent)
116 return create_window (parent);
120 class StandardPage : public wxPreferencesPage, public Page
123 StandardPage (wxSize panel_size, int border)
124 : Page (panel_size, border)
127 wxWindow* CreateWindow (wxWindow* parent)
129 return create_window (parent);
133 class GeneralPage : public StockPage
136 GeneralPage (wxSize panel_size, int border)
137 : StockPage (Kind_General, panel_size, border)
143 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
144 table->AddGrowableCol (1, 1);
145 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
147 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
148 table->Add (_set_language, 1);
149 _language = new wxChoice (_panel, wxID_ANY);
150 _language->Append (wxT ("Deutsch"));
151 _language->Append (wxT ("English"));
152 _language->Append (wxT ("Español"));
153 _language->Append (wxT ("Français"));
154 _language->Append (wxT ("Italiano"));
155 _language->Append (wxT ("Nederlands"));
156 _language->Append (wxT ("Svenska"));
157 table->Add (_language);
159 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
160 wxFont font = restart->GetFont();
161 font.SetStyle (wxFONTSTYLE_ITALIC);
162 font.SetPointSize (font.GetPointSize() - 1);
163 restart->SetFont (font);
164 table->AddSpacer (0);
166 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
167 _num_local_encoding_threads = new wxSpinCtrl (_panel);
168 table->Add (_num_local_encoding_threads, 1);
170 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
171 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
172 table->AddSpacer (0);
174 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
175 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
176 table->AddSpacer (0);
178 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
179 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
181 _num_local_encoding_threads->SetRange (1, 128);
182 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
184 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
185 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
188 void config_changed ()
190 Config* config = Config::instance ();
192 _set_language->SetValue (config->language ());
194 if (config->language().get_value_or ("") == "fr") {
195 _language->SetSelection (3);
196 } else if (config->language().get_value_or ("") == "it") {
197 _language->SetSelection (4);
198 } else if (config->language().get_value_or ("") == "es") {
199 _language->SetSelection (2);
200 } else if (config->language().get_value_or ("") == "sv") {
201 _language->SetSelection (6);
202 } else if (config->language().get_value_or ("") == "de") {
203 _language->SetSelection (0);
204 } else if (config->language().get_value_or ("") == "nl") {
205 _language->SetSelection (5);
207 _language->SetSelection (1);
210 setup_language_sensitivity ();
212 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
213 _check_for_updates->SetValue (config->check_for_updates ());
214 _check_for_test_updates->SetValue (config->check_for_test_updates ());
217 void setup_language_sensitivity ()
219 _language->Enable (_set_language->GetValue ());
222 void set_language_changed ()
224 setup_language_sensitivity ();
225 if (_set_language->GetValue ()) {
228 Config::instance()->unset_language ();
232 void language_changed ()
234 switch (_language->GetSelection ()) {
236 Config::instance()->set_language ("de");
239 Config::instance()->set_language ("en");
242 Config::instance()->set_language ("es");
245 Config::instance()->set_language ("fr");
248 Config::instance()->set_language ("it");
251 Config::instance()->set_language ("nl");
254 Config::instance()->set_language ("sv");
259 void check_for_updates_changed ()
261 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
264 void check_for_test_updates_changed ()
266 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
269 void num_local_encoding_threads_changed ()
271 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
274 wxCheckBox* _set_language;
276 wxSpinCtrl* _num_local_encoding_threads;
277 wxCheckBox* _check_for_updates;
278 wxCheckBox* _check_for_test_updates;
281 class DefaultsPage : public StandardPage
284 DefaultsPage (wxSize panel_size, int border)
285 : StandardPage (panel_size, border)
288 wxString GetName () const
290 return _("Defaults");
294 wxBitmap GetLargeIcon () const
296 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
303 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
304 table->AddGrowableCol (1, 1);
305 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
308 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
309 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
310 _still_length = new wxSpinCtrl (_panel);
311 s->Add (_still_length);
312 add_label_to_sizer (s, _panel, _("s"), false);
316 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
317 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
318 _directory = new DirPickerCtrl (_panel);
320 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
322 table->Add (_directory, 1, wxEXPAND);
324 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
325 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
326 table->Add (_isdcf_metadata_button);
328 add_label_to_sizer (table, _panel, _("Default container"), true);
329 _container = new wxChoice (_panel, wxID_ANY);
330 table->Add (_container);
332 add_label_to_sizer (table, _panel, _("Default content type"), true);
333 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
334 table->Add (_dcp_content_type);
337 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
338 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
339 _j2k_bandwidth = new wxSpinCtrl (_panel);
340 s->Add (_j2k_bandwidth);
341 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
346 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
347 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
348 _audio_delay = new wxSpinCtrl (_panel);
349 s->Add (_audio_delay);
350 add_label_to_sizer (s, _panel, _("ms"), false);
354 add_label_to_sizer (table, _panel, _("Default issuer"), true);
355 _issuer = new wxTextCtrl (_panel, wxID_ANY);
356 table->Add (_issuer, 1, wxEXPAND);
358 _still_length->SetRange (1, 3600);
359 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
361 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
363 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
365 vector<Ratio const *> ratios = Ratio::all ();
366 for (size_t i = 0; i < ratios.size(); ++i) {
367 _container->Append (std_to_wx (ratios[i]->nickname ()));
370 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
372 vector<DCPContentType const *> const ct = DCPContentType::all ();
373 for (size_t i = 0; i < ct.size(); ++i) {
374 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
377 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
379 _j2k_bandwidth->SetRange (50, 250);
380 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
382 _audio_delay->SetRange (-1000, 1000);
383 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
385 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
388 void config_changed ()
390 Config* config = Config::instance ();
392 vector<Ratio const *> ratios = Ratio::all ();
393 for (size_t i = 0; i < ratios.size(); ++i) {
394 if (ratios[i] == config->default_container ()) {
395 _container->SetSelection (i);
399 vector<DCPContentType const *> const ct = DCPContentType::all ();
400 for (size_t i = 0; i < ct.size(); ++i) {
401 if (ct[i] == config->default_dcp_content_type ()) {
402 _dcp_content_type->SetSelection (i);
406 _still_length->SetValue (config->default_still_length ());
407 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
408 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
409 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
410 _audio_delay->SetValue (config->default_audio_delay ());
411 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
414 void j2k_bandwidth_changed ()
416 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
419 void audio_delay_changed ()
421 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
424 void directory_changed ()
426 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
429 void edit_isdcf_metadata_clicked ()
431 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
433 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
437 void still_length_changed ()
439 Config::instance()->set_default_still_length (_still_length->GetValue ());
442 void container_changed ()
444 vector<Ratio const *> ratio = Ratio::all ();
445 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
448 void dcp_content_type_changed ()
450 vector<DCPContentType const *> ct = DCPContentType::all ();
451 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
454 void issuer_changed ()
456 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
459 wxSpinCtrl* _j2k_bandwidth;
460 wxSpinCtrl* _audio_delay;
461 wxButton* _isdcf_metadata_button;
462 wxSpinCtrl* _still_length;
463 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
464 DirPickerCtrl* _directory;
466 wxDirPickerCtrl* _directory;
468 wxChoice* _container;
469 wxChoice* _dcp_content_type;
473 class EncodingServersPage : public StandardPage
476 EncodingServersPage (wxSize panel_size, int border)
477 : StandardPage (panel_size, border)
480 wxString GetName () const
486 wxBitmap GetLargeIcon () const
488 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
495 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
496 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
498 vector<string> columns;
499 columns.push_back (wx_to_std (_("IP address / host name")));
500 _servers_list = new EditableList<string, ServerDialog> (
503 boost::bind (&Config::servers, Config::instance()),
504 boost::bind (&Config::set_servers, Config::instance(), _1),
505 boost::bind (&EncodingServersPage::server_column, this, _1)
508 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
510 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
513 void config_changed ()
515 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
516 _servers_list->refresh ();
519 void use_any_servers_changed ()
521 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
524 string server_column (string s)
529 wxCheckBox* _use_any_servers;
530 EditableList<string, ServerDialog>* _servers_list;
533 class ColourConversionsPage : public StandardPage
536 ColourConversionsPage (wxSize panel_size, int border)
537 : StandardPage (panel_size, border)
540 wxString GetName () const
542 return _("Colour Conversions");
546 wxBitmap GetLargeIcon () const
548 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
555 vector<string> columns;
556 columns.push_back (wx_to_std (_("Name")));
557 _list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
560 boost::bind (&Config::colour_conversions, Config::instance()),
561 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
562 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
566 _panel->GetSizer()->Add (_list, 1, wxEXPAND | wxALL, _border);
569 void config_changed ()
574 string colour_conversion_column (PresetColourConversion c)
579 EditableList<PresetColourConversion, PresetColourConversionDialog>* _list;
582 class KeysPage : public StandardPage
585 KeysPage (wxSize panel_size, int border)
586 : StandardPage (panel_size, border)
589 wxString GetName () const
595 wxBitmap GetLargeIcon () const
597 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
604 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
605 _panel->GetSizer()->Add (m, 0, wxALL, _border);
607 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
608 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
610 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
615 ip.SetText (_("Type"));
617 _certificates->InsertColumn (0, ip);
623 ip.SetText (_("Thumbprint"));
626 wxFont font = ip.GetFont ();
627 font.SetFamily (wxFONTFAMILY_TELETYPE);
630 _certificates->InsertColumn (1, ip);
633 certificates_sizer->Add (_certificates, 1, wxEXPAND);
636 wxSizer* s = new wxBoxSizer (wxVERTICAL);
637 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
638 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
639 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
640 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
641 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
644 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
645 table->AddGrowableCol (1, 1);
646 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
648 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
649 table->Add (_remake_certificates, 0);
650 table->AddSpacer (0);
652 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
654 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
655 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
656 wxFont font = _signer_private_key->GetFont ();
657 font.SetFamily (wxFONTFAMILY_TELETYPE);
658 _signer_private_key->SetFont (font);
659 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
660 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
661 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
665 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
667 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
668 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
669 wxFont font = _decryption_certificate->GetFont ();
670 font.SetFamily (wxFONTFAMILY_TELETYPE);
671 _decryption_certificate->SetFont (font);
672 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
673 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
674 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
678 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
680 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
681 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
682 wxFont font = _decryption_private_key->GetFont ();
683 font.SetFamily (wxFONTFAMILY_TELETYPE);
684 _decryption_private_key->SetFont (font);
685 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
686 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
687 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
691 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
692 table->Add (_export_decryption_certificate);
693 table->AddSpacer (0);
695 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
696 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
697 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
698 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
699 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
700 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
701 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
702 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
703 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
706 void config_changed ()
708 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
710 update_certificate_list ();
711 update_signer_private_key ();
712 update_decryption_certificate ();
713 update_decryption_private_key ();
714 update_sensitivity ();
717 void add_certificate ()
719 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
721 if (d->ShowModal() == wxID_OK) {
723 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
724 _signer->certificates().add (c);
725 Config::instance()->set_signer (_signer);
726 update_certificate_list ();
727 } catch (dcp::MiscError& e) {
728 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
734 update_sensitivity ();
737 void remove_certificate ()
739 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
744 _certificates->DeleteItem (i);
745 _signer->certificates().remove (i);
746 Config::instance()->set_signer (_signer);
748 update_sensitivity ();
751 void update_certificate_list ()
753 _certificates->DeleteAllItems ();
754 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
756 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
759 _certificates->InsertItem (item);
760 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
763 _certificates->SetItem (n, 0, _("Root"));
764 } else if (n == (certs.size() - 1)) {
765 _certificates->SetItem (n, 0, _("Leaf"));
767 _certificates->SetItem (n, 0, _("Intermediate"));
774 void remake_certificates ()
776 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
777 if (d->ShowModal () == wxID_OK) {
782 d->organisational_unit (),
783 d->root_common_name (),
784 d->intermediate_common_name (),
785 d->leaf_common_name ()
789 Config::instance()->set_signer (_signer);
790 update_certificate_list ();
791 update_signer_private_key ();
797 void update_sensitivity ()
799 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
802 void update_signer_private_key ()
804 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
807 void load_signer_private_key ()
809 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
811 if (d->ShowModal() == wxID_OK) {
813 boost::filesystem::path p (wx_to_std (d->GetPath ()));
814 if (boost::filesystem::file_size (p) > 1024) {
815 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
819 _signer->set_key (dcp::file_to_string (p));
820 Config::instance()->set_signer (_signer);
821 update_signer_private_key ();
822 } catch (dcp::MiscError& e) {
823 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
829 update_sensitivity ();
833 void load_decryption_certificate ()
835 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
837 if (d->ShowModal() == wxID_OK) {
839 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
840 Config::instance()->set_decryption_certificate (c);
841 update_decryption_certificate ();
842 } catch (dcp::MiscError& e) {
843 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
850 void update_decryption_certificate ()
852 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
855 void load_decryption_private_key ()
857 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
859 if (d->ShowModal() == wxID_OK) {
861 boost::filesystem::path p (wx_to_std (d->GetPath ()));
862 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
863 update_decryption_private_key ();
864 } catch (dcp::MiscError& e) {
865 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
872 void update_decryption_private_key ()
874 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
877 void export_decryption_certificate ()
879 wxFileDialog* d = new wxFileDialog (
880 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
881 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
884 if (d->ShowModal () == wxID_OK) {
885 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
887 throw OpenFileError (wx_to_std (d->GetPath ()));
890 string const s = Config::instance()->decryption_certificate().certificate (true);
891 fwrite (s.c_str(), 1, s.length(), f);
897 wxListCtrl* _certificates;
898 wxButton* _add_certificate;
899 wxButton* _remove_certificate;
900 wxButton* _remake_certificates;
901 wxStaticText* _signer_private_key;
902 wxButton* _load_signer_private_key;
903 wxStaticText* _decryption_certificate;
904 wxButton* _load_decryption_certificate;
905 wxStaticText* _decryption_private_key;
906 wxButton* _load_decryption_private_key;
907 wxButton* _export_decryption_certificate;
908 shared_ptr<dcp::Signer> _signer;
911 class TMSPage : public StandardPage
914 TMSPage (wxSize panel_size, int border)
915 : StandardPage (panel_size, border)
918 wxString GetName () const
924 wxBitmap GetLargeIcon () const
926 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
933 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
934 table->AddGrowableCol (1, 1);
935 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
937 add_label_to_sizer (table, _panel, _("IP address"), true);
938 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
939 table->Add (_tms_ip, 1, wxEXPAND);
941 add_label_to_sizer (table, _panel, _("Target path"), true);
942 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
943 table->Add (_tms_path, 1, wxEXPAND);
945 add_label_to_sizer (table, _panel, _("User name"), true);
946 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
947 table->Add (_tms_user, 1, wxEXPAND);
949 add_label_to_sizer (table, _panel, _("Password"), true);
950 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
951 table->Add (_tms_password, 1, wxEXPAND);
953 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
954 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
955 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
956 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
959 void config_changed ()
961 Config* config = Config::instance ();
963 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
964 _tms_path->SetValue (std_to_wx (config->tms_path ()));
965 _tms_user->SetValue (std_to_wx (config->tms_user ()));
966 _tms_password->SetValue (std_to_wx (config->tms_password ()));
969 void tms_ip_changed ()
971 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
974 void tms_path_changed ()
976 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
979 void tms_user_changed ()
981 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
984 void tms_password_changed ()
986 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
990 wxTextCtrl* _tms_path;
991 wxTextCtrl* _tms_user;
992 wxTextCtrl* _tms_password;
995 class KDMEmailPage : public StandardPage
999 KDMEmailPage (wxSize panel_size, int border)
1000 #ifdef DCPOMATIC_OSX
1001 /* We have to force both width and height of this one */
1002 : StandardPage (wxSize (480, 128), border)
1004 : StandardPage (panel_size, border)
1008 wxString GetName () const
1010 return _("KDM Email");
1013 #ifdef DCPOMATIC_OSX
1014 wxBitmap GetLargeIcon () const
1016 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1023 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1024 table->AddGrowableCol (1, 1);
1025 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1027 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1028 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1029 table->Add (_mail_server, 1, wxEXPAND | wxALL);
1031 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1032 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1033 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1035 add_label_to_sizer (table, _panel, _("Mail password"), true);
1036 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1037 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1039 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
1040 wxFont font = plain->GetFont();
1041 font.SetStyle (wxFONTSTYLE_ITALIC);
1042 font.SetPointSize (font.GetPointSize() - 1);
1043 plain->SetFont (font);
1044 table->AddSpacer (0);
1046 add_label_to_sizer (table, _panel, _("Subject"), true);
1047 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1048 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1050 add_label_to_sizer (table, _panel, _("From address"), true);
1051 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1052 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1054 add_label_to_sizer (table, _panel, _("CC address"), true);
1055 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1056 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1058 add_label_to_sizer (table, _panel, _("BCC address"), true);
1059 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1060 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1062 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1063 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1065 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1066 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1068 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1069 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1070 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1071 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1072 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1073 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1074 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1075 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1076 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1079 void config_changed ()
1081 Config* config = Config::instance ();
1083 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1084 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1085 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1086 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1087 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1088 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1089 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1090 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1093 void mail_server_changed ()
1095 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1098 void mail_user_changed ()
1100 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1103 void mail_password_changed ()
1105 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1108 void kdm_subject_changed ()
1110 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1113 void kdm_from_changed ()
1115 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1118 void kdm_cc_changed ()
1120 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1123 void kdm_bcc_changed ()
1125 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1128 void kdm_email_changed ()
1130 if (_kdm_email->GetValue().IsEmpty ()) {
1131 /* Sometimes we get sent an erroneous notification that the email
1132 is empty; I don't know why.
1136 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1139 void reset_kdm_email ()
1141 Config::instance()->reset_kdm_email ();
1142 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1145 wxTextCtrl* _mail_server;
1146 wxTextCtrl* _mail_user;
1147 wxTextCtrl* _mail_password;
1148 wxTextCtrl* _kdm_subject;
1149 wxTextCtrl* _kdm_from;
1150 wxTextCtrl* _kdm_cc;
1151 wxTextCtrl* _kdm_bcc;
1152 wxTextCtrl* _kdm_email;
1153 wxButton* _reset_kdm_email;
1156 /** @class AdvancedPage
1157 * @brief Advanced page of the preferences dialog.
1159 class AdvancedPage : public StockPage
1162 AdvancedPage (wxSize panel_size, int border)
1163 : StockPage (Kind_Advanced, panel_size, border)
1164 , _maximum_j2k_bandwidth (0)
1165 , _allow_any_dcp_frame_rate (0)
1175 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1176 table->AddGrowableCol (1, 1);
1177 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1180 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1181 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1182 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1183 s->Add (_maximum_j2k_bandwidth, 1);
1184 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1188 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1189 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1190 table->AddSpacer (0);
1193 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1194 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1196 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1197 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1201 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1202 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1203 t->Add (_log_general, 1, wxEXPAND | wxALL);
1204 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1205 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1206 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1207 t->Add (_log_error, 1, wxEXPAND | wxALL);
1208 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1209 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1210 table->Add (t, 0, wxALL, 6);
1213 #ifdef DCPOMATIC_WINDOWS
1214 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1215 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1216 table->AddSpacer (0);
1219 _maximum_j2k_bandwidth->SetRange (1, 1000);
1220 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1221 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1222 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1223 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1224 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1225 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1226 #ifdef DCPOMATIC_WINDOWS
1227 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1231 void config_changed ()
1233 Config* config = Config::instance ();
1235 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1236 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1237 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1238 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1239 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1240 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1241 #ifdef DCPOMATIC_WINDOWS
1242 _win32_console->SetValue (config->win32_console());
1246 void maximum_j2k_bandwidth_changed ()
1248 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1251 void allow_any_dcp_frame_rate_changed ()
1253 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1259 if (_log_general->GetValue ()) {
1260 types |= Log::TYPE_GENERAL;
1262 if (_log_warning->GetValue ()) {
1263 types |= Log::TYPE_WARNING;
1265 if (_log_error->GetValue ()) {
1266 types |= Log::TYPE_ERROR;
1268 if (_log_timing->GetValue ()) {
1269 types |= Log::TYPE_TIMING;
1271 Config::instance()->set_log_types (types);
1274 #ifdef DCPOMATIC_WINDOWS
1275 void win32_console_changed ()
1277 Config::instance()->set_win32_console (_win32_console->GetValue ());
1281 wxSpinCtrl* _maximum_j2k_bandwidth;
1282 wxCheckBox* _allow_any_dcp_frame_rate;
1283 wxCheckBox* _log_general;
1284 wxCheckBox* _log_warning;
1285 wxCheckBox* _log_error;
1286 wxCheckBox* _log_timing;
1287 #ifdef DCPOMATIC_WINDOWS
1288 wxCheckBox* _win32_console;
1292 wxPreferencesEditor*
1293 create_config_dialog ()
1295 wxPreferencesEditor* e = new wxPreferencesEditor ();
1297 #ifdef DCPOMATIC_OSX
1298 /* Width that we force some of the config panels to be on OSX so that
1299 the containing window doesn't shrink too much when we select those panels.
1300 This is obviously an unpleasant hack.
1302 wxSize ps = wxSize (520, -1);
1303 int const border = 16;
1305 wxSize ps = wxSize (-1, -1);
1306 int const border = 8;
1309 e->AddPage (new GeneralPage (ps, border));
1310 e->AddPage (new DefaultsPage (ps, border));
1311 e->AddPage (new EncodingServersPage (ps, border));
1312 e->AddPage (new ColourConversionsPage (ps, border));
1313 e->AddPage (new KeysPage (ps, border));
1314 e->AddPage (new TMSPage (ps, border));
1315 e->AddPage (new KDMEmailPage (ps, border));
1316 e->AddPage (new AdvancedPage (ps, border));