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 ());
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 wxSizer* 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, 200), 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);
667 void add_certificate ()
669 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
671 if (d->ShowModal() == wxID_OK) {
673 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
676 update_certificate_list ();
677 } catch (dcp::MiscError& e) {
678 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
684 update_sensitivity ();
687 void remove_certificate ()
689 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
694 _certificates->DeleteItem (i);
698 update_sensitivity ();
701 void update_certificate_list ()
703 _certificates->DeleteAllItems ();
705 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
706 BOOST_FOREACH (dcp::Certificate const & i, certs) {
709 _certificates->InsertItem (item);
710 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
713 _certificates->SetItem (n, 0, _("Root"));
714 } else if (n == (certs.size() - 1)) {
715 _certificates->SetItem (n, 0, _("Leaf"));
717 _certificates->SetItem (n, 0, _("Intermediate"));
724 void remake_certificates ()
726 shared_ptr<const dcp::CertificateChain> chain = _get ();
728 string intermediate_common_name;
729 if (chain->root_to_leaf().size() >= 3) {
730 dcp::CertificateChain::List all = chain->root_to_leaf ();
731 dcp::CertificateChain::List::iterator i = all.begin ();
733 intermediate_common_name = i->subject_common_name ();
736 MakeChainDialog* d = new MakeChainDialog (
738 chain->root().subject_organization_name (),
739 chain->root().subject_organizational_unit_name (),
740 chain->root().subject_common_name (),
741 intermediate_common_name,
742 chain->leaf().subject_common_name ()
745 if (d->ShowModal () == wxID_OK) {
747 new dcp::CertificateChain (
750 d->organisational_unit (),
751 d->root_common_name (),
752 d->intermediate_common_name (),
753 d->leaf_common_name ()
758 update_certificate_list ();
759 update_private_key ();
765 void update_sensitivity ()
767 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
770 void update_private_key ()
772 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
775 void load_private_key ()
777 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
779 if (d->ShowModal() == wxID_OK) {
781 boost::filesystem::path p (wx_to_std (d->GetPath ()));
782 if (boost::filesystem::file_size (p) > 1024) {
783 error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
787 _chain->set_key (dcp::file_to_string (p));
789 update_private_key ();
790 } catch (dcp::MiscError& e) {
791 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
797 update_sensitivity ();
801 wxListCtrl* _certificates;
802 wxButton* _add_certificate;
803 wxButton* _remove_certificate;
804 wxButton* _remake_certificates;
805 wxStaticText* _private_key;
806 wxButton* _load_private_key;
807 wxBoxSizer* _button_sizer;
808 shared_ptr<dcp::CertificateChain> _chain;
809 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
810 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
813 class KeysPage : public StandardPage
816 KeysPage (wxSize panel_size, int border)
817 : StandardPage (panel_size, border)
820 wxString GetName () const
826 wxBitmap GetLargeIcon () const
828 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
836 _signer = new CertificateChainEditor (
837 _panel, _("Signing DCPs and KDMs"), _border,
838 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
839 boost::bind (&Config::signer_chain, Config::instance ())
842 _panel->GetSizer()->Add (_signer);
844 _decryption = new CertificateChainEditor (
845 _panel, _("Decrypting DCPs"), _border,
846 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
847 boost::bind (&Config::decryption_chain, Config::instance ())
850 _panel->GetSizer()->Add (_decryption);
852 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
853 _decryption->add_button (_export_decryption_certificate);
855 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
858 void export_decryption_certificate ()
860 wxFileDialog* d = new wxFileDialog (
861 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
862 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
865 if (d->ShowModal () == wxID_OK) {
866 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
868 throw OpenFileError (wx_to_std (d->GetPath ()));
871 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
872 fwrite (s.c_str(), 1, s.length(), f);
878 void config_changed ()
880 _signer->config_changed ();
881 _decryption->config_changed ();
884 CertificateChainEditor* _signer;
885 CertificateChainEditor* _decryption;
886 wxButton* _export_decryption_certificate;
889 class TMSPage : public StandardPage
892 TMSPage (wxSize panel_size, int border)
893 : StandardPage (panel_size, border)
896 wxString GetName () const
902 wxBitmap GetLargeIcon () const
904 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
911 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
912 table->AddGrowableCol (1, 1);
913 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
915 add_label_to_sizer (table, _panel, _("Protocol"), true);
916 _tms_protocol = new wxChoice (_panel, wxID_ANY);
917 table->Add (_tms_protocol, 1, wxEXPAND);
919 add_label_to_sizer (table, _panel, _("IP address"), true);
920 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
921 table->Add (_tms_ip, 1, wxEXPAND);
923 add_label_to_sizer (table, _panel, _("Target path"), true);
924 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
925 table->Add (_tms_path, 1, wxEXPAND);
927 add_label_to_sizer (table, _panel, _("User name"), true);
928 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
929 table->Add (_tms_user, 1, wxEXPAND);
931 add_label_to_sizer (table, _panel, _("Password"), true);
932 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
933 table->Add (_tms_password, 1, wxEXPAND);
935 _tms_protocol->Append (_("SCP (for AAM)"));
936 _tms_protocol->Append (_("FTP (for Dolby)"));
938 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
939 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
940 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
941 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
942 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
945 void config_changed ()
947 Config* config = Config::instance ();
949 checked_set (_tms_protocol, config->tms_protocol ());
950 checked_set (_tms_ip, config->tms_ip ());
951 checked_set (_tms_path, config->tms_path ());
952 checked_set (_tms_user, config->tms_user ());
953 checked_set (_tms_password, config->tms_password ());
956 void tms_protocol_changed ()
958 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
961 void tms_ip_changed ()
963 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
966 void tms_path_changed ()
968 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
971 void tms_user_changed ()
973 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
976 void tms_password_changed ()
978 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
981 wxChoice* _tms_protocol;
983 wxTextCtrl* _tms_path;
984 wxTextCtrl* _tms_user;
985 wxTextCtrl* _tms_password;
988 class KDMEmailPage : public StandardPage
992 KDMEmailPage (wxSize panel_size, int border)
994 /* We have to force both width and height of this one */
995 : StandardPage (wxSize (480, 128), border)
997 : StandardPage (panel_size, border)
1001 wxString GetName () const
1003 return _("KDM Email");
1006 #ifdef DCPOMATIC_OSX
1007 wxBitmap GetLargeIcon () const
1009 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1016 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1017 table->AddGrowableCol (1, 1);
1018 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1020 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1022 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1023 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1024 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1025 add_label_to_sizer (s, _panel, _("port"), false);
1026 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1027 _mail_port->SetRange (0, 65535);
1028 s->Add (_mail_port);
1029 table->Add (s, 1, wxEXPAND | wxALL);
1032 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1033 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1034 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1036 add_label_to_sizer (table, _panel, _("Mail password"), true);
1037 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1038 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1040 add_label_to_sizer (table, _panel, _("Subject"), true);
1041 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1042 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1044 add_label_to_sizer (table, _panel, _("From address"), true);
1045 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1046 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1048 add_label_to_sizer (table, _panel, _("CC address"), true);
1049 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1050 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1052 add_label_to_sizer (table, _panel, _("BCC address"), true);
1053 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1054 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1056 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1057 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1059 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1060 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1062 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1063 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1064 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1065 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1066 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1067 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1068 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1069 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1070 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1071 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1074 void config_changed ()
1076 Config* config = Config::instance ();
1078 checked_set (_mail_server, config->mail_server ());
1079 checked_set (_mail_port, config->mail_port ());
1080 checked_set (_mail_user, config->mail_user ());
1081 checked_set (_mail_password, config->mail_password ());
1082 checked_set (_kdm_subject, config->kdm_subject ());
1083 checked_set (_kdm_from, config->kdm_from ());
1084 checked_set (_kdm_cc, config->kdm_cc ());
1085 checked_set (_kdm_bcc, config->kdm_bcc ());
1086 checked_set (_kdm_email, Config::instance()->kdm_email ());
1089 void mail_server_changed ()
1091 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1094 void mail_port_changed ()
1096 Config::instance()->set_mail_port (_mail_port->GetValue ());
1099 void mail_user_changed ()
1101 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1104 void mail_password_changed ()
1106 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1109 void kdm_subject_changed ()
1111 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1114 void kdm_from_changed ()
1116 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1119 void kdm_cc_changed ()
1121 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1124 void kdm_bcc_changed ()
1126 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1129 void kdm_email_changed ()
1131 if (_kdm_email->GetValue().IsEmpty ()) {
1132 /* Sometimes we get sent an erroneous notification that the email
1133 is empty; I don't know why.
1137 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1140 void reset_kdm_email ()
1142 Config::instance()->reset_kdm_email ();
1143 checked_set (_kdm_email, Config::instance()->kdm_email ());
1146 wxTextCtrl* _mail_server;
1147 wxSpinCtrl* _mail_port;
1148 wxTextCtrl* _mail_user;
1149 wxTextCtrl* _mail_password;
1150 wxTextCtrl* _kdm_subject;
1151 wxTextCtrl* _kdm_from;
1152 wxTextCtrl* _kdm_cc;
1153 wxTextCtrl* _kdm_bcc;
1154 wxTextCtrl* _kdm_email;
1155 wxButton* _reset_kdm_email;
1158 /** @class AdvancedPage
1159 * @brief Advanced page of the preferences dialog.
1161 class AdvancedPage : public StockPage
1164 AdvancedPage (wxSize panel_size, int border)
1165 : StockPage (Kind_Advanced, panel_size, border)
1166 , _maximum_j2k_bandwidth (0)
1167 , _allow_any_dcp_frame_rate (0)
1172 , _log_debug_decode (0)
1173 , _log_debug_encode (0)
1179 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1180 table->AddGrowableCol (1, 1);
1181 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1184 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1185 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1186 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1187 s->Add (_maximum_j2k_bandwidth, 1);
1188 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1192 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1193 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1194 table->AddSpacer (0);
1197 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1198 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1200 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1201 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1205 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1206 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1207 t->Add (_log_general, 1, wxEXPAND | wxALL);
1208 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1209 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1210 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1211 t->Add (_log_error, 1, wxEXPAND | wxALL);
1212 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1213 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1214 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1215 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1216 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1217 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1218 table->Add (t, 0, wxALL, 6);
1221 #ifdef DCPOMATIC_WINDOWS
1222 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1223 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1224 table->AddSpacer (0);
1227 _maximum_j2k_bandwidth->SetRange (1, 1000);
1228 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1229 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1230 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1231 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1232 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1233 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1234 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1235 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1236 #ifdef DCPOMATIC_WINDOWS
1237 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1241 void config_changed ()
1243 Config* config = Config::instance ();
1245 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1246 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1247 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1248 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1249 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1250 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1251 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1252 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1253 #ifdef DCPOMATIC_WINDOWS
1254 checked_set (_win32_console, config->win32_console());
1258 void maximum_j2k_bandwidth_changed ()
1260 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1263 void allow_any_dcp_frame_rate_changed ()
1265 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1271 if (_log_general->GetValue ()) {
1272 types |= Log::TYPE_GENERAL;
1274 if (_log_warning->GetValue ()) {
1275 types |= Log::TYPE_WARNING;
1277 if (_log_error->GetValue ()) {
1278 types |= Log::TYPE_ERROR;
1280 if (_log_timing->GetValue ()) {
1281 types |= Log::TYPE_TIMING;
1283 if (_log_debug_decode->GetValue ()) {
1284 types |= Log::TYPE_DEBUG_DECODE;
1286 if (_log_debug_encode->GetValue ()) {
1287 types |= Log::TYPE_DEBUG_ENCODE;
1289 Config::instance()->set_log_types (types);
1292 #ifdef DCPOMATIC_WINDOWS
1293 void win32_console_changed ()
1295 Config::instance()->set_win32_console (_win32_console->GetValue ());
1299 wxSpinCtrl* _maximum_j2k_bandwidth;
1300 wxCheckBox* _allow_any_dcp_frame_rate;
1301 wxCheckBox* _log_general;
1302 wxCheckBox* _log_warning;
1303 wxCheckBox* _log_error;
1304 wxCheckBox* _log_timing;
1305 wxCheckBox* _log_debug_decode;
1306 wxCheckBox* _log_debug_encode;
1307 #ifdef DCPOMATIC_WINDOWS
1308 wxCheckBox* _win32_console;
1312 wxPreferencesEditor*
1313 create_config_dialog ()
1315 wxPreferencesEditor* e = new wxPreferencesEditor ();
1317 #ifdef DCPOMATIC_OSX
1318 /* Width that we force some of the config panels to be on OSX so that
1319 the containing window doesn't shrink too much when we select those panels.
1320 This is obviously an unpleasant hack.
1322 wxSize ps = wxSize (520, -1);
1323 int const border = 16;
1325 wxSize ps = wxSize (-1, -1);
1326 int const border = 8;
1329 e->AddPage (new GeneralPage (ps, border));
1330 e->AddPage (new DefaultsPage (ps, border));
1331 e->AddPage (new EncodingServersPage (ps, border));
1332 e->AddPage (new KeysPage (ps, border));
1333 e->AddPage (new TMSPage (ps, border));
1334 e->AddPage (new KDMEmailPage (ps, border));
1335 e->AddPage (new AdvancedPage (ps, border));