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.
24 #include "config_dialog.h"
26 #include "editable_list.h"
27 #include "filter_dialog.h"
28 #include "dir_picker_ctrl.h"
29 #include "isdcf_metadata_dialog.h"
30 #include "server_dialog.h"
31 #include "make_signer_chain_dialog.h"
32 #include "lib/config.h"
33 #include "lib/ratio.h"
34 #include "lib/filter.h"
35 #include "lib/dcp_content_type.h"
38 #include "lib/raw_convert.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include <dcp/exceptions.h>
42 #include <dcp/certificate_chain.h>
43 #include <wx/stdpaths.h>
44 #include <wx/preferences.h>
45 #include <wx/filepicker.h>
46 #include <wx/spinctrl.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/filesystem.hpp>
56 using boost::shared_ptr;
57 using boost::lexical_cast;
62 Page (wxSize panel_size, int border)
65 , _panel_size (panel_size)
66 , _window_exists (false)
68 _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);
81 _window_exists = true;
84 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
93 virtual void config_changed () = 0;
94 virtual void setup () = 0;
96 void config_changed_wrapper ()
103 void window_destroyed ()
105 _window_exists = false;
109 boost::signals2::scoped_connection _config_connection;
113 class StockPage : public wxStockPreferencesPage, public Page
116 StockPage (Kind kind, wxSize panel_size, int border)
117 : wxStockPreferencesPage (kind)
118 , Page (panel_size, border)
121 wxWindow* CreateWindow (wxWindow* parent)
123 return create_window (parent);
127 class StandardPage : public wxPreferencesPage, public Page
130 StandardPage (wxSize panel_size, int border)
131 : Page (panel_size, border)
134 wxWindow* CreateWindow (wxWindow* parent)
136 return create_window (parent);
140 class GeneralPage : public StockPage
143 GeneralPage (wxSize panel_size, int border)
144 : StockPage (Kind_General, panel_size, border)
150 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
151 table->AddGrowableCol (1, 1);
152 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
154 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
155 table->Add (_set_language, 1);
156 _language = new wxChoice (_panel, wxID_ANY);
157 _language->Append (wxT ("Deutsch"));
158 _language->Append (wxT ("English"));
159 _language->Append (wxT ("Español"));
160 _language->Append (wxT ("Français"));
161 _language->Append (wxT ("Italiano"));
162 _language->Append (wxT ("Nederlands"));
163 _language->Append (wxT ("Svenska"));
164 _language->Append (wxT ("Русский"));
165 _language->Append (wxT ("Polski"));
166 table->Add (_language);
168 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
169 wxFont font = restart->GetFont();
170 font.SetStyle (wxFONTSTYLE_ITALIC);
171 font.SetPointSize (font.GetPointSize() - 1);
172 restart->SetFont (font);
173 table->AddSpacer (0);
175 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
176 _num_local_encoding_threads = new wxSpinCtrl (_panel);
177 table->Add (_num_local_encoding_threads, 1);
179 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
180 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
181 table->AddSpacer (0);
183 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
184 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
185 table->AddSpacer (0);
187 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
188 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
190 _num_local_encoding_threads->SetRange (1, 128);
191 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
193 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
194 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
197 void config_changed ()
199 Config* config = Config::instance ();
201 checked_set (_set_language, config->language ());
203 if (config->language().get_value_or ("") == "fr") {
204 _language->SetSelection (3);
205 } else if (config->language().get_value_or ("") == "it") {
206 _language->SetSelection (4);
207 } else if (config->language().get_value_or ("") == "es") {
208 _language->SetSelection (2);
209 } else if (config->language().get_value_or ("") == "sv") {
210 _language->SetSelection (6);
211 } else if (config->language().get_value_or ("") == "de") {
212 _language->SetSelection (0);
213 } else if (config->language().get_value_or ("") == "nl") {
214 _language->SetSelection (5);
215 } else if (config->language().get_value_or ("") == "ru") {
216 _language->SetSelection (7);
217 } else if (config->language().get_value_or ("") == "pl") {
218 checked_set (_language, 8);
220 _language->SetSelection (1);
223 setup_language_sensitivity ();
225 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
226 checked_set (_check_for_updates, config->check_for_updates ());
227 checked_set (_check_for_test_updates, config->check_for_test_updates ());
230 void setup_language_sensitivity ()
232 _language->Enable (_set_language->GetValue ());
235 void set_language_changed ()
237 setup_language_sensitivity ();
238 if (_set_language->GetValue ()) {
241 Config::instance()->unset_language ();
245 void language_changed ()
247 switch (_language->GetSelection ()) {
249 Config::instance()->set_language ("de");
252 Config::instance()->set_language ("en");
255 Config::instance()->set_language ("es");
258 Config::instance()->set_language ("fr");
261 Config::instance()->set_language ("it");
264 Config::instance()->set_language ("nl");
267 Config::instance()->set_language ("sv");
270 Config::instance()->set_language ("ru");
273 Config::instance()->set_language ("pl");
278 void check_for_updates_changed ()
280 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
283 void check_for_test_updates_changed ()
285 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
288 void num_local_encoding_threads_changed ()
290 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
293 wxCheckBox* _set_language;
295 wxSpinCtrl* _num_local_encoding_threads;
296 wxCheckBox* _check_for_updates;
297 wxCheckBox* _check_for_test_updates;
300 class DefaultsPage : public StandardPage
303 DefaultsPage (wxSize panel_size, int border)
304 : StandardPage (panel_size, border)
307 wxString GetName () const
309 return _("Defaults");
313 wxBitmap GetLargeIcon () const
315 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
322 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
323 table->AddGrowableCol (1, 1);
324 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
327 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
328 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
329 _still_length = new wxSpinCtrl (_panel);
330 s->Add (_still_length);
331 add_label_to_sizer (s, _panel, _("s"), false);
335 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
336 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
337 _directory = new DirPickerCtrl (_panel);
339 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
341 table->Add (_directory, 1, wxEXPAND);
343 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
344 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
345 table->Add (_isdcf_metadata_button);
347 add_label_to_sizer (table, _panel, _("Default container"), true);
348 _container = new wxChoice (_panel, wxID_ANY);
349 table->Add (_container);
351 add_label_to_sizer (table, _panel, _("Default content type"), true);
352 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
353 table->Add (_dcp_content_type);
356 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
357 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
358 _j2k_bandwidth = new wxSpinCtrl (_panel);
359 s->Add (_j2k_bandwidth);
360 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
365 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
366 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
367 _audio_delay = new wxSpinCtrl (_panel);
368 s->Add (_audio_delay);
369 add_label_to_sizer (s, _panel, _("ms"), false);
373 add_label_to_sizer (table, _panel, _("Default issuer"), true);
374 _issuer = new wxTextCtrl (_panel, wxID_ANY);
375 table->Add (_issuer, 1, wxEXPAND);
377 _still_length->SetRange (1, 3600);
378 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
380 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
382 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
384 vector<Ratio const *> ratios = Ratio::all ();
385 for (size_t i = 0; i < ratios.size(); ++i) {
386 _container->Append (std_to_wx (ratios[i]->nickname ()));
389 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
391 vector<DCPContentType const *> const ct = DCPContentType::all ();
392 for (size_t i = 0; i < ct.size(); ++i) {
393 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
396 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
398 _j2k_bandwidth->SetRange (50, 250);
399 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
401 _audio_delay->SetRange (-1000, 1000);
402 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
404 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
407 void config_changed ()
409 Config* config = Config::instance ();
411 vector<Ratio const *> ratios = Ratio::all ();
412 for (size_t i = 0; i < ratios.size(); ++i) {
413 if (ratios[i] == config->default_container ()) {
414 _container->SetSelection (i);
418 vector<DCPContentType const *> const ct = DCPContentType::all ();
419 for (size_t i = 0; i < ct.size(); ++i) {
420 if (ct[i] == config->default_dcp_content_type ()) {
421 _dcp_content_type->SetSelection (i);
425 checked_set (_still_length, config->default_still_length ());
426 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
427 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
428 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
429 checked_set (_audio_delay, config->default_audio_delay ());
430 checked_set (_issuer, config->dcp_issuer ());
433 void j2k_bandwidth_changed ()
435 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
438 void audio_delay_changed ()
440 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
443 void directory_changed ()
445 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
448 void edit_isdcf_metadata_clicked ()
450 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
452 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
456 void still_length_changed ()
458 Config::instance()->set_default_still_length (_still_length->GetValue ());
461 void container_changed ()
463 vector<Ratio const *> ratio = Ratio::all ();
464 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
467 void dcp_content_type_changed ()
469 vector<DCPContentType const *> ct = DCPContentType::all ();
470 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
473 void issuer_changed ()
475 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
478 wxSpinCtrl* _j2k_bandwidth;
479 wxSpinCtrl* _audio_delay;
480 wxButton* _isdcf_metadata_button;
481 wxSpinCtrl* _still_length;
482 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
483 DirPickerCtrl* _directory;
485 wxDirPickerCtrl* _directory;
487 wxChoice* _container;
488 wxChoice* _dcp_content_type;
492 class EncodingServersPage : public StandardPage
495 EncodingServersPage (wxSize panel_size, int border)
496 : StandardPage (panel_size, border)
499 wxString GetName () const
505 wxBitmap GetLargeIcon () const
507 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
514 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
515 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
517 vector<string> columns;
518 columns.push_back (wx_to_std (_("IP address / host name")));
519 _servers_list = new EditableList<string, ServerDialog> (
522 boost::bind (&Config::servers, Config::instance()),
523 boost::bind (&Config::set_servers, Config::instance(), _1),
524 boost::bind (&EncodingServersPage::server_column, this, _1)
527 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
529 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
532 void config_changed ()
534 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
535 _servers_list->refresh ();
538 void use_any_servers_changed ()
540 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
543 string server_column (string s)
548 wxCheckBox* _use_any_servers;
549 EditableList<string, ServerDialog>* _servers_list;
552 class KeysPage : public StandardPage
555 KeysPage (wxSize panel_size, int border)
556 : StandardPage (panel_size, border)
559 wxString GetName () const
565 wxBitmap GetLargeIcon () const
567 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
574 wxFont subheading_font (*wxNORMAL_FONT);
575 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
578 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Signing DCPs and KDMs"));
579 m->SetFont (subheading_font);
580 _panel->GetSizer()->Add (m, 0, wxALL, _border);
583 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
584 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
586 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
591 ip.SetText (_("Type"));
593 _certificates->InsertColumn (0, ip);
599 ip.SetText (_("Thumbprint"));
602 wxFont font = ip.GetFont ();
603 font.SetFamily (wxFONTFAMILY_TELETYPE);
606 _certificates->InsertColumn (1, ip);
609 certificates_sizer->Add (_certificates, 1, wxEXPAND);
612 wxSizer* s = new wxBoxSizer (wxVERTICAL);
613 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
614 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
615 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
616 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
617 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
620 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
621 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
625 add_label_to_grid_bag_sizer (table, _panel, _("Leaf private key"), true, wxGBPosition (r, 0));
626 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
627 wxFont font = _signer_private_key->GetFont ();
628 font.SetFamily (wxFONTFAMILY_TELETYPE);
629 _signer_private_key->SetFont (font);
630 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
631 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
632 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
635 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates and key..."));
636 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
640 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Decrypting DCPs"));
641 m->SetFont (subheading_font);
642 table->Add (m, wxGBPosition (r, 0), wxGBSpan (1, 3), wxTOP, _border * 1.5);
646 add_label_to_grid_bag_sizer (table, _panel, _("Certificate"), true, wxGBPosition (r, 0));
647 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
648 _decryption_certificate->SetFont (font);
649 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
650 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
651 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
654 add_label_to_grid_bag_sizer (table, _panel, _("Private key"), true, wxGBPosition (r, 0));
655 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
656 _decryption_private_key->SetFont (font);
657 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
658 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
659 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
662 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
663 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
666 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
667 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
668 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
669 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
670 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
671 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
672 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
673 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
674 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
677 void config_changed ()
679 _signer.reset (new dcp::CertificateChain (*Config::instance()->signer().get ()));
681 update_certificate_list ();
682 update_signer_private_key ();
683 update_decryption_certificate ();
684 update_decryption_private_key ();
685 update_sensitivity ();
688 void add_certificate ()
690 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
692 if (d->ShowModal() == wxID_OK) {
694 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
696 Config::instance()->set_signer (_signer);
697 update_certificate_list ();
698 } catch (dcp::MiscError& e) {
699 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
705 update_sensitivity ();
708 void remove_certificate ()
710 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
715 _certificates->DeleteItem (i);
717 Config::instance()->set_signer (_signer);
719 update_sensitivity ();
722 void update_certificate_list ()
724 _certificates->DeleteAllItems ();
725 dcp::CertificateChain::List certs = _signer->root_to_leaf ();
727 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
730 _certificates->InsertItem (item);
731 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
734 _certificates->SetItem (n, 0, _("Root"));
735 } else if (n == (certs.size() - 1)) {
736 _certificates->SetItem (n, 0, _("Leaf"));
738 _certificates->SetItem (n, 0, _("Intermediate"));
745 void remake_certificates ()
747 shared_ptr<const dcp::CertificateChain> chain = Config::instance()->signer();
749 string intermediate_common_name;
750 if (chain->root_to_leaf().size() >= 3) {
751 dcp::CertificateChain::List all = chain->root_to_leaf ();
752 dcp::CertificateChain::List::iterator i = all.begin ();
754 intermediate_common_name = i->subject_common_name ();
757 MakeSignerChainDialog* d = new MakeSignerChainDialog (
759 chain->root().subject_organization_name (),
760 chain->root().subject_organizational_unit_name (),
761 chain->root().subject_common_name (),
762 intermediate_common_name,
763 chain->leaf().subject_common_name ()
766 if (d->ShowModal () == wxID_OK) {
768 new dcp::CertificateChain (
771 d->organisational_unit (),
772 d->root_common_name (),
773 d->intermediate_common_name (),
774 d->leaf_common_name ()
778 Config::instance()->set_signer (_signer);
779 update_certificate_list ();
780 update_signer_private_key ();
786 void update_sensitivity ()
788 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
791 void update_signer_private_key ()
793 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key().get ()));
796 void load_signer_private_key ()
798 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
800 if (d->ShowModal() == wxID_OK) {
802 boost::filesystem::path p (wx_to_std (d->GetPath ()));
803 if (boost::filesystem::file_size (p) > 1024) {
804 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
808 _signer->set_key (dcp::file_to_string (p));
809 Config::instance()->set_signer (_signer);
810 update_signer_private_key ();
811 } catch (dcp::MiscError& e) {
812 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
818 update_sensitivity ();
822 void load_decryption_certificate ()
824 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
826 if (d->ShowModal() == wxID_OK) {
828 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
829 Config::instance()->set_decryption_certificate (c);
830 update_decryption_certificate ();
831 } catch (dcp::MiscError& e) {
832 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
839 void update_decryption_certificate ()
841 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
844 void load_decryption_private_key ()
846 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
848 if (d->ShowModal() == wxID_OK) {
850 boost::filesystem::path p (wx_to_std (d->GetPath ()));
851 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
852 update_decryption_private_key ();
853 } catch (dcp::MiscError& e) {
854 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
861 void update_decryption_private_key ()
863 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
866 void export_decryption_certificate ()
868 wxFileDialog* d = new wxFileDialog (
869 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
870 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
873 if (d->ShowModal () == wxID_OK) {
874 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
876 throw OpenFileError (wx_to_std (d->GetPath ()));
879 string const s = Config::instance()->decryption_certificate().certificate (true);
880 fwrite (s.c_str(), 1, s.length(), f);
886 wxListCtrl* _certificates;
887 wxButton* _add_certificate;
888 wxButton* _remove_certificate;
889 wxButton* _remake_certificates;
890 wxStaticText* _signer_private_key;
891 wxButton* _load_signer_private_key;
892 wxStaticText* _decryption_certificate;
893 wxButton* _load_decryption_certificate;
894 wxStaticText* _decryption_private_key;
895 wxButton* _load_decryption_private_key;
896 wxButton* _export_decryption_certificate;
897 shared_ptr<dcp::CertificateChain> _signer;
900 class TMSPage : public StandardPage
903 TMSPage (wxSize panel_size, int border)
904 : StandardPage (panel_size, border)
907 wxString GetName () const
913 wxBitmap GetLargeIcon () const
915 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
922 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
923 table->AddGrowableCol (1, 1);
924 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
926 add_label_to_sizer (table, _panel, _("Protocol"), true);
927 _tms_protocol = new wxChoice (_panel, wxID_ANY);
928 table->Add (_tms_protocol, 1, wxEXPAND);
930 add_label_to_sizer (table, _panel, _("IP address"), true);
931 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
932 table->Add (_tms_ip, 1, wxEXPAND);
934 add_label_to_sizer (table, _panel, _("Target path"), true);
935 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
936 table->Add (_tms_path, 1, wxEXPAND);
938 add_label_to_sizer (table, _panel, _("User name"), true);
939 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
940 table->Add (_tms_user, 1, wxEXPAND);
942 add_label_to_sizer (table, _panel, _("Password"), true);
943 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
944 table->Add (_tms_password, 1, wxEXPAND);
946 _tms_protocol->Append (_("SCP (for AAM)"));
947 _tms_protocol->Append (_("FTP (for Dolby)"));
949 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
950 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
951 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
952 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
953 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
956 void config_changed ()
958 Config* config = Config::instance ();
960 checked_set (_tms_protocol, config->tms_protocol ());
961 checked_set (_tms_ip, config->tms_ip ());
962 checked_set (_tms_path, config->tms_path ());
963 checked_set (_tms_user, config->tms_user ());
964 checked_set (_tms_password, config->tms_password ());
967 void tms_protocol_changed ()
969 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
972 void tms_ip_changed ()
974 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
977 void tms_path_changed ()
979 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
982 void tms_user_changed ()
984 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
987 void tms_password_changed ()
989 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
992 wxChoice* _tms_protocol;
994 wxTextCtrl* _tms_path;
995 wxTextCtrl* _tms_user;
996 wxTextCtrl* _tms_password;
999 class KDMEmailPage : public StandardPage
1003 KDMEmailPage (wxSize panel_size, int border)
1004 #ifdef DCPOMATIC_OSX
1005 /* We have to force both width and height of this one */
1006 : StandardPage (wxSize (480, 128), border)
1008 : StandardPage (panel_size, border)
1012 wxString GetName () const
1014 return _("KDM Email");
1017 #ifdef DCPOMATIC_OSX
1018 wxBitmap GetLargeIcon () const
1020 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1027 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1028 table->AddGrowableCol (1, 1);
1029 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1031 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1033 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1034 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1035 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1036 add_label_to_sizer (s, _panel, _("port"), false);
1037 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1038 _mail_port->SetRange (0, 65535);
1039 s->Add (_mail_port);
1040 table->Add (s, 1, wxEXPAND | wxALL);
1043 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1044 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1045 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1047 add_label_to_sizer (table, _panel, _("Mail password"), true);
1048 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1049 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1051 add_label_to_sizer (table, _panel, _("Subject"), true);
1052 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1053 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1055 add_label_to_sizer (table, _panel, _("From address"), true);
1056 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1057 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1059 add_label_to_sizer (table, _panel, _("CC address"), true);
1060 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1061 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1063 add_label_to_sizer (table, _panel, _("BCC address"), true);
1064 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1065 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1067 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1068 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1070 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1071 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1073 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1074 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1075 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1076 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1077 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1078 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1079 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1080 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1081 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1082 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1085 void config_changed ()
1087 Config* config = Config::instance ();
1089 checked_set (_mail_server, config->mail_server ());
1090 checked_set (_mail_port, config->mail_port ());
1091 checked_set (_mail_user, config->mail_user ());
1092 checked_set (_mail_password, config->mail_password ());
1093 checked_set (_kdm_subject, config->kdm_subject ());
1094 checked_set (_kdm_from, config->kdm_from ());
1095 checked_set (_kdm_cc, config->kdm_cc ());
1096 checked_set (_kdm_bcc, config->kdm_bcc ());
1097 checked_set (_kdm_email, Config::instance()->kdm_email ());
1100 void mail_server_changed ()
1102 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1105 void mail_port_changed ()
1107 Config::instance()->set_mail_port (_mail_port->GetValue ());
1110 void mail_user_changed ()
1112 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1115 void mail_password_changed ()
1117 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1120 void kdm_subject_changed ()
1122 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1125 void kdm_from_changed ()
1127 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1130 void kdm_cc_changed ()
1132 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1135 void kdm_bcc_changed ()
1137 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1140 void kdm_email_changed ()
1142 if (_kdm_email->GetValue().IsEmpty ()) {
1143 /* Sometimes we get sent an erroneous notification that the email
1144 is empty; I don't know why.
1148 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1151 void reset_kdm_email ()
1153 Config::instance()->reset_kdm_email ();
1154 checked_set (_kdm_email, Config::instance()->kdm_email ());
1157 wxTextCtrl* _mail_server;
1158 wxSpinCtrl* _mail_port;
1159 wxTextCtrl* _mail_user;
1160 wxTextCtrl* _mail_password;
1161 wxTextCtrl* _kdm_subject;
1162 wxTextCtrl* _kdm_from;
1163 wxTextCtrl* _kdm_cc;
1164 wxTextCtrl* _kdm_bcc;
1165 wxTextCtrl* _kdm_email;
1166 wxButton* _reset_kdm_email;
1169 /** @class AdvancedPage
1170 * @brief Advanced page of the preferences dialog.
1172 class AdvancedPage : public StockPage
1175 AdvancedPage (wxSize panel_size, int border)
1176 : StockPage (Kind_Advanced, panel_size, border)
1177 , _maximum_j2k_bandwidth (0)
1178 , _allow_any_dcp_frame_rate (0)
1183 , _log_debug_decode (0)
1184 , _log_debug_encode (0)
1190 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1191 table->AddGrowableCol (1, 1);
1192 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1195 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1196 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1197 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1198 s->Add (_maximum_j2k_bandwidth, 1);
1199 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1203 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1204 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1205 table->AddSpacer (0);
1208 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1209 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1211 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1212 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1216 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1217 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1218 t->Add (_log_general, 1, wxEXPAND | wxALL);
1219 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1220 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1221 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1222 t->Add (_log_error, 1, wxEXPAND | wxALL);
1223 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1224 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1225 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1226 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1227 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1228 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1229 table->Add (t, 0, wxALL, 6);
1232 #ifdef DCPOMATIC_WINDOWS
1233 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1234 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1235 table->AddSpacer (0);
1238 _maximum_j2k_bandwidth->SetRange (1, 1000);
1239 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1240 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1241 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1242 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1243 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1244 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1245 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1246 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1247 #ifdef DCPOMATIC_WINDOWS
1248 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1252 void config_changed ()
1254 Config* config = Config::instance ();
1256 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1257 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1258 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1259 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1260 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1261 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1262 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1263 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1264 #ifdef DCPOMATIC_WINDOWS
1265 checked_set (_win32_console, config->win32_console());
1269 void maximum_j2k_bandwidth_changed ()
1271 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1274 void allow_any_dcp_frame_rate_changed ()
1276 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1282 if (_log_general->GetValue ()) {
1283 types |= Log::TYPE_GENERAL;
1285 if (_log_warning->GetValue ()) {
1286 types |= Log::TYPE_WARNING;
1288 if (_log_error->GetValue ()) {
1289 types |= Log::TYPE_ERROR;
1291 if (_log_timing->GetValue ()) {
1292 types |= Log::TYPE_TIMING;
1294 if (_log_debug_decode->GetValue ()) {
1295 types |= Log::TYPE_DEBUG_DECODE;
1297 if (_log_debug_encode->GetValue ()) {
1298 types |= Log::TYPE_DEBUG_ENCODE;
1300 Config::instance()->set_log_types (types);
1303 #ifdef DCPOMATIC_WINDOWS
1304 void win32_console_changed ()
1306 Config::instance()->set_win32_console (_win32_console->GetValue ());
1310 wxSpinCtrl* _maximum_j2k_bandwidth;
1311 wxCheckBox* _allow_any_dcp_frame_rate;
1312 wxCheckBox* _log_general;
1313 wxCheckBox* _log_warning;
1314 wxCheckBox* _log_error;
1315 wxCheckBox* _log_timing;
1316 wxCheckBox* _log_debug_decode;
1317 wxCheckBox* _log_debug_encode;
1318 #ifdef DCPOMATIC_WINDOWS
1319 wxCheckBox* _win32_console;
1323 wxPreferencesEditor*
1324 create_config_dialog ()
1326 wxPreferencesEditor* e = new wxPreferencesEditor ();
1328 #ifdef DCPOMATIC_OSX
1329 /* Width that we force some of the config panels to be on OSX so that
1330 the containing window doesn't shrink too much when we select those panels.
1331 This is obviously an unpleasant hack.
1333 wxSize ps = wxSize (520, -1);
1334 int const border = 16;
1336 wxSize ps = wxSize (-1, -1);
1337 int const border = 8;
1340 e->AddPage (new GeneralPage (ps, border));
1341 e->AddPage (new DefaultsPage (ps, border));
1342 e->AddPage (new EncodingServersPage (ps, border));
1343 e->AddPage (new KeysPage (ps, border));
1344 e->AddPage (new TMSPage (ps, border));
1345 e->AddPage (new KDMEmailPage (ps, border));
1346 e->AddPage (new AdvancedPage (ps, border));