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 _language->Append (wxT ("Русский"));
164 _language->Append (wxT ("Polski"));
165 table->Add (_language);
167 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
168 wxFont font = restart->GetFont();
169 font.SetStyle (wxFONTSTYLE_ITALIC);
170 font.SetPointSize (font.GetPointSize() - 1);
171 restart->SetFont (font);
172 table->AddSpacer (0);
174 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
175 _num_local_encoding_threads = new wxSpinCtrl (_panel);
176 table->Add (_num_local_encoding_threads, 1);
178 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
179 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
180 table->AddSpacer (0);
182 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
183 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
184 table->AddSpacer (0);
186 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
187 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
189 _num_local_encoding_threads->SetRange (1, 128);
190 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
192 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
193 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
196 void config_changed ()
198 Config* config = Config::instance ();
200 checked_set (_set_language, config->language ());
202 if (config->language().get_value_or ("") == "fr") {
203 _language->SetSelection (3);
204 } else if (config->language().get_value_or ("") == "it") {
205 _language->SetSelection (4);
206 } else if (config->language().get_value_or ("") == "es") {
207 _language->SetSelection (2);
208 } else if (config->language().get_value_or ("") == "sv") {
209 _language->SetSelection (6);
210 } else if (config->language().get_value_or ("") == "de") {
211 _language->SetSelection (0);
212 } else if (config->language().get_value_or ("") == "nl") {
213 _language->SetSelection (5);
214 } else if (config->language().get_value_or ("") == "ru") {
215 _language->SetSelection (7);
216 } else if (config->language().get_value_or ("") == "pl") {
217 checked_set (_language, 8);
219 _language->SetSelection (1);
222 setup_language_sensitivity ();
224 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
225 checked_set (_check_for_updates, config->check_for_updates ());
226 checked_set (_check_for_test_updates, config->check_for_test_updates ());
229 void setup_language_sensitivity ()
231 _language->Enable (_set_language->GetValue ());
234 void set_language_changed ()
236 setup_language_sensitivity ();
237 if (_set_language->GetValue ()) {
240 Config::instance()->unset_language ();
244 void language_changed ()
246 switch (_language->GetSelection ()) {
248 Config::instance()->set_language ("de");
251 Config::instance()->set_language ("en");
254 Config::instance()->set_language ("es");
257 Config::instance()->set_language ("fr");
260 Config::instance()->set_language ("it");
263 Config::instance()->set_language ("nl");
266 Config::instance()->set_language ("sv");
269 Config::instance()->set_language ("ru");
272 Config::instance()->set_language ("pl");
277 void check_for_updates_changed ()
279 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
282 void check_for_test_updates_changed ()
284 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
287 void num_local_encoding_threads_changed ()
289 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
292 wxCheckBox* _set_language;
294 wxSpinCtrl* _num_local_encoding_threads;
295 wxCheckBox* _check_for_updates;
296 wxCheckBox* _check_for_test_updates;
299 class DefaultsPage : public StandardPage
302 DefaultsPage (wxSize panel_size, int border)
303 : StandardPage (panel_size, border)
306 wxString GetName () const
308 return _("Defaults");
312 wxBitmap GetLargeIcon () const
314 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
321 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
322 table->AddGrowableCol (1, 1);
323 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
326 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
327 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
328 _still_length = new wxSpinCtrl (_panel);
329 s->Add (_still_length);
330 add_label_to_sizer (s, _panel, _("s"), false);
334 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
335 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
336 _directory = new DirPickerCtrl (_panel);
338 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
340 table->Add (_directory, 1, wxEXPAND);
342 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
343 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
344 table->Add (_isdcf_metadata_button);
346 add_label_to_sizer (table, _panel, _("Default container"), true);
347 _container = new wxChoice (_panel, wxID_ANY);
348 table->Add (_container);
350 add_label_to_sizer (table, _panel, _("Default content type"), true);
351 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
352 table->Add (_dcp_content_type);
355 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
356 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
357 _j2k_bandwidth = new wxSpinCtrl (_panel);
358 s->Add (_j2k_bandwidth);
359 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
364 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
365 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
366 _audio_delay = new wxSpinCtrl (_panel);
367 s->Add (_audio_delay);
368 add_label_to_sizer (s, _panel, _("ms"), false);
372 add_label_to_sizer (table, _panel, _("Default issuer"), true);
373 _issuer = new wxTextCtrl (_panel, wxID_ANY);
374 table->Add (_issuer, 1, wxEXPAND);
376 _still_length->SetRange (1, 3600);
377 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
379 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
381 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
383 vector<Ratio const *> ratios = Ratio::all ();
384 for (size_t i = 0; i < ratios.size(); ++i) {
385 _container->Append (std_to_wx (ratios[i]->nickname ()));
388 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
390 vector<DCPContentType const *> const ct = DCPContentType::all ();
391 for (size_t i = 0; i < ct.size(); ++i) {
392 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
395 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
397 _j2k_bandwidth->SetRange (50, 250);
398 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
400 _audio_delay->SetRange (-1000, 1000);
401 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
403 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
406 void config_changed ()
408 Config* config = Config::instance ();
410 vector<Ratio const *> ratios = Ratio::all ();
411 for (size_t i = 0; i < ratios.size(); ++i) {
412 if (ratios[i] == config->default_container ()) {
413 _container->SetSelection (i);
417 vector<DCPContentType const *> const ct = DCPContentType::all ();
418 for (size_t i = 0; i < ct.size(); ++i) {
419 if (ct[i] == config->default_dcp_content_type ()) {
420 _dcp_content_type->SetSelection (i);
424 checked_set (_still_length, config->default_still_length ());
425 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
426 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
427 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
428 checked_set (_audio_delay, config->default_audio_delay ());
429 checked_set (_issuer, config->dcp_issuer ());
432 void j2k_bandwidth_changed ()
434 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
437 void audio_delay_changed ()
439 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
442 void directory_changed ()
444 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
447 void edit_isdcf_metadata_clicked ()
449 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
451 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
455 void still_length_changed ()
457 Config::instance()->set_default_still_length (_still_length->GetValue ());
460 void container_changed ()
462 vector<Ratio const *> ratio = Ratio::all ();
463 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
466 void dcp_content_type_changed ()
468 vector<DCPContentType const *> ct = DCPContentType::all ();
469 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
472 void issuer_changed ()
474 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
477 wxSpinCtrl* _j2k_bandwidth;
478 wxSpinCtrl* _audio_delay;
479 wxButton* _isdcf_metadata_button;
480 wxSpinCtrl* _still_length;
481 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
482 DirPickerCtrl* _directory;
484 wxDirPickerCtrl* _directory;
486 wxChoice* _container;
487 wxChoice* _dcp_content_type;
491 class EncodingServersPage : public StandardPage
494 EncodingServersPage (wxSize panel_size, int border)
495 : StandardPage (panel_size, border)
498 wxString GetName () const
504 wxBitmap GetLargeIcon () const
506 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
513 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
514 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
516 vector<string> columns;
517 columns.push_back (wx_to_std (_("IP address / host name")));
518 _servers_list = new EditableList<string, ServerDialog> (
521 boost::bind (&Config::servers, Config::instance()),
522 boost::bind (&Config::set_servers, Config::instance(), _1),
523 boost::bind (&EncodingServersPage::server_column, this, _1)
526 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
528 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
531 void config_changed ()
533 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
534 _servers_list->refresh ();
537 void use_any_servers_changed ()
539 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
542 string server_column (string s)
547 wxCheckBox* _use_any_servers;
548 EditableList<string, ServerDialog>* _servers_list;
551 class KeysPage : public StandardPage
554 KeysPage (wxSize panel_size, int border)
555 : StandardPage (panel_size, border)
558 wxString GetName () const
564 wxBitmap GetLargeIcon () const
566 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
573 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
574 _panel->GetSizer()->Add (m, 0, wxALL, _border);
576 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
577 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
579 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
584 ip.SetText (_("Type"));
586 _certificates->InsertColumn (0, ip);
592 ip.SetText (_("Thumbprint"));
595 wxFont font = ip.GetFont ();
596 font.SetFamily (wxFONTFAMILY_TELETYPE);
599 _certificates->InsertColumn (1, ip);
602 certificates_sizer->Add (_certificates, 1, wxEXPAND);
605 wxSizer* s = new wxBoxSizer (wxVERTICAL);
606 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
607 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
608 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
609 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
610 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
613 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
614 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
618 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
619 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
622 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
623 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
624 wxFont font = _signer_private_key->GetFont ();
625 font.SetFamily (wxFONTFAMILY_TELETYPE);
626 _signer_private_key->SetFont (font);
627 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
628 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
629 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
632 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
633 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
634 _decryption_certificate->SetFont (font);
635 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
636 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
637 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
640 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
641 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
642 _decryption_private_key->SetFont (font);
643 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
644 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
645 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
648 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
649 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
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)
1127 , _log_debug_decode (0)
1128 , _log_debug_encode (0)
1134 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1135 table->AddGrowableCol (1, 1);
1136 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1139 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1140 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1141 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1142 s->Add (_maximum_j2k_bandwidth, 1);
1143 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1147 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1148 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1149 table->AddSpacer (0);
1152 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1153 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1155 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1156 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1160 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1161 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1162 t->Add (_log_general, 1, wxEXPAND | wxALL);
1163 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1164 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1165 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1166 t->Add (_log_error, 1, wxEXPAND | wxALL);
1167 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1168 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1169 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1170 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1171 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1172 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1173 table->Add (t, 0, wxALL, 6);
1176 #ifdef DCPOMATIC_WINDOWS
1177 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1178 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1179 table->AddSpacer (0);
1182 _maximum_j2k_bandwidth->SetRange (1, 1000);
1183 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1184 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1185 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1186 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1187 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1188 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1189 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1190 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1191 #ifdef DCPOMATIC_WINDOWS
1192 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1196 void config_changed ()
1198 Config* config = Config::instance ();
1200 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1201 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1202 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1203 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1204 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1205 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1206 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1207 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1208 #ifdef DCPOMATIC_WINDOWS
1209 checked_set (_win32_console, config->win32_console());
1213 void maximum_j2k_bandwidth_changed ()
1215 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1218 void allow_any_dcp_frame_rate_changed ()
1220 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1226 if (_log_general->GetValue ()) {
1227 types |= Log::TYPE_GENERAL;
1229 if (_log_warning->GetValue ()) {
1230 types |= Log::TYPE_WARNING;
1232 if (_log_error->GetValue ()) {
1233 types |= Log::TYPE_ERROR;
1235 if (_log_timing->GetValue ()) {
1236 types |= Log::TYPE_TIMING;
1238 if (_log_debug_decode->GetValue ()) {
1239 types |= Log::TYPE_DEBUG_DECODE;
1241 if (_log_debug_encode->GetValue ()) {
1242 types |= Log::TYPE_DEBUG_ENCODE;
1244 Config::instance()->set_log_types (types);
1247 #ifdef DCPOMATIC_WINDOWS
1248 void win32_console_changed ()
1250 Config::instance()->set_win32_console (_win32_console->GetValue ());
1254 wxSpinCtrl* _maximum_j2k_bandwidth;
1255 wxCheckBox* _allow_any_dcp_frame_rate;
1256 wxCheckBox* _log_general;
1257 wxCheckBox* _log_warning;
1258 wxCheckBox* _log_error;
1259 wxCheckBox* _log_timing;
1260 wxCheckBox* _log_debug_decode;
1261 wxCheckBox* _log_debug_encode;
1262 #ifdef DCPOMATIC_WINDOWS
1263 wxCheckBox* _win32_console;
1267 wxPreferencesEditor*
1268 create_config_dialog ()
1270 wxPreferencesEditor* e = new wxPreferencesEditor ();
1272 #ifdef DCPOMATIC_OSX
1273 /* Width that we force some of the config panels to be on OSX so that
1274 the containing window doesn't shrink too much when we select those panels.
1275 This is obviously an unpleasant hack.
1277 wxSize ps = wxSize (520, -1);
1278 int const border = 16;
1280 wxSize ps = wxSize (-1, -1);
1281 int const border = 8;
1284 e->AddPage (new GeneralPage (ps, border));
1285 e->AddPage (new DefaultsPage (ps, border));
1286 e->AddPage (new EncodingServersPage (ps, border));
1287 e->AddPage (new KeysPage (ps, border));
1288 e->AddPage (new TMSPage (ps, border));
1289 e->AddPage (new KDMEmailPage (ps, border));
1290 e->AddPage (new AdvancedPage (ps, border));