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 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
153 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
156 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
157 table->Add (_set_language, wxGBPosition (r, 0));
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, wxGBPosition (r, 1));
172 wxStaticText* restart = add_label_to_grid_bag_sizer (
173 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
175 wxFont font = restart->GetFont();
176 font.SetStyle (wxFONTSTYLE_ITALIC);
177 font.SetPointSize (font.GetPointSize() - 1);
178 restart->SetFont (font);
181 add_label_to_grid_bag_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
182 _num_local_encoding_threads = new wxSpinCtrl (_panel);
183 table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
186 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
187 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
190 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
191 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
194 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
195 bottom_table->AddGrowableCol (1, 1);
197 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
198 _issuer = new wxTextCtrl (_panel, wxID_ANY);
199 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
201 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
202 _creator = new wxTextCtrl (_panel, wxID_ANY);
203 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
205 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
208 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
209 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
211 _num_local_encoding_threads->SetRange (1, 128);
212 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
214 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
215 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
217 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::issuer_changed, this));
218 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::creator_changed, this));
221 void config_changed ()
223 Config* config = Config::instance ();
225 checked_set (_set_language, config->language ());
227 if (config->language().get_value_or ("") == "fr") {
228 checked_set (_language, 3);
229 } else if (config->language().get_value_or ("") == "it") {
230 checked_set (_language, 4);
231 } else if (config->language().get_value_or ("") == "es") {
232 checked_set (_language, 2);
233 } else if (config->language().get_value_or ("") == "sv") {
234 checked_set (_language, 6);
235 } else if (config->language().get_value_or ("") == "de") {
236 checked_set (_language, 0);
237 } else if (config->language().get_value_or ("") == "nl") {
238 checked_set (_language, 5);
239 } else if (config->language().get_value_or ("") == "ru") {
240 checked_set (_language, 7);
241 } else if (config->language().get_value_or ("") == "pl") {
242 checked_set (_language, 8);
243 } else if (config->language().get_value_or ("") == "da") {
244 checked_set (_language, 9);
246 _language->SetSelection (1);
249 setup_language_sensitivity ();
251 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
252 checked_set (_check_for_updates, config->check_for_updates ());
253 checked_set (_check_for_test_updates, config->check_for_test_updates ());
254 checked_set (_issuer, config->dcp_issuer ());
255 checked_set (_creator, config->dcp_creator ());
258 void setup_language_sensitivity ()
260 _language->Enable (_set_language->GetValue ());
263 void set_language_changed ()
265 setup_language_sensitivity ();
266 if (_set_language->GetValue ()) {
269 Config::instance()->unset_language ();
273 void language_changed ()
275 switch (_language->GetSelection ()) {
277 Config::instance()->set_language ("de");
280 Config::instance()->set_language ("en");
283 Config::instance()->set_language ("es");
286 Config::instance()->set_language ("fr");
289 Config::instance()->set_language ("it");
292 Config::instance()->set_language ("nl");
295 Config::instance()->set_language ("sv");
298 Config::instance()->set_language ("ru");
301 Config::instance()->set_language ("pl");
304 Config::instance()->set_language ("da");
309 void check_for_updates_changed ()
311 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
314 void check_for_test_updates_changed ()
316 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
319 void num_local_encoding_threads_changed ()
321 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
324 void issuer_changed ()
326 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
329 void creator_changed ()
331 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
334 wxCheckBox* _set_language;
336 wxSpinCtrl* _num_local_encoding_threads;
337 wxCheckBox* _check_for_updates;
338 wxCheckBox* _check_for_test_updates;
340 wxTextCtrl* _creator;
343 class DefaultsPage : public StandardPage
346 DefaultsPage (wxSize panel_size, int border)
347 : StandardPage (panel_size, border)
350 wxString GetName () const
352 return _("Defaults");
356 wxBitmap GetLargeIcon () const
358 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
365 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
366 table->AddGrowableCol (1, 1);
367 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
370 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
371 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
372 _still_length = new wxSpinCtrl (_panel);
373 s->Add (_still_length);
374 add_label_to_sizer (s, _panel, _("s"), false);
378 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
379 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
380 _directory = new DirPickerCtrl (_panel);
382 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
384 table->Add (_directory, 1, wxEXPAND);
386 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
387 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
388 table->Add (_isdcf_metadata_button);
390 add_label_to_sizer (table, _panel, _("Default container"), true);
391 _container = new wxChoice (_panel, wxID_ANY);
392 table->Add (_container);
394 add_label_to_sizer (table, _panel, _("Default content type"), true);
395 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
396 table->Add (_dcp_content_type);
399 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
400 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
401 _j2k_bandwidth = new wxSpinCtrl (_panel);
402 s->Add (_j2k_bandwidth);
403 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
408 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
409 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
410 _audio_delay = new wxSpinCtrl (_panel);
411 s->Add (_audio_delay);
412 add_label_to_sizer (s, _panel, _("ms"), false);
416 _still_length->SetRange (1, 3600);
417 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
419 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
421 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
423 vector<Ratio const *> ratios = Ratio::all ();
424 for (size_t i = 0; i < ratios.size(); ++i) {
425 _container->Append (std_to_wx (ratios[i]->nickname ()));
428 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
430 vector<DCPContentType const *> const ct = DCPContentType::all ();
431 for (size_t i = 0; i < ct.size(); ++i) {
432 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
435 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
437 _j2k_bandwidth->SetRange (50, 250);
438 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
440 _audio_delay->SetRange (-1000, 1000);
441 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
444 void config_changed ()
446 Config* config = Config::instance ();
448 vector<Ratio const *> ratios = Ratio::all ();
449 for (size_t i = 0; i < ratios.size(); ++i) {
450 if (ratios[i] == config->default_container ()) {
451 _container->SetSelection (i);
455 vector<DCPContentType const *> const ct = DCPContentType::all ();
456 for (size_t i = 0; i < ct.size(); ++i) {
457 if (ct[i] == config->default_dcp_content_type ()) {
458 _dcp_content_type->SetSelection (i);
462 checked_set (_still_length, config->default_still_length ());
463 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
464 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
465 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
466 checked_set (_audio_delay, config->default_audio_delay ());
469 void j2k_bandwidth_changed ()
471 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
474 void audio_delay_changed ()
476 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
479 void directory_changed ()
481 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
484 void edit_isdcf_metadata_clicked ()
486 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
488 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
492 void still_length_changed ()
494 Config::instance()->set_default_still_length (_still_length->GetValue ());
497 void container_changed ()
499 vector<Ratio const *> ratio = Ratio::all ();
500 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
503 void dcp_content_type_changed ()
505 vector<DCPContentType const *> ct = DCPContentType::all ();
506 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
509 wxSpinCtrl* _j2k_bandwidth;
510 wxSpinCtrl* _audio_delay;
511 wxButton* _isdcf_metadata_button;
512 wxSpinCtrl* _still_length;
513 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
514 DirPickerCtrl* _directory;
516 wxDirPickerCtrl* _directory;
518 wxChoice* _container;
519 wxChoice* _dcp_content_type;
522 class EncodingServersPage : public StandardPage
525 EncodingServersPage (wxSize panel_size, int border)
526 : StandardPage (panel_size, border)
529 wxString GetName () const
535 wxBitmap GetLargeIcon () const
537 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
544 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
545 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
547 vector<string> columns;
548 columns.push_back (wx_to_std (_("IP address / host name")));
549 _servers_list = new EditableList<string, ServerDialog> (
552 boost::bind (&Config::servers, Config::instance()),
553 boost::bind (&Config::set_servers, Config::instance(), _1),
554 boost::bind (&EncodingServersPage::server_column, this, _1)
557 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
559 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
562 void config_changed ()
564 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
565 _servers_list->refresh ();
568 void use_any_servers_changed ()
570 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
573 string server_column (string s)
578 wxCheckBox* _use_any_servers;
579 EditableList<string, ServerDialog>* _servers_list;
582 class CertificateChainEditor : public wxPanel
585 CertificateChainEditor (
589 function<void (shared_ptr<dcp::CertificateChain>)> set,
590 function<shared_ptr<const dcp::CertificateChain> (void)> get
596 wxFont subheading_font (*wxNORMAL_FONT);
597 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
599 _sizer = new wxBoxSizer (wxVERTICAL);
602 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
603 m->SetFont (subheading_font);
604 _sizer->Add (m, 0, wxALL, border);
607 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
608 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
610 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
615 ip.SetText (_("Type"));
617 _certificates->InsertColumn (0, ip);
623 ip.SetText (_("Thumbprint"));
626 wxFont font = ip.GetFont ();
627 font.SetFamily (wxFONTFAMILY_TELETYPE);
630 _certificates->InsertColumn (1, ip);
633 certificates_sizer->Add (_certificates, 1, wxEXPAND);
636 wxSizer* s = new wxBoxSizer (wxVERTICAL);
637 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
638 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
639 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
640 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
641 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
644 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
645 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
648 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
649 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
650 wxFont font = _private_key->GetFont ();
651 font.SetFamily (wxFONTFAMILY_TELETYPE);
652 _private_key->SetFont (font);
653 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
654 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
655 table->Add (_load_private_key, wxGBPosition (r, 2));
658 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
659 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
660 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
661 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
664 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::add_certificate, this));
665 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remove_certificate, this));
666 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
667 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
668 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remake_certificates, this));
669 _load_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::load_private_key, this));
671 SetSizerAndFit (_sizer);
674 void config_changed ()
676 _chain.reset (new dcp::CertificateChain (*_get().get ()));
678 update_certificate_list ();
679 update_private_key ();
680 update_sensitivity ();
683 void add_button (wxWindow* button)
685 _button_sizer->Add (button);
690 void add_certificate ()
692 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
694 if (d->ShowModal() == wxID_OK) {
696 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
699 update_certificate_list ();
700 } catch (dcp::MiscError& e) {
701 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
707 update_sensitivity ();
710 void remove_certificate ()
712 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
717 _certificates->DeleteItem (i);
721 update_sensitivity ();
724 void update_certificate_list ()
726 _certificates->DeleteAllItems ();
728 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
729 BOOST_FOREACH (dcp::Certificate const & i, certs) {
732 _certificates->InsertItem (item);
733 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
736 _certificates->SetItem (n, 0, _("Root"));
737 } else if (n == (certs.size() - 1)) {
738 _certificates->SetItem (n, 0, _("Leaf"));
740 _certificates->SetItem (n, 0, _("Intermediate"));
747 void remake_certificates ()
749 shared_ptr<const dcp::CertificateChain> chain = _get ();
751 string intermediate_common_name;
752 if (chain->root_to_leaf().size() >= 3) {
753 dcp::CertificateChain::List all = chain->root_to_leaf ();
754 dcp::CertificateChain::List::iterator i = all.begin ();
756 intermediate_common_name = i->subject_common_name ();
759 MakeChainDialog* d = new MakeChainDialog (
761 chain->root().subject_organization_name (),
762 chain->root().subject_organizational_unit_name (),
763 chain->root().subject_common_name (),
764 intermediate_common_name,
765 chain->leaf().subject_common_name ()
768 if (d->ShowModal () == wxID_OK) {
770 new dcp::CertificateChain (
773 d->organisational_unit (),
774 d->root_common_name (),
775 d->intermediate_common_name (),
776 d->leaf_common_name ()
781 update_certificate_list ();
782 update_private_key ();
788 void update_sensitivity ()
790 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
793 void update_private_key ()
795 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
799 void load_private_key ()
801 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
803 if (d->ShowModal() == wxID_OK) {
805 boost::filesystem::path p (wx_to_std (d->GetPath ()));
806 if (boost::filesystem::file_size (p) > 1024) {
807 error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
811 _chain->set_key (dcp::file_to_string (p));
813 update_private_key ();
814 } catch (dcp::MiscError& e) {
815 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
821 update_sensitivity ();
824 wxListCtrl* _certificates;
825 wxButton* _add_certificate;
826 wxButton* _remove_certificate;
827 wxButton* _remake_certificates;
828 wxStaticText* _private_key;
829 wxButton* _load_private_key;
831 wxBoxSizer* _button_sizer;
832 shared_ptr<dcp::CertificateChain> _chain;
833 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
834 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
837 class KeysPage : public StandardPage
840 KeysPage (wxSize panel_size, int border)
841 : StandardPage (panel_size, border)
844 wxString GetName () const
850 wxBitmap GetLargeIcon () const
852 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
860 _signer = new CertificateChainEditor (
861 _panel, _("Signing DCPs and KDMs"), _border,
862 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
863 boost::bind (&Config::signer_chain, Config::instance ())
866 _panel->GetSizer()->Add (_signer);
868 _decryption = new CertificateChainEditor (
869 _panel, _("Decrypting DCPs"), _border,
870 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
871 boost::bind (&Config::decryption_chain, Config::instance ())
874 _panel->GetSizer()->Add (_decryption);
876 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
877 _decryption->add_button (_export_decryption_certificate);
879 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
882 void export_decryption_certificate ()
884 wxFileDialog* d = new wxFileDialog (
885 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
886 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
889 if (d->ShowModal () == wxID_OK) {
890 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
892 throw OpenFileError (wx_to_std (d->GetPath ()));
895 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
896 fwrite (s.c_str(), 1, s.length(), f);
902 void config_changed ()
904 _signer->config_changed ();
905 _decryption->config_changed ();
908 CertificateChainEditor* _signer;
909 CertificateChainEditor* _decryption;
910 wxButton* _export_decryption_certificate;
913 class TMSPage : public StandardPage
916 TMSPage (wxSize panel_size, int border)
917 : StandardPage (panel_size, border)
920 wxString GetName () const
926 wxBitmap GetLargeIcon () const
928 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
935 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
936 table->AddGrowableCol (1, 1);
937 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
939 add_label_to_sizer (table, _panel, _("Protocol"), true);
940 _tms_protocol = new wxChoice (_panel, wxID_ANY);
941 table->Add (_tms_protocol, 1, wxEXPAND);
943 add_label_to_sizer (table, _panel, _("IP address"), true);
944 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
945 table->Add (_tms_ip, 1, wxEXPAND);
947 add_label_to_sizer (table, _panel, _("Target path"), true);
948 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
949 table->Add (_tms_path, 1, wxEXPAND);
951 add_label_to_sizer (table, _panel, _("User name"), true);
952 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
953 table->Add (_tms_user, 1, wxEXPAND);
955 add_label_to_sizer (table, _panel, _("Password"), true);
956 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
957 table->Add (_tms_password, 1, wxEXPAND);
959 _tms_protocol->Append (_("SCP (for AAM)"));
960 _tms_protocol->Append (_("FTP (for Dolby)"));
962 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
963 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
964 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
965 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
966 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
969 void config_changed ()
971 Config* config = Config::instance ();
973 checked_set (_tms_protocol, config->tms_protocol ());
974 checked_set (_tms_ip, config->tms_ip ());
975 checked_set (_tms_path, config->tms_path ());
976 checked_set (_tms_user, config->tms_user ());
977 checked_set (_tms_password, config->tms_password ());
980 void tms_protocol_changed ()
982 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
985 void tms_ip_changed ()
987 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
990 void tms_path_changed ()
992 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
995 void tms_user_changed ()
997 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1000 void tms_password_changed ()
1002 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1005 wxChoice* _tms_protocol;
1006 wxTextCtrl* _tms_ip;
1007 wxTextCtrl* _tms_path;
1008 wxTextCtrl* _tms_user;
1009 wxTextCtrl* _tms_password;
1012 class KDMEmailPage : public StandardPage
1016 KDMEmailPage (wxSize panel_size, int border)
1017 #ifdef DCPOMATIC_OSX
1018 /* We have to force both width and height of this one */
1019 : StandardPage (wxSize (480, 128), border)
1021 : StandardPage (panel_size, border)
1025 wxString GetName () const
1027 return _("KDM Email");
1030 #ifdef DCPOMATIC_OSX
1031 wxBitmap GetLargeIcon () const
1033 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1040 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1041 table->AddGrowableCol (1, 1);
1042 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1044 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1046 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1047 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1048 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1049 add_label_to_sizer (s, _panel, _("port"), false);
1050 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1051 _mail_port->SetRange (0, 65535);
1052 s->Add (_mail_port);
1053 table->Add (s, 1, wxEXPAND | wxALL);
1056 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1057 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1058 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1060 add_label_to_sizer (table, _panel, _("Mail password"), true);
1061 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1062 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1064 add_label_to_sizer (table, _panel, _("Subject"), true);
1065 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1066 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1068 add_label_to_sizer (table, _panel, _("From address"), true);
1069 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1070 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1072 add_label_to_sizer (table, _panel, _("CC address"), true);
1073 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1074 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1076 add_label_to_sizer (table, _panel, _("BCC address"), true);
1077 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1078 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1080 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1081 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1083 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1084 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1086 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1087 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1088 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1089 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1090 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1091 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1092 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1093 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1094 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1095 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1098 void config_changed ()
1100 Config* config = Config::instance ();
1102 checked_set (_mail_server, config->mail_server ());
1103 checked_set (_mail_port, config->mail_port ());
1104 checked_set (_mail_user, config->mail_user ());
1105 checked_set (_mail_password, config->mail_password ());
1106 checked_set (_kdm_subject, config->kdm_subject ());
1107 checked_set (_kdm_from, config->kdm_from ());
1108 checked_set (_kdm_cc, config->kdm_cc ());
1109 checked_set (_kdm_bcc, config->kdm_bcc ());
1110 checked_set (_kdm_email, Config::instance()->kdm_email ());
1113 void mail_server_changed ()
1115 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1118 void mail_port_changed ()
1120 Config::instance()->set_mail_port (_mail_port->GetValue ());
1123 void mail_user_changed ()
1125 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1128 void mail_password_changed ()
1130 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1133 void kdm_subject_changed ()
1135 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1138 void kdm_from_changed ()
1140 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1143 void kdm_cc_changed ()
1145 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1148 void kdm_bcc_changed ()
1150 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1153 void kdm_email_changed ()
1155 if (_kdm_email->GetValue().IsEmpty ()) {
1156 /* Sometimes we get sent an erroneous notification that the email
1157 is empty; I don't know why.
1161 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1164 void reset_kdm_email ()
1166 Config::instance()->reset_kdm_email ();
1167 checked_set (_kdm_email, Config::instance()->kdm_email ());
1170 wxTextCtrl* _mail_server;
1171 wxSpinCtrl* _mail_port;
1172 wxTextCtrl* _mail_user;
1173 wxTextCtrl* _mail_password;
1174 wxTextCtrl* _kdm_subject;
1175 wxTextCtrl* _kdm_from;
1176 wxTextCtrl* _kdm_cc;
1177 wxTextCtrl* _kdm_bcc;
1178 wxTextCtrl* _kdm_email;
1179 wxButton* _reset_kdm_email;
1182 /** @class AdvancedPage
1183 * @brief Advanced page of the preferences dialog.
1185 class AdvancedPage : public StockPage
1188 AdvancedPage (wxSize panel_size, int border)
1189 : StockPage (Kind_Advanced, panel_size, border)
1190 , _maximum_j2k_bandwidth (0)
1191 , _allow_any_dcp_frame_rate (0)
1192 , _only_servers_encode (0)
1197 , _log_debug_decode (0)
1198 , _log_debug_encode (0)
1204 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1205 table->AddGrowableCol (1, 1);
1206 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1209 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1210 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1211 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1212 s->Add (_maximum_j2k_bandwidth, 1);
1213 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1217 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1218 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1219 table->AddSpacer (0);
1221 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1222 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1223 table->AddSpacer (0);
1226 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1227 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1229 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1230 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1234 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1235 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1236 t->Add (_log_general, 1, wxEXPAND | wxALL);
1237 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1238 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1239 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1240 t->Add (_log_error, 1, wxEXPAND | wxALL);
1241 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1242 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1243 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1244 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1245 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1246 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1247 table->Add (t, 0, wxALL, 6);
1250 #ifdef DCPOMATIC_WINDOWS
1251 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1252 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1253 table->AddSpacer (0);
1256 _maximum_j2k_bandwidth->SetRange (1, 1000);
1257 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1258 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1259 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1260 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1261 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1262 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1263 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1264 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1265 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1266 #ifdef DCPOMATIC_WINDOWS
1267 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1271 void config_changed ()
1273 Config* config = Config::instance ();
1275 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1276 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1277 checked_set (_only_servers_encode, config->only_servers_encode ());
1278 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1279 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1280 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1281 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1282 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1283 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1284 #ifdef DCPOMATIC_WINDOWS
1285 checked_set (_win32_console, config->win32_console());
1289 void maximum_j2k_bandwidth_changed ()
1291 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1294 void allow_any_dcp_frame_rate_changed ()
1296 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1299 void only_servers_encode_changed ()
1301 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1307 if (_log_general->GetValue ()) {
1308 types |= Log::TYPE_GENERAL;
1310 if (_log_warning->GetValue ()) {
1311 types |= Log::TYPE_WARNING;
1313 if (_log_error->GetValue ()) {
1314 types |= Log::TYPE_ERROR;
1316 if (_log_timing->GetValue ()) {
1317 types |= Log::TYPE_TIMING;
1319 if (_log_debug_decode->GetValue ()) {
1320 types |= Log::TYPE_DEBUG_DECODE;
1322 if (_log_debug_encode->GetValue ()) {
1323 types |= Log::TYPE_DEBUG_ENCODE;
1325 Config::instance()->set_log_types (types);
1328 #ifdef DCPOMATIC_WINDOWS
1329 void win32_console_changed ()
1331 Config::instance()->set_win32_console (_win32_console->GetValue ());
1335 wxSpinCtrl* _maximum_j2k_bandwidth;
1336 wxCheckBox* _allow_any_dcp_frame_rate;
1337 wxCheckBox* _only_servers_encode;
1338 wxCheckBox* _log_general;
1339 wxCheckBox* _log_warning;
1340 wxCheckBox* _log_error;
1341 wxCheckBox* _log_timing;
1342 wxCheckBox* _log_debug_decode;
1343 wxCheckBox* _log_debug_encode;
1344 #ifdef DCPOMATIC_WINDOWS
1345 wxCheckBox* _win32_console;
1349 wxPreferencesEditor*
1350 create_config_dialog ()
1352 wxPreferencesEditor* e = new wxPreferencesEditor ();
1354 #ifdef DCPOMATIC_OSX
1355 /* Width that we force some of the config panels to be on OSX so that
1356 the containing window doesn't shrink too much when we select those panels.
1357 This is obviously an unpleasant hack.
1359 wxSize ps = wxSize (520, -1);
1360 int const border = 16;
1362 wxSize ps = wxSize (-1, -1);
1363 int const border = 8;
1366 e->AddPage (new GeneralPage (ps, border));
1367 e->AddPage (new DefaultsPage (ps, border));
1368 e->AddPage (new EncodingServersPage (ps, border));
1369 e->AddPage (new KeysPage (ps, border));
1370 e->AddPage (new TMSPage (ps, border));
1371 e->AddPage (new KDMEmailPage (ps, border));
1372 e->AddPage (new AdvancedPage (ps, border));