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 table->Add (_language);
167 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
168 wxFont font = restart->GetFont();
169 font.SetStyle (wxFONTSTYLE_ITALIC);
170 font.SetPointSize (font.GetPointSize() - 1);
171 restart->SetFont (font);
172 table->AddSpacer (0);
174 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
175 _num_local_encoding_threads = new wxSpinCtrl (_panel);
176 table->Add (_num_local_encoding_threads, 1);
178 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
179 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
180 table->AddSpacer (0);
182 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
183 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
184 table->AddSpacer (0);
186 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
187 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
189 _num_local_encoding_threads->SetRange (1, 128);
190 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
192 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
193 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
196 void config_changed ()
198 Config* config = Config::instance ();
200 checked_set (_set_language, config->language ());
202 if (config->language().get_value_or ("") == "fr") {
203 _language->SetSelection (3);
204 } else if (config->language().get_value_or ("") == "it") {
205 _language->SetSelection (4);
206 } else if (config->language().get_value_or ("") == "es") {
207 _language->SetSelection (2);
208 } else if (config->language().get_value_or ("") == "sv") {
209 _language->SetSelection (6);
210 } else if (config->language().get_value_or ("") == "de") {
211 _language->SetSelection (0);
212 } else if (config->language().get_value_or ("") == "nl") {
213 _language->SetSelection (5);
214 } else if (config->language().get_value_or ("") == "ru") {
215 _language->SetSelection (7);
217 _language->SetSelection (1);
220 setup_language_sensitivity ();
222 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
223 checked_set (_check_for_updates, config->check_for_updates ());
224 checked_set (_check_for_test_updates, config->check_for_test_updates ());
227 void setup_language_sensitivity ()
229 _language->Enable (_set_language->GetValue ());
232 void set_language_changed ()
234 setup_language_sensitivity ();
235 if (_set_language->GetValue ()) {
238 Config::instance()->unset_language ();
242 void language_changed ()
244 switch (_language->GetSelection ()) {
246 Config::instance()->set_language ("de");
249 Config::instance()->set_language ("en");
252 Config::instance()->set_language ("es");
255 Config::instance()->set_language ("fr");
258 Config::instance()->set_language ("it");
261 Config::instance()->set_language ("nl");
264 Config::instance()->set_language ("sv");
267 Config::instance()->set_language ("ru");
272 void check_for_updates_changed ()
274 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
277 void check_for_test_updates_changed ()
279 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
282 void num_local_encoding_threads_changed ()
284 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
287 wxCheckBox* _set_language;
289 wxSpinCtrl* _num_local_encoding_threads;
290 wxCheckBox* _check_for_updates;
291 wxCheckBox* _check_for_test_updates;
294 class DefaultsPage : public StandardPage
297 DefaultsPage (wxSize panel_size, int border)
298 : StandardPage (panel_size, border)
301 wxString GetName () const
303 return _("Defaults");
307 wxBitmap GetLargeIcon () const
309 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
316 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
317 table->AddGrowableCol (1, 1);
318 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
321 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
322 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
323 _still_length = new wxSpinCtrl (_panel);
324 s->Add (_still_length);
325 add_label_to_sizer (s, _panel, _("s"), false);
329 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
330 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
331 _directory = new DirPickerCtrl (_panel);
333 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
335 table->Add (_directory, 1, wxEXPAND);
337 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
338 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
339 table->Add (_isdcf_metadata_button);
341 add_label_to_sizer (table, _panel, _("Default container"), true);
342 _container = new wxChoice (_panel, wxID_ANY);
343 table->Add (_container);
345 add_label_to_sizer (table, _panel, _("Default content type"), true);
346 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
347 table->Add (_dcp_content_type);
350 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
351 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
352 _j2k_bandwidth = new wxSpinCtrl (_panel);
353 s->Add (_j2k_bandwidth);
354 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
359 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
360 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
361 _audio_delay = new wxSpinCtrl (_panel);
362 s->Add (_audio_delay);
363 add_label_to_sizer (s, _panel, _("ms"), false);
367 add_label_to_sizer (table, _panel, _("Default issuer"), true);
368 _issuer = new wxTextCtrl (_panel, wxID_ANY);
369 table->Add (_issuer, 1, wxEXPAND);
371 _still_length->SetRange (1, 3600);
372 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
374 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
376 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
378 vector<Ratio const *> ratios = Ratio::all ();
379 for (size_t i = 0; i < ratios.size(); ++i) {
380 _container->Append (std_to_wx (ratios[i]->nickname ()));
383 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
385 vector<DCPContentType const *> const ct = DCPContentType::all ();
386 for (size_t i = 0; i < ct.size(); ++i) {
387 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
390 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
392 _j2k_bandwidth->SetRange (50, 250);
393 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
395 _audio_delay->SetRange (-1000, 1000);
396 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
398 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
401 void config_changed ()
403 Config* config = Config::instance ();
405 vector<Ratio const *> ratios = Ratio::all ();
406 for (size_t i = 0; i < ratios.size(); ++i) {
407 if (ratios[i] == config->default_container ()) {
408 _container->SetSelection (i);
412 vector<DCPContentType const *> const ct = DCPContentType::all ();
413 for (size_t i = 0; i < ct.size(); ++i) {
414 if (ct[i] == config->default_dcp_content_type ()) {
415 _dcp_content_type->SetSelection (i);
419 checked_set (_still_length, config->default_still_length ());
420 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
421 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
422 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
423 checked_set (_audio_delay, config->default_audio_delay ());
424 checked_set (_issuer, config->dcp_issuer ());
427 void j2k_bandwidth_changed ()
429 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
432 void audio_delay_changed ()
434 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
437 void directory_changed ()
439 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
442 void edit_isdcf_metadata_clicked ()
444 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
446 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
450 void still_length_changed ()
452 Config::instance()->set_default_still_length (_still_length->GetValue ());
455 void container_changed ()
457 vector<Ratio const *> ratio = Ratio::all ();
458 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
461 void dcp_content_type_changed ()
463 vector<DCPContentType const *> ct = DCPContentType::all ();
464 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
467 void issuer_changed ()
469 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
472 wxSpinCtrl* _j2k_bandwidth;
473 wxSpinCtrl* _audio_delay;
474 wxButton* _isdcf_metadata_button;
475 wxSpinCtrl* _still_length;
476 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
477 DirPickerCtrl* _directory;
479 wxDirPickerCtrl* _directory;
481 wxChoice* _container;
482 wxChoice* _dcp_content_type;
486 class EncodingServersPage : public StandardPage
489 EncodingServersPage (wxSize panel_size, int border)
490 : StandardPage (panel_size, border)
493 wxString GetName () const
499 wxBitmap GetLargeIcon () const
501 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
508 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
509 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
511 vector<string> columns;
512 columns.push_back (wx_to_std (_("IP address / host name")));
513 _servers_list = new EditableList<string, ServerDialog> (
516 boost::bind (&Config::servers, Config::instance()),
517 boost::bind (&Config::set_servers, Config::instance(), _1),
518 boost::bind (&EncodingServersPage::server_column, this, _1)
521 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
523 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
526 void config_changed ()
528 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
529 _servers_list->refresh ();
532 void use_any_servers_changed ()
534 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
537 string server_column (string s)
542 wxCheckBox* _use_any_servers;
543 EditableList<string, ServerDialog>* _servers_list;
546 class KeysPage : public StandardPage
549 KeysPage (wxSize panel_size, int border)
550 : StandardPage (panel_size, border)
553 wxString GetName () const
559 wxBitmap GetLargeIcon () const
561 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
568 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
569 _panel->GetSizer()->Add (m, 0, wxALL, _border);
571 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
572 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
574 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
579 ip.SetText (_("Type"));
581 _certificates->InsertColumn (0, ip);
587 ip.SetText (_("Thumbprint"));
590 wxFont font = ip.GetFont ();
591 font.SetFamily (wxFONTFAMILY_TELETYPE);
594 _certificates->InsertColumn (1, ip);
597 certificates_sizer->Add (_certificates, 1, wxEXPAND);
600 wxSizer* s = new wxBoxSizer (wxVERTICAL);
601 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
602 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
603 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
604 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
605 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
608 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
609 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
613 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
614 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
617 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
618 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
619 wxFont font = _signer_private_key->GetFont ();
620 font.SetFamily (wxFONTFAMILY_TELETYPE);
621 _signer_private_key->SetFont (font);
622 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
623 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
624 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
627 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
628 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
629 _decryption_certificate->SetFont (font);
630 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
631 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
632 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
635 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
636 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
637 _decryption_private_key->SetFont (font);
638 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
639 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
640 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
643 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
644 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
647 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
648 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
649 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
650 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
651 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
652 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
653 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
654 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
655 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
658 void config_changed ()
660 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
662 update_certificate_list ();
663 update_signer_private_key ();
664 update_decryption_certificate ();
665 update_decryption_private_key ();
666 update_sensitivity ();
669 void add_certificate ()
671 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
673 if (d->ShowModal() == wxID_OK) {
675 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
676 _signer->certificates().add (c);
677 Config::instance()->set_signer (_signer);
678 update_certificate_list ();
679 } catch (dcp::MiscError& e) {
680 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
686 update_sensitivity ();
689 void remove_certificate ()
691 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
696 _certificates->DeleteItem (i);
697 _signer->certificates().remove (i);
698 Config::instance()->set_signer (_signer);
700 update_sensitivity ();
703 void update_certificate_list ()
705 _certificates->DeleteAllItems ();
706 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
708 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
711 _certificates->InsertItem (item);
712 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
715 _certificates->SetItem (n, 0, _("Root"));
716 } else if (n == (certs.size() - 1)) {
717 _certificates->SetItem (n, 0, _("Leaf"));
719 _certificates->SetItem (n, 0, _("Intermediate"));
726 void remake_certificates ()
728 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
729 if (d->ShowModal () == wxID_OK) {
734 d->organisational_unit (),
735 d->root_common_name (),
736 d->intermediate_common_name (),
737 d->leaf_common_name ()
741 Config::instance()->set_signer (_signer);
742 update_certificate_list ();
743 update_signer_private_key ();
749 void update_sensitivity ()
751 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
754 void update_signer_private_key ()
756 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
759 void load_signer_private_key ()
761 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
763 if (d->ShowModal() == wxID_OK) {
765 boost::filesystem::path p (wx_to_std (d->GetPath ()));
766 if (boost::filesystem::file_size (p) > 1024) {
767 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
771 _signer->set_key (dcp::file_to_string (p));
772 Config::instance()->set_signer (_signer);
773 update_signer_private_key ();
774 } catch (dcp::MiscError& e) {
775 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
781 update_sensitivity ();
785 void load_decryption_certificate ()
787 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
789 if (d->ShowModal() == wxID_OK) {
791 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
792 Config::instance()->set_decryption_certificate (c);
793 update_decryption_certificate ();
794 } catch (dcp::MiscError& e) {
795 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
802 void update_decryption_certificate ()
804 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
807 void load_decryption_private_key ()
809 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
811 if (d->ShowModal() == wxID_OK) {
813 boost::filesystem::path p (wx_to_std (d->GetPath ()));
814 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
815 update_decryption_private_key ();
816 } catch (dcp::MiscError& e) {
817 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
824 void update_decryption_private_key ()
826 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
829 void export_decryption_certificate ()
831 wxFileDialog* d = new wxFileDialog (
832 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
833 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
836 if (d->ShowModal () == wxID_OK) {
837 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
839 throw OpenFileError (wx_to_std (d->GetPath ()));
842 string const s = Config::instance()->decryption_certificate().certificate (true);
843 fwrite (s.c_str(), 1, s.length(), f);
849 wxListCtrl* _certificates;
850 wxButton* _add_certificate;
851 wxButton* _remove_certificate;
852 wxButton* _remake_certificates;
853 wxStaticText* _signer_private_key;
854 wxButton* _load_signer_private_key;
855 wxStaticText* _decryption_certificate;
856 wxButton* _load_decryption_certificate;
857 wxStaticText* _decryption_private_key;
858 wxButton* _load_decryption_private_key;
859 wxButton* _export_decryption_certificate;
860 shared_ptr<dcp::Signer> _signer;
863 class TMSPage : public StandardPage
866 TMSPage (wxSize panel_size, int border)
867 : StandardPage (panel_size, border)
870 wxString GetName () const
876 wxBitmap GetLargeIcon () const
878 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
885 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
886 table->AddGrowableCol (1, 1);
887 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
889 add_label_to_sizer (table, _panel, _("IP address"), true);
890 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
891 table->Add (_tms_ip, 1, wxEXPAND);
893 add_label_to_sizer (table, _panel, _("Target path"), true);
894 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
895 table->Add (_tms_path, 1, wxEXPAND);
897 add_label_to_sizer (table, _panel, _("User name"), true);
898 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
899 table->Add (_tms_user, 1, wxEXPAND);
901 add_label_to_sizer (table, _panel, _("Password"), true);
902 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
903 table->Add (_tms_password, 1, wxEXPAND);
905 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
906 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
907 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
908 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
911 void config_changed ()
913 Config* config = Config::instance ();
915 checked_set (_tms_ip, config->tms_ip ());
916 checked_set (_tms_path, config->tms_path ());
917 checked_set (_tms_user, config->tms_user ());
918 checked_set (_tms_password, config->tms_password ());
921 void tms_ip_changed ()
923 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
926 void tms_path_changed ()
928 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
931 void tms_user_changed ()
933 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
936 void tms_password_changed ()
938 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
942 wxTextCtrl* _tms_path;
943 wxTextCtrl* _tms_user;
944 wxTextCtrl* _tms_password;
947 class KDMEmailPage : public StandardPage
951 KDMEmailPage (wxSize panel_size, int border)
953 /* We have to force both width and height of this one */
954 : StandardPage (wxSize (480, 128), border)
956 : StandardPage (panel_size, border)
960 wxString GetName () const
962 return _("KDM Email");
966 wxBitmap GetLargeIcon () const
968 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
975 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
976 table->AddGrowableCol (1, 1);
977 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
979 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
981 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
982 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
983 s->Add (_mail_server, 1, wxEXPAND | wxALL);
984 add_label_to_sizer (s, _panel, _("port"), false);
985 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
986 _mail_port->SetRange (0, 65535);
988 table->Add (s, 1, wxEXPAND | wxALL);
991 add_label_to_sizer (table, _panel, _("Mail user name"), true);
992 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
993 table->Add (_mail_user, 1, wxEXPAND | wxALL);
995 add_label_to_sizer (table, _panel, _("Mail password"), true);
996 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
997 table->Add (_mail_password, 1, wxEXPAND | wxALL);
999 add_label_to_sizer (table, _panel, _("Subject"), true);
1000 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1001 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1003 add_label_to_sizer (table, _panel, _("From address"), true);
1004 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1005 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1007 add_label_to_sizer (table, _panel, _("CC address"), true);
1008 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1009 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1011 add_label_to_sizer (table, _panel, _("BCC address"), true);
1012 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1013 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1015 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1016 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1018 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1019 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1021 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1022 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1023 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1024 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1025 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1026 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1027 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1028 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1029 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1030 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1033 void config_changed ()
1035 Config* config = Config::instance ();
1037 checked_set (_mail_server, config->mail_server ());
1038 checked_set (_mail_port, config->mail_port ());
1039 checked_set (_mail_user, config->mail_user ());
1040 checked_set (_mail_password, config->mail_password ());
1041 checked_set (_kdm_subject, config->kdm_subject ());
1042 checked_set (_kdm_from, config->kdm_from ());
1043 checked_set (_kdm_cc, config->kdm_cc ());
1044 checked_set (_kdm_bcc, config->kdm_bcc ());
1045 checked_set (_kdm_email, Config::instance()->kdm_email ());
1048 void mail_server_changed ()
1050 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1053 void mail_port_changed ()
1055 Config::instance()->set_mail_port (_mail_port->GetValue ());
1058 void mail_user_changed ()
1060 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1063 void mail_password_changed ()
1065 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1068 void kdm_subject_changed ()
1070 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1073 void kdm_from_changed ()
1075 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1078 void kdm_cc_changed ()
1080 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1083 void kdm_bcc_changed ()
1085 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1088 void kdm_email_changed ()
1090 if (_kdm_email->GetValue().IsEmpty ()) {
1091 /* Sometimes we get sent an erroneous notification that the email
1092 is empty; I don't know why.
1096 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1099 void reset_kdm_email ()
1101 Config::instance()->reset_kdm_email ();
1102 checked_set (_kdm_email, Config::instance()->kdm_email ());
1105 wxTextCtrl* _mail_server;
1106 wxSpinCtrl* _mail_port;
1107 wxTextCtrl* _mail_user;
1108 wxTextCtrl* _mail_password;
1109 wxTextCtrl* _kdm_subject;
1110 wxTextCtrl* _kdm_from;
1111 wxTextCtrl* _kdm_cc;
1112 wxTextCtrl* _kdm_bcc;
1113 wxTextCtrl* _kdm_email;
1114 wxButton* _reset_kdm_email;
1117 /** @class AdvancedPage
1118 * @brief Advanced page of the preferences dialog.
1120 class AdvancedPage : public StockPage
1123 AdvancedPage (wxSize panel_size, int border)
1124 : StockPage (Kind_Advanced, panel_size, border)
1125 , _maximum_j2k_bandwidth (0)
1126 , _allow_any_dcp_frame_rate (0)
1131 , _log_debug_decode (0)
1132 , _log_debug_encode (0)
1138 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1139 table->AddGrowableCol (1, 1);
1140 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1143 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1144 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1145 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1146 s->Add (_maximum_j2k_bandwidth, 1);
1147 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1151 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1152 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1153 table->AddSpacer (0);
1156 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1157 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1159 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1160 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1164 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1165 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1166 t->Add (_log_general, 1, wxEXPAND | wxALL);
1167 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1168 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1169 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1170 t->Add (_log_error, 1, wxEXPAND | wxALL);
1171 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1172 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1173 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1174 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1175 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1176 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1177 table->Add (t, 0, wxALL, 6);
1180 #ifdef DCPOMATIC_WINDOWS
1181 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1182 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1183 table->AddSpacer (0);
1186 _maximum_j2k_bandwidth->SetRange (1, 1000);
1187 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1188 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1189 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1190 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1191 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1192 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1193 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1194 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1195 #ifdef DCPOMATIC_WINDOWS
1196 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1200 void config_changed ()
1202 Config* config = Config::instance ();
1204 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1205 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1206 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1207 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1208 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1209 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1210 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1211 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1212 #ifdef DCPOMATIC_WINDOWS
1213 checked_set (_win32_console, config->win32_console());
1217 void maximum_j2k_bandwidth_changed ()
1219 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1222 void allow_any_dcp_frame_rate_changed ()
1224 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1230 if (_log_general->GetValue ()) {
1231 types |= Log::TYPE_GENERAL;
1233 if (_log_warning->GetValue ()) {
1234 types |= Log::TYPE_WARNING;
1236 if (_log_error->GetValue ()) {
1237 types |= Log::TYPE_ERROR;
1239 if (_log_timing->GetValue ()) {
1240 types |= Log::TYPE_TIMING;
1242 if (_log_debug_decode->GetValue ()) {
1243 types |= Log::TYPE_DEBUG_DECODE;
1245 if (_log_debug_encode->GetValue ()) {
1246 types |= Log::TYPE_DEBUG_ENCODE;
1248 Config::instance()->set_log_types (types);
1251 #ifdef DCPOMATIC_WINDOWS
1252 void win32_console_changed ()
1254 Config::instance()->set_win32_console (_win32_console->GetValue ());
1258 wxSpinCtrl* _maximum_j2k_bandwidth;
1259 wxCheckBox* _allow_any_dcp_frame_rate;
1260 wxCheckBox* _log_general;
1261 wxCheckBox* _log_warning;
1262 wxCheckBox* _log_error;
1263 wxCheckBox* _log_timing;
1264 wxCheckBox* _log_debug_decode;
1265 wxCheckBox* _log_debug_encode;
1266 #ifdef DCPOMATIC_WINDOWS
1267 wxCheckBox* _win32_console;
1271 wxPreferencesEditor*
1272 create_config_dialog ()
1274 wxPreferencesEditor* e = new wxPreferencesEditor ();
1276 #ifdef DCPOMATIC_OSX
1277 /* Width that we force some of the config panels to be on OSX so that
1278 the containing window doesn't shrink too much when we select those panels.
1279 This is obviously an unpleasant hack.
1281 wxSize ps = wxSize (520, -1);
1282 int const border = 16;
1284 wxSize ps = wxSize (-1, -1);
1285 int const border = 8;
1288 e->AddPage (new GeneralPage (ps, border));
1289 e->AddPage (new DefaultsPage (ps, border));
1290 e->AddPage (new EncodingServersPage (ps, border));
1291 e->AddPage (new KeysPage (ps, border));
1292 e->AddPage (new TMSPage (ps, border));
1293 e->AddPage (new KDMEmailPage (ps, border));
1294 e->AddPage (new AdvancedPage (ps, border));