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"
58 using boost::shared_ptr;
59 using boost::lexical_cast;
64 Page (wxSize panel_size, int border)
65 : _panel_size (panel_size)
70 wxPanel* make_panel (wxWindow* parent)
72 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
73 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
82 class GeneralPage : public wxStockPreferencesPage, public Page
85 GeneralPage (wxSize panel_size, int border)
86 : wxStockPreferencesPage (Kind_General)
87 , Page (panel_size, border)
90 wxWindow* CreateWindow (wxWindow* parent)
92 wxPanel* panel = make_panel (parent);
94 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
95 table->AddGrowableCol (1, 1);
96 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
98 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
99 table->Add (_set_language, 1);
100 _language = new wxChoice (panel, wxID_ANY);
101 _language->Append (wxT ("Deutsch"));
102 _language->Append (wxT ("English"));
103 _language->Append (wxT ("Español"));
104 _language->Append (wxT ("Français"));
105 _language->Append (wxT ("Italiano"));
106 _language->Append (wxT ("Nederlands"));
107 _language->Append (wxT ("Svenska"));
108 table->Add (_language);
110 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
111 wxFont font = restart->GetFont();
112 font.SetStyle (wxFONTSTYLE_ITALIC);
113 font.SetPointSize (font.GetPointSize() - 1);
114 restart->SetFont (font);
115 table->AddSpacer (0);
117 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
118 _num_local_encoding_threads = new wxSpinCtrl (panel);
119 table->Add (_num_local_encoding_threads, 1);
121 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
122 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
123 table->AddSpacer (0);
125 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
126 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
127 table->AddSpacer (0);
129 Config* config = Config::instance ();
131 _set_language->SetValue (config->language ());
133 if (config->language().get_value_or ("") == "fr") {
134 _language->SetSelection (3);
135 } else if (config->language().get_value_or ("") == "it") {
136 _language->SetSelection (4);
137 } else if (config->language().get_value_or ("") == "es") {
138 _language->SetSelection (2);
139 } else if (config->language().get_value_or ("") == "sv") {
140 _language->SetSelection (6);
141 } else if (config->language().get_value_or ("") == "de") {
142 _language->SetSelection (0);
143 } else if (config->language().get_value_or ("") == "nl") {
144 _language->SetSelection (5);
146 _language->SetSelection (1);
149 setup_language_sensitivity ();
151 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
152 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
154 _num_local_encoding_threads->SetRange (1, 128);
155 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
156 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
158 _check_for_updates->SetValue (config->check_for_updates ());
159 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
160 _check_for_test_updates->SetValue (config->check_for_test_updates ());
161 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
167 void setup_language_sensitivity ()
169 _language->Enable (_set_language->GetValue ());
172 void set_language_changed ()
174 setup_language_sensitivity ();
175 if (_set_language->GetValue ()) {
178 Config::instance()->unset_language ();
182 void language_changed ()
184 switch (_language->GetSelection ()) {
186 Config::instance()->set_language ("de");
189 Config::instance()->set_language ("en");
192 Config::instance()->set_language ("es");
195 Config::instance()->set_language ("fr");
198 Config::instance()->set_language ("it");
201 Config::instance()->set_language ("nl");
204 Config::instance()->set_language ("sv");
209 void check_for_updates_changed ()
211 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
214 void check_for_test_updates_changed ()
216 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
219 void num_local_encoding_threads_changed ()
221 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
224 wxCheckBox* _set_language;
226 wxSpinCtrl* _num_local_encoding_threads;
227 wxCheckBox* _check_for_updates;
228 wxCheckBox* _check_for_test_updates;
231 class DefaultsPage : public wxPreferencesPage, public Page
234 DefaultsPage (wxSize panel_size, int border)
235 : Page (panel_size, border)
238 wxString GetName () const
240 return _("Defaults");
244 wxBitmap GetLargeIcon () const
246 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
250 wxWindow* CreateWindow (wxWindow* parent)
252 wxPanel* panel = make_panel (parent);
254 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
255 table->AddGrowableCol (1, 1);
256 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
259 add_label_to_sizer (table, panel, _("Default duration of still images"), true);
260 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
261 _still_length = new wxSpinCtrl (panel);
262 s->Add (_still_length);
263 add_label_to_sizer (s, panel, _("s"), false);
267 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
268 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
269 _directory = new DirPickerCtrl (panel);
271 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
273 table->Add (_directory, 1, wxEXPAND);
275 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
276 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
277 table->Add (_isdcf_metadata_button);
279 add_label_to_sizer (table, panel, _("Default scale to"), true);
280 _scale = new wxChoice (panel, wxID_ANY);
283 add_label_to_sizer (table, panel, _("Default container"), true);
284 _container = new wxChoice (panel, wxID_ANY);
285 table->Add (_container);
287 add_label_to_sizer (table, panel, _("Default content type"), true);
288 _dcp_content_type = new wxChoice (panel, wxID_ANY);
289 table->Add (_dcp_content_type);
292 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
293 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
294 _j2k_bandwidth = new wxSpinCtrl (panel);
295 s->Add (_j2k_bandwidth);
296 add_label_to_sizer (s, panel, _("Mbit/s"), false);
301 add_label_to_sizer (table, panel, _("Default audio delay"), true);
302 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
303 _audio_delay = new wxSpinCtrl (panel);
304 s->Add (_audio_delay);
305 add_label_to_sizer (s, panel, _("ms"), false);
309 add_label_to_sizer (table, panel, _("Default issuer"), true);
310 _issuer = new wxTextCtrl (panel, wxID_ANY);
311 table->Add (_issuer, 1, wxEXPAND);
313 Config* config = Config::instance ();
315 _still_length->SetRange (1, 3600);
316 _still_length->SetValue (config->default_still_length ());
317 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
319 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
320 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
322 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
324 vector<VideoContentScale> scales = VideoContentScale::all ();
325 for (size_t i = 0; i < scales.size(); ++i) {
326 _scale->Append (std_to_wx (scales[i].name ()));
327 if (scales[i] == config->default_scale ()) {
328 _scale->SetSelection (i);
332 vector<Ratio const *> ratios = Ratio::all ();
333 for (size_t i = 0; i < ratios.size(); ++i) {
334 _container->Append (std_to_wx (ratios[i]->nickname ()));
335 if (ratios[i] == config->default_container ()) {
336 _container->SetSelection (i);
340 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
341 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
343 vector<DCPContentType const *> const ct = DCPContentType::all ();
344 for (size_t i = 0; i < ct.size(); ++i) {
345 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
346 if (ct[i] == config->default_dcp_content_type ()) {
347 _dcp_content_type->SetSelection (i);
351 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
353 _j2k_bandwidth->SetRange (50, 250);
354 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
355 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
357 _audio_delay->SetRange (-1000, 1000);
358 _audio_delay->SetValue (config->default_audio_delay ());
359 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
361 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
362 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
368 void j2k_bandwidth_changed ()
370 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
373 void audio_delay_changed ()
375 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
378 void directory_changed ()
380 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
383 void edit_isdcf_metadata_clicked (wxWindow* parent)
385 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
387 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
391 void still_length_changed ()
393 Config::instance()->set_default_still_length (_still_length->GetValue ());
396 void scale_changed ()
398 vector<VideoContentScale> scale = VideoContentScale::all ();
399 Config::instance()->set_default_scale (scale[_scale->GetSelection()]);
402 void container_changed ()
404 vector<Ratio const *> ratio = Ratio::all ();
405 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
408 void dcp_content_type_changed ()
410 vector<DCPContentType const *> ct = DCPContentType::all ();
411 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
414 void issuer_changed ()
416 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
419 wxSpinCtrl* _j2k_bandwidth;
420 wxSpinCtrl* _audio_delay;
421 wxButton* _isdcf_metadata_button;
422 wxSpinCtrl* _still_length;
423 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
424 DirPickerCtrl* _directory;
426 wxDirPickerCtrl* _directory;
429 wxChoice* _container;
430 wxChoice* _dcp_content_type;
434 class EncodingServersPage : public wxPreferencesPage, public Page
437 EncodingServersPage (wxSize panel_size, int border)
438 : Page (panel_size, border)
441 wxString GetName () const
447 wxBitmap GetLargeIcon () const
449 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
453 wxWindow* CreateWindow (wxWindow* parent)
455 wxPanel* panel = make_panel (parent);
457 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
458 panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
460 vector<string> columns;
461 columns.push_back (wx_to_std (_("IP address / host name")));
462 _servers_list = new EditableList<string, ServerDialog> (
465 boost::bind (&Config::servers, Config::instance()),
466 boost::bind (&Config::set_servers, Config::instance(), _1),
467 boost::bind (&EncodingServersPage::server_column, this, _1)
470 panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
472 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
473 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
480 void use_any_servers_changed ()
482 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
485 string server_column (string s)
490 wxCheckBox* _use_any_servers;
491 EditableList<string, ServerDialog>* _servers_list;
494 class ColourConversionsPage : public wxPreferencesPage, public Page
497 ColourConversionsPage (wxSize panel_size, int border)
498 : Page (panel_size, border)
501 wxString GetName () const
503 return _("Colour Conversions");
507 wxBitmap GetLargeIcon () const
509 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
512 wxWindow* CreateWindow (wxWindow* parent)
514 wxPanel* panel = make_panel (parent);
516 vector<string> columns;
517 columns.push_back (wx_to_std (_("Name")));
518 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
521 boost::bind (&Config::colour_conversions, Config::instance()),
522 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
523 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
527 panel->GetSizer()->Add (list, 1, wxEXPAND | wxALL, _border);
532 string colour_conversion_column (PresetColourConversion c)
538 class KeysPage : public wxPreferencesPage, public Page
541 KeysPage (wxSize panel_size, int border)
542 : Page (panel_size, border)
545 wxString GetName () const
551 wxBitmap GetLargeIcon () const
553 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
557 wxWindow* CreateWindow (wxWindow* parent)
559 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
560 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
561 _panel->SetSizer (overall_sizer);
563 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
564 overall_sizer->Add (m, 0, wxALL, _border);
566 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
567 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
569 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
574 ip.SetText (_("Type"));
576 _certificates->InsertColumn (0, ip);
582 ip.SetText (_("Thumbprint"));
585 wxFont font = ip.GetFont ();
586 font.SetFamily (wxFONTFAMILY_TELETYPE);
589 _certificates->InsertColumn (1, ip);
592 certificates_sizer->Add (_certificates, 1, wxEXPAND);
595 wxSizer* s = new wxBoxSizer (wxVERTICAL);
596 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
597 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
598 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
599 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
600 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
603 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
604 table->AddGrowableCol (1, 1);
605 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
607 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
609 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
610 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
611 wxFont font = _signer_private_key->GetFont ();
612 font.SetFamily (wxFONTFAMILY_TELETYPE);
613 _signer_private_key->SetFont (font);
614 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
615 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
616 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
620 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
622 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
623 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
624 wxFont font = _decryption_certificate->GetFont ();
625 font.SetFamily (wxFONTFAMILY_TELETYPE);
626 _decryption_certificate->SetFont (font);
627 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
628 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
629 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
633 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
635 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
636 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
637 wxFont font = _decryption_private_key->GetFont ();
638 font.SetFamily (wxFONTFAMILY_TELETYPE);
639 _decryption_private_key->SetFont (font);
640 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
641 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
642 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
646 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
647 table->Add (_export_decryption_certificate);
648 table->AddSpacer (0);
650 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
651 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
652 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
653 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
654 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
655 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
656 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
657 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
659 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
661 update_certificate_list ();
662 update_signer_private_key ();
663 update_decryption_certificate ();
664 update_decryption_private_key ();
665 update_sensitivity ();
671 void add_certificate ()
673 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
675 if (d->ShowModal() == wxID_OK) {
677 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
678 _signer->certificates().add (c);
679 Config::instance()->set_signer (_signer);
680 update_certificate_list ();
681 } catch (dcp::MiscError& e) {
682 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
688 update_sensitivity ();
691 void remove_certificate ()
693 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
698 _certificates->DeleteItem (i);
699 _signer->certificates().remove (i);
700 Config::instance()->set_signer (_signer);
702 update_sensitivity ();
705 void update_certificate_list ()
707 _certificates->DeleteAllItems ();
708 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
710 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
713 _certificates->InsertItem (item);
714 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
717 _certificates->SetItem (n, 0, _("Root"));
718 } else if (n == (certs.size() - 1)) {
719 _certificates->SetItem (n, 0, _("Leaf"));
721 _certificates->SetItem (n, 0, _("Intermediate"));
728 void update_sensitivity ()
730 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
733 void update_signer_private_key ()
735 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
738 void load_signer_private_key ()
740 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
742 if (d->ShowModal() == wxID_OK) {
744 boost::filesystem::path p (wx_to_std (d->GetPath ()));
745 if (boost::filesystem::file_size (p) > 1024) {
746 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
750 _signer->set_key (dcp::file_to_string (p));
751 Config::instance()->set_signer (_signer);
752 update_signer_private_key ();
753 } catch (dcp::MiscError& e) {
754 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
760 update_sensitivity ();
764 void load_decryption_certificate ()
766 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
768 if (d->ShowModal() == wxID_OK) {
770 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
771 Config::instance()->set_decryption_certificate (c);
772 update_decryption_certificate ();
773 } catch (dcp::MiscError& e) {
774 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
781 void update_decryption_certificate ()
783 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
786 void load_decryption_private_key ()
788 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
790 if (d->ShowModal() == wxID_OK) {
792 boost::filesystem::path p (wx_to_std (d->GetPath ()));
793 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
794 update_decryption_private_key ();
795 } catch (dcp::MiscError& e) {
796 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
803 void update_decryption_private_key ()
805 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
808 void export_decryption_certificate ()
810 wxFileDialog* d = new wxFileDialog (
811 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
812 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
815 if (d->ShowModal () == wxID_OK) {
816 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
818 throw OpenFileError (wx_to_std (d->GetPath ()));
821 string const s = Config::instance()->decryption_certificate().certificate (true);
822 fwrite (s.c_str(), 1, s.length(), f);
829 wxListCtrl* _certificates;
830 wxButton* _add_certificate;
831 wxButton* _remove_certificate;
832 wxStaticText* _signer_private_key;
833 wxButton* _load_signer_private_key;
834 wxStaticText* _decryption_certificate;
835 wxButton* _load_decryption_certificate;
836 wxStaticText* _decryption_private_key;
837 wxButton* _load_decryption_private_key;
838 wxButton* _export_decryption_certificate;
839 shared_ptr<dcp::Signer> _signer;
842 class TMSPage : public wxPreferencesPage, public Page
845 TMSPage (wxSize panel_size, int border)
846 : Page (panel_size, border)
849 wxString GetName () const
855 wxBitmap GetLargeIcon () const
857 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
861 wxWindow* CreateWindow (wxWindow* parent)
863 wxPanel* panel = make_panel (parent);
865 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
866 table->AddGrowableCol (1, 1);
867 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
869 add_label_to_sizer (table, panel, _("IP address"), true);
870 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
871 table->Add (_tms_ip, 1, wxEXPAND);
873 add_label_to_sizer (table, panel, _("Target path"), true);
874 _tms_path = new wxTextCtrl (panel, wxID_ANY);
875 table->Add (_tms_path, 1, wxEXPAND);
877 add_label_to_sizer (table, panel, _("User name"), true);
878 _tms_user = new wxTextCtrl (panel, wxID_ANY);
879 table->Add (_tms_user, 1, wxEXPAND);
881 add_label_to_sizer (table, panel, _("Password"), true);
882 _tms_password = new wxTextCtrl (panel, wxID_ANY);
883 table->Add (_tms_password, 1, wxEXPAND);
885 Config* config = Config::instance ();
887 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
888 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
889 _tms_path->SetValue (std_to_wx (config->tms_path ()));
890 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
891 _tms_user->SetValue (std_to_wx (config->tms_user ()));
892 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
893 _tms_password->SetValue (std_to_wx (config->tms_password ()));
894 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
900 void tms_ip_changed ()
902 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
905 void tms_path_changed ()
907 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
910 void tms_user_changed ()
912 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
915 void tms_password_changed ()
917 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
921 wxTextCtrl* _tms_path;
922 wxTextCtrl* _tms_user;
923 wxTextCtrl* _tms_password;
926 class KDMEmailPage : public wxPreferencesPage, public Page
930 KDMEmailPage (wxSize panel_size, int border)
931 : Page (panel_size, border)
934 wxString GetName () const
936 return _("KDM Email");
940 wxBitmap GetLargeIcon () const
942 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
946 wxWindow* CreateWindow (wxWindow* parent)
949 /* We have to force both width and height of this one */
950 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
952 wxPanel* panel = new wxPanel (parent);
954 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
957 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
958 table->AddGrowableCol (1, 1);
959 panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
961 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
962 _mail_server = new wxTextCtrl (panel, wxID_ANY);
963 table->Add (_mail_server, 1, wxEXPAND | wxALL);
965 add_label_to_sizer (table, panel, _("Mail user name"), true);
966 _mail_user = new wxTextCtrl (panel, wxID_ANY);
967 table->Add (_mail_user, 1, wxEXPAND | wxALL);
969 add_label_to_sizer (table, panel, _("Mail password"), true);
970 _mail_password = new wxTextCtrl (panel, wxID_ANY);
971 table->Add (_mail_password, 1, wxEXPAND | wxALL);
973 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
974 wxFont font = plain->GetFont();
975 font.SetStyle (wxFONTSTYLE_ITALIC);
976 font.SetPointSize (font.GetPointSize() - 1);
977 plain->SetFont (font);
978 table->AddSpacer (0);
980 add_label_to_sizer (table, panel, _("Subject"), true);
981 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
982 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
984 add_label_to_sizer (table, panel, _("From address"), true);
985 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
986 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
988 add_label_to_sizer (table, panel, _("CC address"), true);
989 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
990 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
992 add_label_to_sizer (table, panel, _("BCC address"), true);
993 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
994 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
996 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
997 panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
999 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1000 panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1002 Config* config = Config::instance ();
1003 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1004 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1005 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1006 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1007 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1008 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1009 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1010 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1011 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1012 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1013 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1014 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1015 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1016 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1017 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1018 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1019 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1025 void mail_server_changed ()
1027 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1030 void mail_user_changed ()
1032 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1035 void mail_password_changed ()
1037 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1040 void kdm_subject_changed ()
1042 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1045 void kdm_from_changed ()
1047 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1050 void kdm_cc_changed ()
1052 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1055 void kdm_bcc_changed ()
1057 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1060 void kdm_email_changed ()
1062 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1065 void reset_kdm_email ()
1067 Config::instance()->reset_kdm_email ();
1068 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1071 wxTextCtrl* _mail_server;
1072 wxTextCtrl* _mail_user;
1073 wxTextCtrl* _mail_password;
1074 wxTextCtrl* _kdm_subject;
1075 wxTextCtrl* _kdm_from;
1076 wxTextCtrl* _kdm_cc;
1077 wxTextCtrl* _kdm_bcc;
1078 wxTextCtrl* _kdm_email;
1079 wxButton* _reset_kdm_email;
1082 /** @class AdvancedPage
1083 * @brief Advanced page of the preferences dialog.
1085 class AdvancedPage : public wxStockPreferencesPage, public Page
1089 AdvancedPage (wxSize panel_size, int border)
1090 : wxStockPreferencesPage (Kind_Advanced)
1091 , Page (panel_size, border)
1094 wxWindow* CreateWindow (wxWindow* parent)
1096 wxPanel* panel = make_panel (parent);
1098 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1099 table->AddGrowableCol (1, 1);
1100 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1103 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1104 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1105 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1106 s->Add (_maximum_j2k_bandwidth, 1);
1107 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1111 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1112 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1113 table->AddSpacer (0);
1116 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log:"));
1117 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1119 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1120 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1124 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1125 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1126 t->Add (_log_general, 1, wxEXPAND | wxALL);
1127 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1128 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1129 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1130 t->Add (_log_error, 1, wxEXPAND | wxALL);
1131 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1132 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1133 table->Add (t, 0, wxALL, 6);
1136 Config* config = Config::instance ();
1138 _maximum_j2k_bandwidth->SetRange (1, 500);
1139 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1140 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1141 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1142 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1143 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1144 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1145 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1146 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1147 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1148 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1149 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1150 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1157 void maximum_j2k_bandwidth_changed ()
1159 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1162 void allow_any_dcp_frame_rate_changed ()
1164 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1170 if (_log_general->GetValue ()) {
1171 types |= Log::TYPE_GENERAL;
1173 if (_log_warning->GetValue ()) {
1174 types |= Log::TYPE_WARNING;
1176 if (_log_error->GetValue ()) {
1177 types |= Log::TYPE_ERROR;
1179 if (_log_timing->GetValue ()) {
1180 types |= Log::TYPE_TIMING;
1182 Config::instance()->set_log_types (types);
1185 wxSpinCtrl* _maximum_j2k_bandwidth;
1186 wxCheckBox* _allow_any_dcp_frame_rate;
1187 wxCheckBox* _log_general;
1188 wxCheckBox* _log_warning;
1189 wxCheckBox* _log_error;
1190 wxCheckBox* _log_timing;
1193 wxPreferencesEditor*
1194 create_config_dialog ()
1196 wxPreferencesEditor* e = new wxPreferencesEditor ();
1198 #ifdef DCPOMATIC_OSX
1199 /* Width that we force some of the config panels to be on OSX so that
1200 the containing window doesn't shrink too much when we select those panels.
1201 This is obviously an unpleasant hack.
1203 wxSize ps = wxSize (520, -1);
1204 int const border = 16;
1206 wxSize ps = wxSize (-1, -1);
1207 int const border = 8;
1210 e->AddPage (new GeneralPage (ps, border));
1211 e->AddPage (new DefaultsPage (ps, border));
1212 e->AddPage (new EncodingServersPage (ps, border));
1213 e->AddPage (new ColourConversionsPage (ps, border));
1214 e->AddPage (new KeysPage (ps, border));
1215 e->AddPage (new TMSPage (ps, border));
1216 e->AddPage (new KDMEmailPage (ps, border));
1217 e->AddPage (new AdvancedPage (ps, border));