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_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>
49 #include <boost/foreach.hpp>
57 using boost::shared_ptr;
58 using boost::lexical_cast;
59 using boost::function;
64 Page (wxSize panel_size, int border)
67 , _panel_size (panel_size)
68 , _window_exists (false)
70 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
76 wxWindow* create_window (wxWindow* parent)
78 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
79 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
83 _window_exists = true;
86 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
95 virtual void config_changed () = 0;
96 virtual void setup () = 0;
98 void config_changed_wrapper ()
100 if (_window_exists) {
105 void window_destroyed ()
107 _window_exists = false;
111 boost::signals2::scoped_connection _config_connection;
115 class StockPage : public wxStockPreferencesPage, public Page
118 StockPage (Kind kind, wxSize panel_size, int border)
119 : wxStockPreferencesPage (kind)
120 , Page (panel_size, border)
123 wxWindow* CreateWindow (wxWindow* parent)
125 return create_window (parent);
129 class StandardPage : public wxPreferencesPage, public Page
132 StandardPage (wxSize panel_size, int border)
133 : Page (panel_size, border)
136 wxWindow* CreateWindow (wxWindow* parent)
138 return create_window (parent);
142 class GeneralPage : public StockPage
145 GeneralPage (wxSize panel_size, int border)
146 : StockPage (Kind_General, panel_size, border)
152 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
153 table->AddGrowableCol (1, 1);
154 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
156 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
157 table->Add (_set_language, 1);
158 _language = new wxChoice (_panel, wxID_ANY);
159 _language->Append (wxT ("Deutsch"));
160 _language->Append (wxT ("English"));
161 _language->Append (wxT ("Español"));
162 _language->Append (wxT ("Français"));
163 _language->Append (wxT ("Italiano"));
164 _language->Append (wxT ("Nederlands"));
165 _language->Append (wxT ("Svenska"));
166 _language->Append (wxT ("Русский"));
167 _language->Append (wxT ("Polski"));
168 _language->Append (wxT ("Danske"));
169 table->Add (_language);
171 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
172 wxFont font = restart->GetFont();
173 font.SetStyle (wxFONTSTYLE_ITALIC);
174 font.SetPointSize (font.GetPointSize() - 1);
175 restart->SetFont (font);
176 table->AddSpacer (0);
178 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
179 _num_local_encoding_threads = new wxSpinCtrl (_panel);
180 table->Add (_num_local_encoding_threads, 1);
182 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
183 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
184 table->AddSpacer (0);
186 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
187 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
188 table->AddSpacer (0);
190 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
191 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
193 _num_local_encoding_threads->SetRange (1, 128);
194 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
196 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
197 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
200 void config_changed ()
202 Config* config = Config::instance ();
204 checked_set (_set_language, config->language ());
206 if (config->language().get_value_or ("") == "fr") {
207 checked_set (_language, 3);
208 } else if (config->language().get_value_or ("") == "it") {
209 checked_set (_language, 4);
210 } else if (config->language().get_value_or ("") == "es") {
211 checked_set (_language, 2);
212 } else if (config->language().get_value_or ("") == "sv") {
213 checked_set (_language, 6);
214 } else if (config->language().get_value_or ("") == "de") {
215 checked_set (_language, 0);
216 } else if (config->language().get_value_or ("") == "nl") {
217 checked_set (_language, 5);
218 } else if (config->language().get_value_or ("") == "ru") {
219 checked_set (_language, 7);
220 } else if (config->language().get_value_or ("") == "pl") {
221 checked_set (_language, 8);
222 } else if (config->language().get_value_or ("") == "da") {
223 checked_set (_language, 9);
225 _language->SetSelection (1);
228 setup_language_sensitivity ();
230 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
231 checked_set (_check_for_updates, config->check_for_updates ());
232 checked_set (_check_for_test_updates, config->check_for_test_updates ());
235 void setup_language_sensitivity ()
237 _language->Enable (_set_language->GetValue ());
240 void set_language_changed ()
242 setup_language_sensitivity ();
243 if (_set_language->GetValue ()) {
246 Config::instance()->unset_language ();
250 void language_changed ()
252 switch (_language->GetSelection ()) {
254 Config::instance()->set_language ("de");
257 Config::instance()->set_language ("en");
260 Config::instance()->set_language ("es");
263 Config::instance()->set_language ("fr");
266 Config::instance()->set_language ("it");
269 Config::instance()->set_language ("nl");
272 Config::instance()->set_language ("sv");
275 Config::instance()->set_language ("ru");
278 Config::instance()->set_language ("pl");
281 Config::instance()->set_language ("da");
286 void check_for_updates_changed ()
288 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
291 void check_for_test_updates_changed ()
293 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
296 void num_local_encoding_threads_changed ()
298 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
301 wxCheckBox* _set_language;
303 wxSpinCtrl* _num_local_encoding_threads;
304 wxCheckBox* _check_for_updates;
305 wxCheckBox* _check_for_test_updates;
308 class DefaultsPage : public StandardPage
311 DefaultsPage (wxSize panel_size, int border)
312 : StandardPage (panel_size, border)
315 wxString GetName () const
317 return _("Defaults");
321 wxBitmap GetLargeIcon () const
323 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
330 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
331 table->AddGrowableCol (1, 1);
332 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
335 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
336 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
337 _still_length = new wxSpinCtrl (_panel);
338 s->Add (_still_length);
339 add_label_to_sizer (s, _panel, _("s"), false);
343 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
344 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
345 _directory = new DirPickerCtrl (_panel);
347 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
349 table->Add (_directory, 1, wxEXPAND);
351 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
352 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
353 table->Add (_isdcf_metadata_button);
355 add_label_to_sizer (table, _panel, _("Default container"), true);
356 _container = new wxChoice (_panel, wxID_ANY);
357 table->Add (_container);
359 add_label_to_sizer (table, _panel, _("Default content type"), true);
360 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
361 table->Add (_dcp_content_type);
364 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
365 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
366 _j2k_bandwidth = new wxSpinCtrl (_panel);
367 s->Add (_j2k_bandwidth);
368 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
373 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
374 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
375 _audio_delay = new wxSpinCtrl (_panel);
376 s->Add (_audio_delay);
377 add_label_to_sizer (s, _panel, _("ms"), false);
381 add_label_to_sizer (table, _panel, _("Default issuer"), true);
382 _issuer = new wxTextCtrl (_panel, wxID_ANY);
383 table->Add (_issuer, 1, wxEXPAND);
385 _still_length->SetRange (1, 3600);
386 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
388 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
390 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
392 vector<Ratio const *> ratios = Ratio::all ();
393 for (size_t i = 0; i < ratios.size(); ++i) {
394 _container->Append (std_to_wx (ratios[i]->nickname ()));
397 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
399 vector<DCPContentType const *> const ct = DCPContentType::all ();
400 for (size_t i = 0; i < ct.size(); ++i) {
401 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
404 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
406 _j2k_bandwidth->SetRange (50, 250);
407 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
409 _audio_delay->SetRange (-1000, 1000);
410 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
412 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
415 void config_changed ()
417 Config* config = Config::instance ();
419 vector<Ratio const *> ratios = Ratio::all ();
420 for (size_t i = 0; i < ratios.size(); ++i) {
421 if (ratios[i] == config->default_container ()) {
422 _container->SetSelection (i);
426 vector<DCPContentType const *> const ct = DCPContentType::all ();
427 for (size_t i = 0; i < ct.size(); ++i) {
428 if (ct[i] == config->default_dcp_content_type ()) {
429 _dcp_content_type->SetSelection (i);
433 checked_set (_still_length, config->default_still_length ());
434 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
435 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
436 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
437 checked_set (_audio_delay, config->default_audio_delay ());
438 checked_set (_issuer, config->dcp_issuer ());
441 void j2k_bandwidth_changed ()
443 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
446 void audio_delay_changed ()
448 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
451 void directory_changed ()
453 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
456 void edit_isdcf_metadata_clicked ()
458 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
460 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
464 void still_length_changed ()
466 Config::instance()->set_default_still_length (_still_length->GetValue ());
469 void container_changed ()
471 vector<Ratio const *> ratio = Ratio::all ();
472 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
475 void dcp_content_type_changed ()
477 vector<DCPContentType const *> ct = DCPContentType::all ();
478 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
481 void issuer_changed ()
483 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
486 wxSpinCtrl* _j2k_bandwidth;
487 wxSpinCtrl* _audio_delay;
488 wxButton* _isdcf_metadata_button;
489 wxSpinCtrl* _still_length;
490 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
491 DirPickerCtrl* _directory;
493 wxDirPickerCtrl* _directory;
495 wxChoice* _container;
496 wxChoice* _dcp_content_type;
500 class EncodingServersPage : public StandardPage
503 EncodingServersPage (wxSize panel_size, int border)
504 : StandardPage (panel_size, border)
507 wxString GetName () const
513 wxBitmap GetLargeIcon () const
515 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
522 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
523 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
525 vector<string> columns;
526 columns.push_back (wx_to_std (_("IP address / host name")));
527 _servers_list = new EditableList<string, ServerDialog> (
530 boost::bind (&Config::servers, Config::instance()),
531 boost::bind (&Config::set_servers, Config::instance(), _1),
532 boost::bind (&EncodingServersPage::server_column, this, _1)
535 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
537 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
540 void config_changed ()
542 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
543 _servers_list->refresh ();
546 void use_any_servers_changed ()
548 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
551 string server_column (string s)
556 wxCheckBox* _use_any_servers;
557 EditableList<string, ServerDialog>* _servers_list;
560 class CertificateChainEditor : public wxPanel
563 CertificateChainEditor (
567 function<void (shared_ptr<dcp::CertificateChain>)> set,
568 function<shared_ptr<const dcp::CertificateChain> (void)> get
574 wxFont subheading_font (*wxNORMAL_FONT);
575 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
577 _sizer = new wxBoxSizer (wxVERTICAL);
580 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
581 m->SetFont (subheading_font);
582 _sizer->Add (m, 0, wxALL, border);
585 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
586 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
588 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
593 ip.SetText (_("Type"));
595 _certificates->InsertColumn (0, ip);
601 ip.SetText (_("Thumbprint"));
604 wxFont font = ip.GetFont ();
605 font.SetFamily (wxFONTFAMILY_TELETYPE);
608 _certificates->InsertColumn (1, ip);
611 certificates_sizer->Add (_certificates, 1, wxEXPAND);
614 wxSizer* s = new wxBoxSizer (wxVERTICAL);
615 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
616 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
617 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
618 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
619 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
622 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
623 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
626 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
627 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
628 wxFont font = _private_key->GetFont ();
629 font.SetFamily (wxFONTFAMILY_TELETYPE);
630 _private_key->SetFont (font);
631 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
632 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
633 table->Add (_load_private_key, wxGBPosition (r, 2));
636 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
637 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
638 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
639 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
642 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::add_certificate, this));
643 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remove_certificate, this));
644 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
645 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
646 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remake_certificates, this));
647 _load_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::load_private_key, this));
649 SetSizerAndFit (_sizer);
652 void config_changed ()
654 _chain.reset (new dcp::CertificateChain (*_get().get ()));
656 update_certificate_list ();
657 update_private_key ();
658 update_sensitivity ();
661 void add_button (wxWindow* button)
663 _button_sizer->Add (button);
668 void add_certificate ()
670 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
672 if (d->ShowModal() == wxID_OK) {
674 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
677 update_certificate_list ();
678 } catch (dcp::MiscError& e) {
679 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
685 update_sensitivity ();
688 void remove_certificate ()
690 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
695 _certificates->DeleteItem (i);
699 update_sensitivity ();
702 void update_certificate_list ()
704 _certificates->DeleteAllItems ();
706 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
707 BOOST_FOREACH (dcp::Certificate const & i, certs) {
710 _certificates->InsertItem (item);
711 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
714 _certificates->SetItem (n, 0, _("Root"));
715 } else if (n == (certs.size() - 1)) {
716 _certificates->SetItem (n, 0, _("Leaf"));
718 _certificates->SetItem (n, 0, _("Intermediate"));
725 void remake_certificates ()
727 shared_ptr<const dcp::CertificateChain> chain = _get ();
729 string intermediate_common_name;
730 if (chain->root_to_leaf().size() >= 3) {
731 dcp::CertificateChain::List all = chain->root_to_leaf ();
732 dcp::CertificateChain::List::iterator i = all.begin ();
734 intermediate_common_name = i->subject_common_name ();
737 MakeChainDialog* d = new MakeChainDialog (
739 chain->root().subject_organization_name (),
740 chain->root().subject_organizational_unit_name (),
741 chain->root().subject_common_name (),
742 intermediate_common_name,
743 chain->leaf().subject_common_name ()
746 if (d->ShowModal () == wxID_OK) {
748 new dcp::CertificateChain (
751 d->organisational_unit (),
752 d->root_common_name (),
753 d->intermediate_common_name (),
754 d->leaf_common_name ()
759 update_certificate_list ();
760 update_private_key ();
766 void update_sensitivity ()
768 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
771 void update_private_key ()
773 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
777 void load_private_key ()
779 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
781 if (d->ShowModal() == wxID_OK) {
783 boost::filesystem::path p (wx_to_std (d->GetPath ()));
784 if (boost::filesystem::file_size (p) > 1024) {
785 error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
789 _chain->set_key (dcp::file_to_string (p));
791 update_private_key ();
792 } catch (dcp::MiscError& e) {
793 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
799 update_sensitivity ();
802 wxListCtrl* _certificates;
803 wxButton* _add_certificate;
804 wxButton* _remove_certificate;
805 wxButton* _remake_certificates;
806 wxStaticText* _private_key;
807 wxButton* _load_private_key;
809 wxBoxSizer* _button_sizer;
810 shared_ptr<dcp::CertificateChain> _chain;
811 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
812 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
815 class KeysPage : public StandardPage
818 KeysPage (wxSize panel_size, int border)
819 : StandardPage (panel_size, border)
822 wxString GetName () const
828 wxBitmap GetLargeIcon () const
830 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
838 _signer = new CertificateChainEditor (
839 _panel, _("Signing DCPs and KDMs"), _border,
840 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
841 boost::bind (&Config::signer_chain, Config::instance ())
844 _panel->GetSizer()->Add (_signer);
846 _decryption = new CertificateChainEditor (
847 _panel, _("Decrypting DCPs"), _border,
848 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
849 boost::bind (&Config::decryption_chain, Config::instance ())
852 _panel->GetSizer()->Add (_decryption);
854 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
855 _decryption->add_button (_export_decryption_certificate);
857 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
860 void export_decryption_certificate ()
862 wxFileDialog* d = new wxFileDialog (
863 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
864 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
867 if (d->ShowModal () == wxID_OK) {
868 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
870 throw OpenFileError (wx_to_std (d->GetPath ()));
873 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
874 fwrite (s.c_str(), 1, s.length(), f);
880 void config_changed ()
882 _signer->config_changed ();
883 _decryption->config_changed ();
886 CertificateChainEditor* _signer;
887 CertificateChainEditor* _decryption;
888 wxButton* _export_decryption_certificate;
891 class TMSPage : public StandardPage
894 TMSPage (wxSize panel_size, int border)
895 : StandardPage (panel_size, border)
898 wxString GetName () const
904 wxBitmap GetLargeIcon () const
906 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
913 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
914 table->AddGrowableCol (1, 1);
915 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
917 add_label_to_sizer (table, _panel, _("Protocol"), true);
918 _tms_protocol = new wxChoice (_panel, wxID_ANY);
919 table->Add (_tms_protocol, 1, wxEXPAND);
921 add_label_to_sizer (table, _panel, _("IP address"), true);
922 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
923 table->Add (_tms_ip, 1, wxEXPAND);
925 add_label_to_sizer (table, _panel, _("Target path"), true);
926 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
927 table->Add (_tms_path, 1, wxEXPAND);
929 add_label_to_sizer (table, _panel, _("User name"), true);
930 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
931 table->Add (_tms_user, 1, wxEXPAND);
933 add_label_to_sizer (table, _panel, _("Password"), true);
934 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
935 table->Add (_tms_password, 1, wxEXPAND);
937 _tms_protocol->Append (_("SCP (for AAM)"));
938 _tms_protocol->Append (_("FTP (for Dolby)"));
940 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
941 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
942 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
943 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
944 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
947 void config_changed ()
949 Config* config = Config::instance ();
951 checked_set (_tms_protocol, config->tms_protocol ());
952 checked_set (_tms_ip, config->tms_ip ());
953 checked_set (_tms_path, config->tms_path ());
954 checked_set (_tms_user, config->tms_user ());
955 checked_set (_tms_password, config->tms_password ());
958 void tms_protocol_changed ()
960 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
963 void tms_ip_changed ()
965 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
968 void tms_path_changed ()
970 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
973 void tms_user_changed ()
975 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
978 void tms_password_changed ()
980 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
983 wxChoice* _tms_protocol;
985 wxTextCtrl* _tms_path;
986 wxTextCtrl* _tms_user;
987 wxTextCtrl* _tms_password;
990 class KDMEmailPage : public StandardPage
994 KDMEmailPage (wxSize panel_size, int border)
996 /* We have to force both width and height of this one */
997 : StandardPage (wxSize (480, 128), border)
999 : StandardPage (panel_size, border)
1003 wxString GetName () const
1005 return _("KDM Email");
1008 #ifdef DCPOMATIC_OSX
1009 wxBitmap GetLargeIcon () const
1011 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1018 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1019 table->AddGrowableCol (1, 1);
1020 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1022 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1024 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1025 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1026 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1027 add_label_to_sizer (s, _panel, _("port"), false);
1028 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1029 _mail_port->SetRange (0, 65535);
1030 s->Add (_mail_port);
1031 table->Add (s, 1, wxEXPAND | wxALL);
1034 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1035 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1036 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1038 add_label_to_sizer (table, _panel, _("Mail password"), true);
1039 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1040 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1042 add_label_to_sizer (table, _panel, _("Subject"), true);
1043 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1044 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1046 add_label_to_sizer (table, _panel, _("From address"), true);
1047 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1048 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1050 add_label_to_sizer (table, _panel, _("CC address"), true);
1051 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1052 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1054 add_label_to_sizer (table, _panel, _("BCC address"), true);
1055 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1056 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1058 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1059 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1061 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1062 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1064 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1065 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1066 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1067 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1068 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1069 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1070 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1071 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1072 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1073 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1076 void config_changed ()
1078 Config* config = Config::instance ();
1080 checked_set (_mail_server, config->mail_server ());
1081 checked_set (_mail_port, config->mail_port ());
1082 checked_set (_mail_user, config->mail_user ());
1083 checked_set (_mail_password, config->mail_password ());
1084 checked_set (_kdm_subject, config->kdm_subject ());
1085 checked_set (_kdm_from, config->kdm_from ());
1086 checked_set (_kdm_cc, config->kdm_cc ());
1087 checked_set (_kdm_bcc, config->kdm_bcc ());
1088 checked_set (_kdm_email, Config::instance()->kdm_email ());
1091 void mail_server_changed ()
1093 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1096 void mail_port_changed ()
1098 Config::instance()->set_mail_port (_mail_port->GetValue ());
1101 void mail_user_changed ()
1103 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1106 void mail_password_changed ()
1108 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1111 void kdm_subject_changed ()
1113 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1116 void kdm_from_changed ()
1118 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1121 void kdm_cc_changed ()
1123 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1126 void kdm_bcc_changed ()
1128 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1131 void kdm_email_changed ()
1133 if (_kdm_email->GetValue().IsEmpty ()) {
1134 /* Sometimes we get sent an erroneous notification that the email
1135 is empty; I don't know why.
1139 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1142 void reset_kdm_email ()
1144 Config::instance()->reset_kdm_email ();
1145 checked_set (_kdm_email, Config::instance()->kdm_email ());
1148 wxTextCtrl* _mail_server;
1149 wxSpinCtrl* _mail_port;
1150 wxTextCtrl* _mail_user;
1151 wxTextCtrl* _mail_password;
1152 wxTextCtrl* _kdm_subject;
1153 wxTextCtrl* _kdm_from;
1154 wxTextCtrl* _kdm_cc;
1155 wxTextCtrl* _kdm_bcc;
1156 wxTextCtrl* _kdm_email;
1157 wxButton* _reset_kdm_email;
1160 /** @class AdvancedPage
1161 * @brief Advanced page of the preferences dialog.
1163 class AdvancedPage : public StockPage
1166 AdvancedPage (wxSize panel_size, int border)
1167 : StockPage (Kind_Advanced, panel_size, border)
1168 , _maximum_j2k_bandwidth (0)
1169 , _allow_any_dcp_frame_rate (0)
1174 , _log_debug_decode (0)
1175 , _log_debug_encode (0)
1181 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1182 table->AddGrowableCol (1, 1);
1183 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1186 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1187 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1188 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1189 s->Add (_maximum_j2k_bandwidth, 1);
1190 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1194 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1195 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1196 table->AddSpacer (0);
1199 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1200 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1202 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1203 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1207 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1208 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1209 t->Add (_log_general, 1, wxEXPAND | wxALL);
1210 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1211 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1212 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1213 t->Add (_log_error, 1, wxEXPAND | wxALL);
1214 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1215 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1216 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1217 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1218 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1219 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1220 table->Add (t, 0, wxALL, 6);
1223 #ifdef DCPOMATIC_WINDOWS
1224 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1225 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1226 table->AddSpacer (0);
1229 _maximum_j2k_bandwidth->SetRange (1, 1000);
1230 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1231 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1232 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1233 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1234 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1235 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1236 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1237 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1238 #ifdef DCPOMATIC_WINDOWS
1239 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1243 void config_changed ()
1245 Config* config = Config::instance ();
1247 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1248 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1249 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1250 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1251 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1252 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1253 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1254 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1255 #ifdef DCPOMATIC_WINDOWS
1256 checked_set (_win32_console, config->win32_console());
1260 void maximum_j2k_bandwidth_changed ()
1262 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1265 void allow_any_dcp_frame_rate_changed ()
1267 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1273 if (_log_general->GetValue ()) {
1274 types |= Log::TYPE_GENERAL;
1276 if (_log_warning->GetValue ()) {
1277 types |= Log::TYPE_WARNING;
1279 if (_log_error->GetValue ()) {
1280 types |= Log::TYPE_ERROR;
1282 if (_log_timing->GetValue ()) {
1283 types |= Log::TYPE_TIMING;
1285 if (_log_debug_decode->GetValue ()) {
1286 types |= Log::TYPE_DEBUG_DECODE;
1288 if (_log_debug_encode->GetValue ()) {
1289 types |= Log::TYPE_DEBUG_ENCODE;
1291 Config::instance()->set_log_types (types);
1294 #ifdef DCPOMATIC_WINDOWS
1295 void win32_console_changed ()
1297 Config::instance()->set_win32_console (_win32_console->GetValue ());
1301 wxSpinCtrl* _maximum_j2k_bandwidth;
1302 wxCheckBox* _allow_any_dcp_frame_rate;
1303 wxCheckBox* _log_general;
1304 wxCheckBox* _log_warning;
1305 wxCheckBox* _log_error;
1306 wxCheckBox* _log_timing;
1307 wxCheckBox* _log_debug_decode;
1308 wxCheckBox* _log_debug_encode;
1309 #ifdef DCPOMATIC_WINDOWS
1310 wxCheckBox* _win32_console;
1314 wxPreferencesEditor*
1315 create_config_dialog ()
1317 wxPreferencesEditor* e = new wxPreferencesEditor ();
1319 #ifdef DCPOMATIC_OSX
1320 /* Width that we force some of the config panels to be on OSX so that
1321 the containing window doesn't shrink too much when we select those panels.
1322 This is obviously an unpleasant hack.
1324 wxSize ps = wxSize (520, -1);
1325 int const border = 16;
1327 wxSize ps = wxSize (-1, -1);
1328 int const border = 8;
1331 e->AddPage (new GeneralPage (ps, border));
1332 e->AddPage (new DefaultsPage (ps, border));
1333 e->AddPage (new EncodingServersPage (ps, border));
1334 e->AddPage (new KeysPage (ps, border));
1335 e->AddPage (new TMSPage (ps, border));
1336 e->AddPage (new KDMEmailPage (ps, border));
1337 e->AddPage (new AdvancedPage (ps, border));