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.
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/exceptions.h>
32 #include <dcp/signer.h>
33 #include "lib/config.h"
34 #include "lib/ratio.h"
35 #include "lib/filter.h"
36 #include "lib/dcp_content_type.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include "config_dialog.h"
43 #include "editable_list.h"
44 #include "filter_dialog.h"
45 #include "dir_picker_ctrl.h"
46 #include "isdcf_metadata_dialog.h"
47 #include "server_dialog.h"
48 #include "make_signer_chain_dialog.h"
55 using boost::shared_ptr;
56 using boost::lexical_cast;
61 Page (wxSize panel_size, int border)
64 , _panel_size (panel_size)
65 , _window_exists (false)
67 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
73 wxWindow* create_window (wxWindow* parent)
75 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
76 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
80 _window_exists = true;
83 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
92 virtual void config_changed () = 0;
93 virtual void setup () = 0;
95 void config_changed_wrapper ()
102 void window_destroyed ()
104 _window_exists = false;
108 boost::signals2::scoped_connection _config_connection;
112 class StockPage : public wxStockPreferencesPage, public Page
115 StockPage (Kind kind, wxSize panel_size, int border)
116 : wxStockPreferencesPage (kind)
117 , Page (panel_size, border)
120 wxWindow* CreateWindow (wxWindow* parent)
122 return create_window (parent);
126 class StandardPage : public wxPreferencesPage, public Page
129 StandardPage (wxSize panel_size, int border)
130 : Page (panel_size, border)
133 wxWindow* CreateWindow (wxWindow* parent)
135 return create_window (parent);
139 class GeneralPage : public StockPage
142 GeneralPage (wxSize panel_size, int border)
143 : StockPage (Kind_General, panel_size, border)
149 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
150 table->AddGrowableCol (1, 1);
151 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
153 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
154 table->Add (_set_language, 1);
155 _language = new wxChoice (_panel, wxID_ANY);
156 _language->Append (wxT ("Deutsch"));
157 _language->Append (wxT ("English"));
158 _language->Append (wxT ("Español"));
159 _language->Append (wxT ("Français"));
160 _language->Append (wxT ("Italiano"));
161 _language->Append (wxT ("Nederlands"));
162 _language->Append (wxT ("Svenska"));
163 table->Add (_language);
165 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
166 wxFont font = restart->GetFont();
167 font.SetStyle (wxFONTSTYLE_ITALIC);
168 font.SetPointSize (font.GetPointSize() - 1);
169 restart->SetFont (font);
170 table->AddSpacer (0);
172 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
173 _num_local_encoding_threads = new wxSpinCtrl (_panel);
174 table->Add (_num_local_encoding_threads, 1);
176 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
177 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
178 table->AddSpacer (0);
180 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
181 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
182 table->AddSpacer (0);
184 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
185 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
187 _num_local_encoding_threads->SetRange (1, 128);
188 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
190 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
191 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
194 void config_changed ()
196 Config* config = Config::instance ();
198 checked_set (_set_language, config->language ());
200 if (config->language().get_value_or ("") == "fr") {
201 _language->SetSelection (3);
202 } else if (config->language().get_value_or ("") == "it") {
203 _language->SetSelection (4);
204 } else if (config->language().get_value_or ("") == "es") {
205 _language->SetSelection (2);
206 } else if (config->language().get_value_or ("") == "sv") {
207 _language->SetSelection (6);
208 } else if (config->language().get_value_or ("") == "de") {
209 _language->SetSelection (0);
210 } else if (config->language().get_value_or ("") == "nl") {
211 _language->SetSelection (5);
213 _language->SetSelection (1);
216 setup_language_sensitivity ();
218 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
219 checked_set (_check_for_updates, config->check_for_updates ());
220 checked_set (_check_for_test_updates, config->check_for_test_updates ());
223 void setup_language_sensitivity ()
225 _language->Enable (_set_language->GetValue ());
228 void set_language_changed ()
230 setup_language_sensitivity ();
231 if (_set_language->GetValue ()) {
234 Config::instance()->unset_language ();
238 void language_changed ()
240 switch (_language->GetSelection ()) {
242 Config::instance()->set_language ("de");
245 Config::instance()->set_language ("en");
248 Config::instance()->set_language ("es");
251 Config::instance()->set_language ("fr");
254 Config::instance()->set_language ("it");
257 Config::instance()->set_language ("nl");
260 Config::instance()->set_language ("sv");
265 void check_for_updates_changed ()
267 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
270 void check_for_test_updates_changed ()
272 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
275 void num_local_encoding_threads_changed ()
277 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
280 wxCheckBox* _set_language;
282 wxSpinCtrl* _num_local_encoding_threads;
283 wxCheckBox* _check_for_updates;
284 wxCheckBox* _check_for_test_updates;
287 class DefaultsPage : public StandardPage
290 DefaultsPage (wxSize panel_size, int border)
291 : StandardPage (panel_size, border)
294 wxString GetName () const
296 return _("Defaults");
300 wxBitmap GetLargeIcon () const
302 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
309 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
310 table->AddGrowableCol (1, 1);
311 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
314 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
315 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
316 _still_length = new wxSpinCtrl (_panel);
317 s->Add (_still_length);
318 add_label_to_sizer (s, _panel, _("s"), false);
322 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
323 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
324 _directory = new DirPickerCtrl (_panel);
326 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
328 table->Add (_directory, 1, wxEXPAND);
330 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
331 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
332 table->Add (_isdcf_metadata_button);
334 add_label_to_sizer (table, _panel, _("Default container"), true);
335 _container = new wxChoice (_panel, wxID_ANY);
336 table->Add (_container);
338 add_label_to_sizer (table, _panel, _("Default content type"), true);
339 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
340 table->Add (_dcp_content_type);
343 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
344 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
345 _j2k_bandwidth = new wxSpinCtrl (_panel);
346 s->Add (_j2k_bandwidth);
347 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
352 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
353 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
354 _audio_delay = new wxSpinCtrl (_panel);
355 s->Add (_audio_delay);
356 add_label_to_sizer (s, _panel, _("ms"), false);
360 add_label_to_sizer (table, _panel, _("Default issuer"), true);
361 _issuer = new wxTextCtrl (_panel, wxID_ANY);
362 table->Add (_issuer, 1, wxEXPAND);
364 _still_length->SetRange (1, 3600);
365 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
367 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
369 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
371 vector<Ratio const *> ratios = Ratio::all ();
372 for (size_t i = 0; i < ratios.size(); ++i) {
373 _container->Append (std_to_wx (ratios[i]->nickname ()));
376 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
378 vector<DCPContentType const *> const ct = DCPContentType::all ();
379 for (size_t i = 0; i < ct.size(); ++i) {
380 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
383 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
385 _j2k_bandwidth->SetRange (50, 250);
386 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
388 _audio_delay->SetRange (-1000, 1000);
389 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
391 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
394 void config_changed ()
396 Config* config = Config::instance ();
398 vector<Ratio const *> ratios = Ratio::all ();
399 for (size_t i = 0; i < ratios.size(); ++i) {
400 if (ratios[i] == config->default_container ()) {
401 _container->SetSelection (i);
405 vector<DCPContentType const *> const ct = DCPContentType::all ();
406 for (size_t i = 0; i < ct.size(); ++i) {
407 if (ct[i] == config->default_dcp_content_type ()) {
408 _dcp_content_type->SetSelection (i);
412 checked_set (_still_length, config->default_still_length ());
413 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
414 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
415 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
416 checked_set (_audio_delay, config->default_audio_delay ());
417 checked_set (_issuer, config->dcp_issuer ());
420 void j2k_bandwidth_changed ()
422 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
425 void audio_delay_changed ()
427 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
430 void directory_changed ()
432 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
435 void edit_isdcf_metadata_clicked ()
437 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
439 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
443 void still_length_changed ()
445 Config::instance()->set_default_still_length (_still_length->GetValue ());
448 void container_changed ()
450 vector<Ratio const *> ratio = Ratio::all ();
451 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
454 void dcp_content_type_changed ()
456 vector<DCPContentType const *> ct = DCPContentType::all ();
457 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
460 void issuer_changed ()
462 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
465 wxSpinCtrl* _j2k_bandwidth;
466 wxSpinCtrl* _audio_delay;
467 wxButton* _isdcf_metadata_button;
468 wxSpinCtrl* _still_length;
469 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
470 DirPickerCtrl* _directory;
472 wxDirPickerCtrl* _directory;
474 wxChoice* _container;
475 wxChoice* _dcp_content_type;
479 class EncodingServersPage : public StandardPage
482 EncodingServersPage (wxSize panel_size, int border)
483 : StandardPage (panel_size, border)
486 wxString GetName () const
492 wxBitmap GetLargeIcon () const
494 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
501 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
502 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
504 vector<string> columns;
505 columns.push_back (wx_to_std (_("IP address / host name")));
506 _servers_list = new EditableList<string, ServerDialog> (
509 boost::bind (&Config::servers, Config::instance()),
510 boost::bind (&Config::set_servers, Config::instance(), _1),
511 boost::bind (&EncodingServersPage::server_column, this, _1)
514 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
516 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
519 void config_changed ()
521 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
522 _servers_list->refresh ();
525 void use_any_servers_changed ()
527 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
530 string server_column (string s)
535 wxCheckBox* _use_any_servers;
536 EditableList<string, ServerDialog>* _servers_list;
539 class KeysPage : public StandardPage
542 KeysPage (wxSize panel_size, int border)
543 : StandardPage (panel_size, border)
546 wxString GetName () const
552 wxBitmap GetLargeIcon () const
554 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
561 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
562 _panel->GetSizer()->Add (m, 0, wxALL, _border);
564 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
565 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
567 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
572 ip.SetText (_("Type"));
574 _certificates->InsertColumn (0, ip);
580 ip.SetText (_("Thumbprint"));
583 wxFont font = ip.GetFont ();
584 font.SetFamily (wxFONTFAMILY_TELETYPE);
587 _certificates->InsertColumn (1, ip);
590 certificates_sizer->Add (_certificates, 1, wxEXPAND);
593 wxSizer* s = new wxBoxSizer (wxVERTICAL);
594 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
595 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
596 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
597 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
598 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
601 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
602 table->AddGrowableCol (1, 1);
603 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
605 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
606 table->Add (_remake_certificates, 0);
607 table->AddSpacer (0);
609 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
611 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
612 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
613 wxFont font = _signer_private_key->GetFont ();
614 font.SetFamily (wxFONTFAMILY_TELETYPE);
615 _signer_private_key->SetFont (font);
616 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
617 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
618 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
622 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
624 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
625 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
626 wxFont font = _decryption_certificate->GetFont ();
627 font.SetFamily (wxFONTFAMILY_TELETYPE);
628 _decryption_certificate->SetFont (font);
629 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
630 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
631 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
635 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
637 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
638 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
639 wxFont font = _decryption_private_key->GetFont ();
640 font.SetFamily (wxFONTFAMILY_TELETYPE);
641 _decryption_private_key->SetFont (font);
642 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
643 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
644 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
648 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
649 table->Add (_export_decryption_certificate);
650 table->AddSpacer (0);
652 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
653 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
654 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
655 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
656 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
657 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
658 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
659 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
660 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
663 void config_changed ()
665 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
667 update_certificate_list ();
668 update_signer_private_key ();
669 update_decryption_certificate ();
670 update_decryption_private_key ();
671 update_sensitivity ();
674 void add_certificate ()
676 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
678 if (d->ShowModal() == wxID_OK) {
680 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
681 _signer->certificates().add (c);
682 Config::instance()->set_signer (_signer);
683 update_certificate_list ();
684 } catch (dcp::MiscError& e) {
685 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
691 update_sensitivity ();
694 void remove_certificate ()
696 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
701 _certificates->DeleteItem (i);
702 _signer->certificates().remove (i);
703 Config::instance()->set_signer (_signer);
705 update_sensitivity ();
708 void update_certificate_list ()
710 _certificates->DeleteAllItems ();
711 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
713 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
716 _certificates->InsertItem (item);
717 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
720 _certificates->SetItem (n, 0, _("Root"));
721 } else if (n == (certs.size() - 1)) {
722 _certificates->SetItem (n, 0, _("Leaf"));
724 _certificates->SetItem (n, 0, _("Intermediate"));
731 void remake_certificates ()
733 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
734 if (d->ShowModal () == wxID_OK) {
739 d->organisational_unit (),
740 d->root_common_name (),
741 d->intermediate_common_name (),
742 d->leaf_common_name ()
746 Config::instance()->set_signer (_signer);
747 update_certificate_list ();
748 update_signer_private_key ();
754 void update_sensitivity ()
756 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
759 void update_signer_private_key ()
761 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
764 void load_signer_private_key ()
766 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
768 if (d->ShowModal() == wxID_OK) {
770 boost::filesystem::path p (wx_to_std (d->GetPath ()));
771 if (boost::filesystem::file_size (p) > 1024) {
772 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
776 _signer->set_key (dcp::file_to_string (p));
777 Config::instance()->set_signer (_signer);
778 update_signer_private_key ();
779 } catch (dcp::MiscError& e) {
780 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
786 update_sensitivity ();
790 void load_decryption_certificate ()
792 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
794 if (d->ShowModal() == wxID_OK) {
796 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
797 Config::instance()->set_decryption_certificate (c);
798 update_decryption_certificate ();
799 } catch (dcp::MiscError& e) {
800 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
807 void update_decryption_certificate ()
809 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
812 void load_decryption_private_key ()
814 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
816 if (d->ShowModal() == wxID_OK) {
818 boost::filesystem::path p (wx_to_std (d->GetPath ()));
819 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
820 update_decryption_private_key ();
821 } catch (dcp::MiscError& e) {
822 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
829 void update_decryption_private_key ()
831 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
834 void export_decryption_certificate ()
836 wxFileDialog* d = new wxFileDialog (
837 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
838 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
841 if (d->ShowModal () == wxID_OK) {
842 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
844 throw OpenFileError (wx_to_std (d->GetPath ()));
847 string const s = Config::instance()->decryption_certificate().certificate (true);
848 fwrite (s.c_str(), 1, s.length(), f);
854 wxListCtrl* _certificates;
855 wxButton* _add_certificate;
856 wxButton* _remove_certificate;
857 wxButton* _remake_certificates;
858 wxStaticText* _signer_private_key;
859 wxButton* _load_signer_private_key;
860 wxStaticText* _decryption_certificate;
861 wxButton* _load_decryption_certificate;
862 wxStaticText* _decryption_private_key;
863 wxButton* _load_decryption_private_key;
864 wxButton* _export_decryption_certificate;
865 shared_ptr<dcp::Signer> _signer;
868 class TMSPage : public StandardPage
871 TMSPage (wxSize panel_size, int border)
872 : StandardPage (panel_size, border)
875 wxString GetName () const
881 wxBitmap GetLargeIcon () const
883 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
890 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
891 table->AddGrowableCol (1, 1);
892 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
894 add_label_to_sizer (table, _panel, _("IP address"), true);
895 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
896 table->Add (_tms_ip, 1, wxEXPAND);
898 add_label_to_sizer (table, _panel, _("Target path"), true);
899 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
900 table->Add (_tms_path, 1, wxEXPAND);
902 add_label_to_sizer (table, _panel, _("User name"), true);
903 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
904 table->Add (_tms_user, 1, wxEXPAND);
906 add_label_to_sizer (table, _panel, _("Password"), true);
907 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
908 table->Add (_tms_password, 1, wxEXPAND);
910 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
911 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
912 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
913 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
916 void config_changed ()
918 Config* config = Config::instance ();
920 checked_set (_tms_ip, config->tms_ip ());
921 checked_set (_tms_path, config->tms_path ());
922 checked_set (_tms_user, config->tms_user ());
923 checked_set (_tms_password, config->tms_password ());
926 void tms_ip_changed ()
928 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
931 void tms_path_changed ()
933 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
936 void tms_user_changed ()
938 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
941 void tms_password_changed ()
943 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
947 wxTextCtrl* _tms_path;
948 wxTextCtrl* _tms_user;
949 wxTextCtrl* _tms_password;
952 class KDMEmailPage : public StandardPage
956 KDMEmailPage (wxSize panel_size, int border)
958 /* We have to force both width and height of this one */
959 : StandardPage (wxSize (480, 128), border)
961 : StandardPage (panel_size, border)
965 wxString GetName () const
967 return _("KDM Email");
971 wxBitmap GetLargeIcon () const
973 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
980 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
981 table->AddGrowableCol (1, 1);
982 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
984 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
985 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
986 table->Add (_mail_server, 1, wxEXPAND | wxALL);
988 add_label_to_sizer (table, _panel, _("Mail user name"), true);
989 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
990 table->Add (_mail_user, 1, wxEXPAND | wxALL);
992 add_label_to_sizer (table, _panel, _("Mail password"), true);
993 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
994 table->Add (_mail_password, 1, wxEXPAND | wxALL);
996 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
997 wxFont font = plain->GetFont();
998 font.SetStyle (wxFONTSTYLE_ITALIC);
999 font.SetPointSize (font.GetPointSize() - 1);
1000 plain->SetFont (font);
1001 table->AddSpacer (0);
1003 add_label_to_sizer (table, _panel, _("Subject"), true);
1004 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1005 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1007 add_label_to_sizer (table, _panel, _("From address"), true);
1008 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1009 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1011 add_label_to_sizer (table, _panel, _("CC address"), true);
1012 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1013 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1015 add_label_to_sizer (table, _panel, _("BCC address"), true);
1016 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1017 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1019 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1020 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1022 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1023 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1025 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1026 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1027 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1028 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1029 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1030 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1031 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1032 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1033 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1036 void config_changed ()
1038 Config* config = Config::instance ();
1040 checked_set (_mail_server, config->mail_server ());
1041 checked_set (_mail_user, config->mail_user ());
1042 checked_set (_mail_password, config->mail_password ());
1043 checked_set (_kdm_subject, config->kdm_subject ());
1044 checked_set (_kdm_from, config->kdm_from ());
1045 checked_set (_kdm_cc, config->kdm_cc ());
1046 checked_set (_kdm_bcc, config->kdm_bcc ());
1047 checked_set (_kdm_email, Config::instance()->kdm_email ());
1050 void mail_server_changed ()
1052 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1055 void mail_user_changed ()
1057 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1060 void mail_password_changed ()
1062 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1065 void kdm_subject_changed ()
1067 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1070 void kdm_from_changed ()
1072 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1075 void kdm_cc_changed ()
1077 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1080 void kdm_bcc_changed ()
1082 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1085 void kdm_email_changed ()
1087 if (_kdm_email->GetValue().IsEmpty ()) {
1088 /* Sometimes we get sent an erroneous notification that the email
1089 is empty; I don't know why.
1093 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1096 void reset_kdm_email ()
1098 Config::instance()->reset_kdm_email ();
1099 checked_set (_kdm_email, Config::instance()->kdm_email ());
1102 wxTextCtrl* _mail_server;
1103 wxTextCtrl* _mail_user;
1104 wxTextCtrl* _mail_password;
1105 wxTextCtrl* _kdm_subject;
1106 wxTextCtrl* _kdm_from;
1107 wxTextCtrl* _kdm_cc;
1108 wxTextCtrl* _kdm_bcc;
1109 wxTextCtrl* _kdm_email;
1110 wxButton* _reset_kdm_email;
1113 /** @class AdvancedPage
1114 * @brief Advanced page of the preferences dialog.
1116 class AdvancedPage : public StockPage
1119 AdvancedPage (wxSize panel_size, int border)
1120 : StockPage (Kind_Advanced, panel_size, border)
1121 , _maximum_j2k_bandwidth (0)
1122 , _allow_any_dcp_frame_rate (0)
1132 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1133 table->AddGrowableCol (1, 1);
1134 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1137 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1138 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1139 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1140 s->Add (_maximum_j2k_bandwidth, 1);
1141 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1145 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1146 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1147 table->AddSpacer (0);
1150 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1151 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1153 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1154 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1158 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1159 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1160 t->Add (_log_general, 1, wxEXPAND | wxALL);
1161 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1162 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1163 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1164 t->Add (_log_error, 1, wxEXPAND | wxALL);
1165 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1166 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1167 table->Add (t, 0, wxALL, 6);
1170 #ifdef DCPOMATIC_WINDOWS
1171 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1172 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1173 table->AddSpacer (0);
1176 _maximum_j2k_bandwidth->SetRange (1, 1000);
1177 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1178 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1179 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1180 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1181 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1182 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1183 #ifdef DCPOMATIC_WINDOWS
1184 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1188 void config_changed ()
1190 Config* config = Config::instance ();
1192 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1193 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1194 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1195 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1196 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1197 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1198 #ifdef DCPOMATIC_WINDOWS
1199 checked_set (_win32_console, config->win32_console());
1203 void maximum_j2k_bandwidth_changed ()
1205 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1208 void allow_any_dcp_frame_rate_changed ()
1210 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1216 if (_log_general->GetValue ()) {
1217 types |= Log::TYPE_GENERAL;
1219 if (_log_warning->GetValue ()) {
1220 types |= Log::TYPE_WARNING;
1222 if (_log_error->GetValue ()) {
1223 types |= Log::TYPE_ERROR;
1225 if (_log_timing->GetValue ()) {
1226 types |= Log::TYPE_TIMING;
1228 Config::instance()->set_log_types (types);
1231 #ifdef DCPOMATIC_WINDOWS
1232 void win32_console_changed ()
1234 Config::instance()->set_win32_console (_win32_console->GetValue ());
1238 wxSpinCtrl* _maximum_j2k_bandwidth;
1239 wxCheckBox* _allow_any_dcp_frame_rate;
1240 wxCheckBox* _log_general;
1241 wxCheckBox* _log_warning;
1242 wxCheckBox* _log_error;
1243 wxCheckBox* _log_timing;
1244 #ifdef DCPOMATIC_WINDOWS
1245 wxCheckBox* _win32_console;
1249 wxPreferencesEditor*
1250 create_config_dialog ()
1252 wxPreferencesEditor* e = new wxPreferencesEditor ();
1254 #ifdef DCPOMATIC_OSX
1255 /* Width that we force some of the config panels to be on OSX so that
1256 the containing window doesn't shrink too much when we select those panels.
1257 This is obviously an unpleasant hack.
1259 wxSize ps = wxSize (520, -1);
1260 int const border = 16;
1262 wxSize ps = wxSize (-1, -1);
1263 int const border = 8;
1266 e->AddPage (new GeneralPage (ps, border));
1267 e->AddPage (new DefaultsPage (ps, border));
1268 e->AddPage (new EncodingServersPage (ps, border));
1269 e->AddPage (new KeysPage (ps, border));
1270 e->AddPage (new TMSPage (ps, border));
1271 e->AddPage (new KDMEmailPage (ps, border));
1272 e->AddPage (new AdvancedPage (ps, border));