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/signer.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 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
575 _panel->GetSizer()->Add (m, 0, wxALL, _border);
577 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
578 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
580 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
585 ip.SetText (_("Type"));
587 _certificates->InsertColumn (0, ip);
593 ip.SetText (_("Thumbprint"));
596 wxFont font = ip.GetFont ();
597 font.SetFamily (wxFONTFAMILY_TELETYPE);
600 _certificates->InsertColumn (1, ip);
603 certificates_sizer->Add (_certificates, 1, wxEXPAND);
606 wxSizer* s = new wxBoxSizer (wxVERTICAL);
607 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
608 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
609 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
610 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
611 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
614 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
615 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
619 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
620 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
623 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
624 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
625 wxFont font = _signer_private_key->GetFont ();
626 font.SetFamily (wxFONTFAMILY_TELETYPE);
627 _signer_private_key->SetFont (font);
628 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
629 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
630 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
633 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
634 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
635 _decryption_certificate->SetFont (font);
636 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
637 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
638 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
641 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
642 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
643 _decryption_private_key->SetFont (font);
644 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
645 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
646 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
649 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
650 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
653 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
654 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
655 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
656 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
657 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
658 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
659 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
660 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
661 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
664 void config_changed ()
666 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
668 update_certificate_list ();
669 update_signer_private_key ();
670 update_decryption_certificate ();
671 update_decryption_private_key ();
672 update_sensitivity ();
675 void add_certificate ()
677 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
679 if (d->ShowModal() == wxID_OK) {
681 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
682 _signer->certificates().add (c);
683 Config::instance()->set_signer (_signer);
684 update_certificate_list ();
685 } catch (dcp::MiscError& e) {
686 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
692 update_sensitivity ();
695 void remove_certificate ()
697 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
702 _certificates->DeleteItem (i);
703 _signer->certificates().remove (i);
704 Config::instance()->set_signer (_signer);
706 update_sensitivity ();
709 void update_certificate_list ()
711 _certificates->DeleteAllItems ();
712 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
714 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
717 _certificates->InsertItem (item);
718 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
721 _certificates->SetItem (n, 0, _("Root"));
722 } else if (n == (certs.size() - 1)) {
723 _certificates->SetItem (n, 0, _("Leaf"));
725 _certificates->SetItem (n, 0, _("Intermediate"));
732 void remake_certificates ()
734 dcp::CertificateChain chain = Config::instance()->signer()->certificates ();
736 string intermediate_common_name;
737 if (chain.root_to_leaf().size() >= 3) {
738 dcp::CertificateChain::List all = chain.root_to_leaf ();
739 dcp::CertificateChain::List::iterator i = all.begin ();
741 intermediate_common_name = i->subject_common_name ();
744 MakeSignerChainDialog* d = new MakeSignerChainDialog (
746 chain.root().subject_organization_name (),
747 chain.root().subject_organizational_unit_name (),
748 chain.root().subject_common_name (),
749 intermediate_common_name,
750 chain.leaf().subject_common_name ()
753 if (d->ShowModal () == wxID_OK) {
758 d->organisational_unit (),
759 d->root_common_name (),
760 d->intermediate_common_name (),
761 d->leaf_common_name ()
765 Config::instance()->set_signer (_signer);
766 update_certificate_list ();
767 update_signer_private_key ();
773 void update_sensitivity ()
775 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
778 void update_signer_private_key ()
780 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
783 void load_signer_private_key ()
785 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
787 if (d->ShowModal() == wxID_OK) {
789 boost::filesystem::path p (wx_to_std (d->GetPath ()));
790 if (boost::filesystem::file_size (p) > 1024) {
791 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
795 _signer->set_key (dcp::file_to_string (p));
796 Config::instance()->set_signer (_signer);
797 update_signer_private_key ();
798 } catch (dcp::MiscError& e) {
799 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
805 update_sensitivity ();
809 void load_decryption_certificate ()
811 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
813 if (d->ShowModal() == wxID_OK) {
815 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
816 Config::instance()->set_decryption_certificate (c);
817 update_decryption_certificate ();
818 } catch (dcp::MiscError& e) {
819 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
826 void update_decryption_certificate ()
828 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
831 void load_decryption_private_key ()
833 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
835 if (d->ShowModal() == wxID_OK) {
837 boost::filesystem::path p (wx_to_std (d->GetPath ()));
838 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
839 update_decryption_private_key ();
840 } catch (dcp::MiscError& e) {
841 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
848 void update_decryption_private_key ()
850 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
853 void export_decryption_certificate ()
855 wxFileDialog* d = new wxFileDialog (
856 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
857 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
860 if (d->ShowModal () == wxID_OK) {
861 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
863 throw OpenFileError (wx_to_std (d->GetPath ()));
866 string const s = Config::instance()->decryption_certificate().certificate (true);
867 fwrite (s.c_str(), 1, s.length(), f);
873 wxListCtrl* _certificates;
874 wxButton* _add_certificate;
875 wxButton* _remove_certificate;
876 wxButton* _remake_certificates;
877 wxStaticText* _signer_private_key;
878 wxButton* _load_signer_private_key;
879 wxStaticText* _decryption_certificate;
880 wxButton* _load_decryption_certificate;
881 wxStaticText* _decryption_private_key;
882 wxButton* _load_decryption_private_key;
883 wxButton* _export_decryption_certificate;
884 shared_ptr<dcp::Signer> _signer;
887 class TMSPage : public StandardPage
890 TMSPage (wxSize panel_size, int border)
891 : StandardPage (panel_size, border)
894 wxString GetName () const
900 wxBitmap GetLargeIcon () const
902 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
909 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
910 table->AddGrowableCol (1, 1);
911 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
913 add_label_to_sizer (table, _panel, _("Protocol"), true);
914 _tms_protocol = new wxChoice (_panel, wxID_ANY);
915 table->Add (_tms_protocol, 1, wxEXPAND);
917 add_label_to_sizer (table, _panel, _("IP address"), true);
918 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
919 table->Add (_tms_ip, 1, wxEXPAND);
921 add_label_to_sizer (table, _panel, _("Target path"), true);
922 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
923 table->Add (_tms_path, 1, wxEXPAND);
925 add_label_to_sizer (table, _panel, _("User name"), true);
926 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
927 table->Add (_tms_user, 1, wxEXPAND);
929 add_label_to_sizer (table, _panel, _("Password"), true);
930 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
931 table->Add (_tms_password, 1, wxEXPAND);
933 _tms_protocol->Append (_("SCP (for AAM)"));
934 _tms_protocol->Append (_("FTP (for Dolby)"));
936 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
937 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
938 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
939 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
940 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
943 void config_changed ()
945 Config* config = Config::instance ();
947 checked_set (_tms_protocol, config->tms_protocol ());
948 checked_set (_tms_ip, config->tms_ip ());
949 checked_set (_tms_path, config->tms_path ());
950 checked_set (_tms_user, config->tms_user ());
951 checked_set (_tms_password, config->tms_password ());
954 void tms_protocol_changed ()
956 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
959 void tms_ip_changed ()
961 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
964 void tms_path_changed ()
966 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
969 void tms_user_changed ()
971 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
974 void tms_password_changed ()
976 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
979 wxChoice* _tms_protocol;
981 wxTextCtrl* _tms_path;
982 wxTextCtrl* _tms_user;
983 wxTextCtrl* _tms_password;
986 class KDMEmailPage : public StandardPage
990 KDMEmailPage (wxSize panel_size, int border)
992 /* We have to force both width and height of this one */
993 : StandardPage (wxSize (480, 128), border)
995 : StandardPage (panel_size, border)
999 wxString GetName () const
1001 return _("KDM Email");
1004 #ifdef DCPOMATIC_OSX
1005 wxBitmap GetLargeIcon () const
1007 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1014 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1015 table->AddGrowableCol (1, 1);
1016 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1018 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1020 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1021 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1022 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1023 add_label_to_sizer (s, _panel, _("port"), false);
1024 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1025 _mail_port->SetRange (0, 65535);
1026 s->Add (_mail_port);
1027 table->Add (s, 1, wxEXPAND | wxALL);
1030 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1031 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1032 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1034 add_label_to_sizer (table, _panel, _("Mail password"), true);
1035 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1036 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1038 add_label_to_sizer (table, _panel, _("Subject"), true);
1039 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1040 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1042 add_label_to_sizer (table, _panel, _("From address"), true);
1043 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1044 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1046 add_label_to_sizer (table, _panel, _("CC address"), true);
1047 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1048 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1050 add_label_to_sizer (table, _panel, _("BCC address"), true);
1051 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1052 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1054 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1055 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1057 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1058 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1060 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1061 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1062 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1063 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1064 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1065 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1066 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1067 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1068 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1069 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1072 void config_changed ()
1074 Config* config = Config::instance ();
1076 checked_set (_mail_server, config->mail_server ());
1077 checked_set (_mail_port, config->mail_port ());
1078 checked_set (_mail_user, config->mail_user ());
1079 checked_set (_mail_password, config->mail_password ());
1080 checked_set (_kdm_subject, config->kdm_subject ());
1081 checked_set (_kdm_from, config->kdm_from ());
1082 checked_set (_kdm_cc, config->kdm_cc ());
1083 checked_set (_kdm_bcc, config->kdm_bcc ());
1084 checked_set (_kdm_email, Config::instance()->kdm_email ());
1087 void mail_server_changed ()
1089 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1092 void mail_port_changed ()
1094 Config::instance()->set_mail_port (_mail_port->GetValue ());
1097 void mail_user_changed ()
1099 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1102 void mail_password_changed ()
1104 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1107 void kdm_subject_changed ()
1109 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1112 void kdm_from_changed ()
1114 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1117 void kdm_cc_changed ()
1119 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1122 void kdm_bcc_changed ()
1124 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1127 void kdm_email_changed ()
1129 if (_kdm_email->GetValue().IsEmpty ()) {
1130 /* Sometimes we get sent an erroneous notification that the email
1131 is empty; I don't know why.
1135 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1138 void reset_kdm_email ()
1140 Config::instance()->reset_kdm_email ();
1141 checked_set (_kdm_email, Config::instance()->kdm_email ());
1144 wxTextCtrl* _mail_server;
1145 wxSpinCtrl* _mail_port;
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)
1170 , _log_debug_decode (0)
1171 , _log_debug_encode (0)
1177 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1178 table->AddGrowableCol (1, 1);
1179 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1182 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1183 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1184 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1185 s->Add (_maximum_j2k_bandwidth, 1);
1186 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1190 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1191 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1192 table->AddSpacer (0);
1195 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1196 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1198 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1199 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1203 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1204 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1205 t->Add (_log_general, 1, wxEXPAND | wxALL);
1206 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1207 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1208 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1209 t->Add (_log_error, 1, wxEXPAND | wxALL);
1210 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1211 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1212 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1213 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1214 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1215 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1216 table->Add (t, 0, wxALL, 6);
1219 #ifdef DCPOMATIC_WINDOWS
1220 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1221 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1222 table->AddSpacer (0);
1225 _maximum_j2k_bandwidth->SetRange (1, 1000);
1226 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1227 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1228 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1229 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1230 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1231 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1232 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1233 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1234 #ifdef DCPOMATIC_WINDOWS
1235 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1239 void config_changed ()
1241 Config* config = Config::instance ();
1243 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1244 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1245 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1246 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1247 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1248 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1249 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1250 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1251 #ifdef DCPOMATIC_WINDOWS
1252 checked_set (_win32_console, config->win32_console());
1256 void maximum_j2k_bandwidth_changed ()
1258 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1261 void allow_any_dcp_frame_rate_changed ()
1263 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1269 if (_log_general->GetValue ()) {
1270 types |= Log::TYPE_GENERAL;
1272 if (_log_warning->GetValue ()) {
1273 types |= Log::TYPE_WARNING;
1275 if (_log_error->GetValue ()) {
1276 types |= Log::TYPE_ERROR;
1278 if (_log_timing->GetValue ()) {
1279 types |= Log::TYPE_TIMING;
1281 if (_log_debug_decode->GetValue ()) {
1282 types |= Log::TYPE_DEBUG_DECODE;
1284 if (_log_debug_encode->GetValue ()) {
1285 types |= Log::TYPE_DEBUG_ENCODE;
1287 Config::instance()->set_log_types (types);
1290 #ifdef DCPOMATIC_WINDOWS
1291 void win32_console_changed ()
1293 Config::instance()->set_win32_console (_win32_console->GetValue ());
1297 wxSpinCtrl* _maximum_j2k_bandwidth;
1298 wxCheckBox* _allow_any_dcp_frame_rate;
1299 wxCheckBox* _log_general;
1300 wxCheckBox* _log_warning;
1301 wxCheckBox* _log_error;
1302 wxCheckBox* _log_timing;
1303 wxCheckBox* _log_debug_decode;
1304 wxCheckBox* _log_debug_encode;
1305 #ifdef DCPOMATIC_WINDOWS
1306 wxCheckBox* _win32_console;
1310 wxPreferencesEditor*
1311 create_config_dialog ()
1313 wxPreferencesEditor* e = new wxPreferencesEditor ();
1315 #ifdef DCPOMATIC_OSX
1316 /* Width that we force some of the config panels to be on OSX so that
1317 the containing window doesn't shrink too much when we select those panels.
1318 This is obviously an unpleasant hack.
1320 wxSize ps = wxSize (520, -1);
1321 int const border = 16;
1323 wxSize ps = wxSize (-1, -1);
1324 int const border = 8;
1327 e->AddPage (new GeneralPage (ps, border));
1328 e->AddPage (new DefaultsPage (ps, border));
1329 e->AddPage (new EncodingServersPage (ps, border));
1330 e->AddPage (new KeysPage (ps, border));
1331 e->AddPage (new TMSPage (ps, border));
1332 e->AddPage (new KDMEmailPage (ps, border));
1333 e->AddPage (new AdvancedPage (ps, border));