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 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
602 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
606 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
607 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
610 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
611 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
612 wxFont font = _signer_private_key->GetFont ();
613 font.SetFamily (wxFONTFAMILY_TELETYPE);
614 _signer_private_key->SetFont (font);
615 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
616 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
617 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
620 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
621 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
622 _decryption_certificate->SetFont (font);
623 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
624 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
625 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
628 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
629 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
630 _decryption_private_key->SetFont (font);
631 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
632 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
633 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
636 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
637 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
640 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
641 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
642 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
643 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
644 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
645 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
646 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
647 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
648 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
651 void config_changed ()
653 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
655 update_certificate_list ();
656 update_signer_private_key ();
657 update_decryption_certificate ();
658 update_decryption_private_key ();
659 update_sensitivity ();
662 void add_certificate ()
664 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
666 if (d->ShowModal() == wxID_OK) {
668 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
669 _signer->certificates().add (c);
670 Config::instance()->set_signer (_signer);
671 update_certificate_list ();
672 } catch (dcp::MiscError& e) {
673 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
679 update_sensitivity ();
682 void remove_certificate ()
684 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
689 _certificates->DeleteItem (i);
690 _signer->certificates().remove (i);
691 Config::instance()->set_signer (_signer);
693 update_sensitivity ();
696 void update_certificate_list ()
698 _certificates->DeleteAllItems ();
699 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
701 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
704 _certificates->InsertItem (item);
705 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
708 _certificates->SetItem (n, 0, _("Root"));
709 } else if (n == (certs.size() - 1)) {
710 _certificates->SetItem (n, 0, _("Leaf"));
712 _certificates->SetItem (n, 0, _("Intermediate"));
719 void remake_certificates ()
721 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
722 if (d->ShowModal () == wxID_OK) {
727 d->organisational_unit (),
728 d->root_common_name (),
729 d->intermediate_common_name (),
730 d->leaf_common_name ()
734 Config::instance()->set_signer (_signer);
735 update_certificate_list ();
736 update_signer_private_key ();
742 void update_sensitivity ()
744 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
747 void update_signer_private_key ()
749 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
752 void load_signer_private_key ()
754 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
756 if (d->ShowModal() == wxID_OK) {
758 boost::filesystem::path p (wx_to_std (d->GetPath ()));
759 if (boost::filesystem::file_size (p) > 1024) {
760 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
764 _signer->set_key (dcp::file_to_string (p));
765 Config::instance()->set_signer (_signer);
766 update_signer_private_key ();
767 } catch (dcp::MiscError& e) {
768 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
774 update_sensitivity ();
778 void load_decryption_certificate ()
780 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
782 if (d->ShowModal() == wxID_OK) {
784 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
785 Config::instance()->set_decryption_certificate (c);
786 update_decryption_certificate ();
787 } catch (dcp::MiscError& e) {
788 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
795 void update_decryption_certificate ()
797 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
800 void load_decryption_private_key ()
802 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
804 if (d->ShowModal() == wxID_OK) {
806 boost::filesystem::path p (wx_to_std (d->GetPath ()));
807 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
808 update_decryption_private_key ();
809 } catch (dcp::MiscError& e) {
810 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
817 void update_decryption_private_key ()
819 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
822 void export_decryption_certificate ()
824 wxFileDialog* d = new wxFileDialog (
825 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
826 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
829 if (d->ShowModal () == wxID_OK) {
830 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
832 throw OpenFileError (wx_to_std (d->GetPath ()));
835 string const s = Config::instance()->decryption_certificate().certificate (true);
836 fwrite (s.c_str(), 1, s.length(), f);
842 wxListCtrl* _certificates;
843 wxButton* _add_certificate;
844 wxButton* _remove_certificate;
845 wxButton* _remake_certificates;
846 wxStaticText* _signer_private_key;
847 wxButton* _load_signer_private_key;
848 wxStaticText* _decryption_certificate;
849 wxButton* _load_decryption_certificate;
850 wxStaticText* _decryption_private_key;
851 wxButton* _load_decryption_private_key;
852 wxButton* _export_decryption_certificate;
853 shared_ptr<dcp::Signer> _signer;
856 class TMSPage : public StandardPage
859 TMSPage (wxSize panel_size, int border)
860 : StandardPage (panel_size, border)
863 wxString GetName () const
869 wxBitmap GetLargeIcon () const
871 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
878 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
879 table->AddGrowableCol (1, 1);
880 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
882 add_label_to_sizer (table, _panel, _("IP address"), true);
883 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
884 table->Add (_tms_ip, 1, wxEXPAND);
886 add_label_to_sizer (table, _panel, _("Target path"), true);
887 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
888 table->Add (_tms_path, 1, wxEXPAND);
890 add_label_to_sizer (table, _panel, _("User name"), true);
891 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
892 table->Add (_tms_user, 1, wxEXPAND);
894 add_label_to_sizer (table, _panel, _("Password"), true);
895 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
896 table->Add (_tms_password, 1, wxEXPAND);
898 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
899 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
900 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
901 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
904 void config_changed ()
906 Config* config = Config::instance ();
908 checked_set (_tms_ip, config->tms_ip ());
909 checked_set (_tms_path, config->tms_path ());
910 checked_set (_tms_user, config->tms_user ());
911 checked_set (_tms_password, config->tms_password ());
914 void tms_ip_changed ()
916 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
919 void tms_path_changed ()
921 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
924 void tms_user_changed ()
926 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
929 void tms_password_changed ()
931 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
935 wxTextCtrl* _tms_path;
936 wxTextCtrl* _tms_user;
937 wxTextCtrl* _tms_password;
940 class KDMEmailPage : public StandardPage
944 KDMEmailPage (wxSize panel_size, int border)
946 /* We have to force both width and height of this one */
947 : StandardPage (wxSize (480, 128), border)
949 : StandardPage (panel_size, border)
953 wxString GetName () const
955 return _("KDM Email");
959 wxBitmap GetLargeIcon () const
961 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
968 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
969 table->AddGrowableCol (1, 1);
970 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
972 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
973 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
974 table->Add (_mail_server, 1, wxEXPAND | wxALL);
976 add_label_to_sizer (table, _panel, _("Mail user name"), true);
977 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
978 table->Add (_mail_user, 1, wxEXPAND | wxALL);
980 add_label_to_sizer (table, _panel, _("Mail password"), true);
981 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
982 table->Add (_mail_password, 1, wxEXPAND | wxALL);
984 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
985 wxFont font = plain->GetFont();
986 font.SetStyle (wxFONTSTYLE_ITALIC);
987 font.SetPointSize (font.GetPointSize() - 1);
988 plain->SetFont (font);
989 table->AddSpacer (0);
991 add_label_to_sizer (table, _panel, _("Subject"), true);
992 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
993 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
995 add_label_to_sizer (table, _panel, _("From address"), true);
996 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
997 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
999 add_label_to_sizer (table, _panel, _("CC address"), true);
1000 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1001 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1003 add_label_to_sizer (table, _panel, _("BCC address"), true);
1004 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1005 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1007 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1008 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1010 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1011 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1013 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1014 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1015 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1016 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1017 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1018 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1019 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1020 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1021 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1024 void config_changed ()
1026 Config* config = Config::instance ();
1028 checked_set (_mail_server, config->mail_server ());
1029 checked_set (_mail_user, config->mail_user ());
1030 checked_set (_mail_password, config->mail_password ());
1031 checked_set (_kdm_subject, config->kdm_subject ());
1032 checked_set (_kdm_from, config->kdm_from ());
1033 checked_set (_kdm_cc, config->kdm_cc ());
1034 checked_set (_kdm_bcc, config->kdm_bcc ());
1035 checked_set (_kdm_email, Config::instance()->kdm_email ());
1038 void mail_server_changed ()
1040 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1043 void mail_user_changed ()
1045 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1048 void mail_password_changed ()
1050 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1053 void kdm_subject_changed ()
1055 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1058 void kdm_from_changed ()
1060 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1063 void kdm_cc_changed ()
1065 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1068 void kdm_bcc_changed ()
1070 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1073 void kdm_email_changed ()
1075 if (_kdm_email->GetValue().IsEmpty ()) {
1076 /* Sometimes we get sent an erroneous notification that the email
1077 is empty; I don't know why.
1081 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1084 void reset_kdm_email ()
1086 Config::instance()->reset_kdm_email ();
1087 checked_set (_kdm_email, Config::instance()->kdm_email ());
1090 wxTextCtrl* _mail_server;
1091 wxTextCtrl* _mail_user;
1092 wxTextCtrl* _mail_password;
1093 wxTextCtrl* _kdm_subject;
1094 wxTextCtrl* _kdm_from;
1095 wxTextCtrl* _kdm_cc;
1096 wxTextCtrl* _kdm_bcc;
1097 wxTextCtrl* _kdm_email;
1098 wxButton* _reset_kdm_email;
1101 /** @class AdvancedPage
1102 * @brief Advanced page of the preferences dialog.
1104 class AdvancedPage : public StockPage
1107 AdvancedPage (wxSize panel_size, int border)
1108 : StockPage (Kind_Advanced, panel_size, border)
1109 , _maximum_j2k_bandwidth (0)
1110 , _allow_any_dcp_frame_rate (0)
1120 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1121 table->AddGrowableCol (1, 1);
1122 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1125 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1126 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1127 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1128 s->Add (_maximum_j2k_bandwidth, 1);
1129 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1133 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1134 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1135 table->AddSpacer (0);
1138 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1139 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1141 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1142 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1146 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1147 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1148 t->Add (_log_general, 1, wxEXPAND | wxALL);
1149 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1150 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1151 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1152 t->Add (_log_error, 1, wxEXPAND | wxALL);
1153 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1154 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1155 table->Add (t, 0, wxALL, 6);
1158 #ifdef DCPOMATIC_WINDOWS
1159 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1160 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1161 table->AddSpacer (0);
1164 _maximum_j2k_bandwidth->SetRange (1, 1000);
1165 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1166 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1167 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1168 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1169 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1170 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1171 #ifdef DCPOMATIC_WINDOWS
1172 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1176 void config_changed ()
1178 Config* config = Config::instance ();
1180 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1181 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1182 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1183 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1184 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1185 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1186 #ifdef DCPOMATIC_WINDOWS
1187 checked_set (_win32_console, config->win32_console());
1191 void maximum_j2k_bandwidth_changed ()
1193 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1196 void allow_any_dcp_frame_rate_changed ()
1198 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1204 if (_log_general->GetValue ()) {
1205 types |= Log::TYPE_GENERAL;
1207 if (_log_warning->GetValue ()) {
1208 types |= Log::TYPE_WARNING;
1210 if (_log_error->GetValue ()) {
1211 types |= Log::TYPE_ERROR;
1213 if (_log_timing->GetValue ()) {
1214 types |= Log::TYPE_TIMING;
1216 Config::instance()->set_log_types (types);
1219 #ifdef DCPOMATIC_WINDOWS
1220 void win32_console_changed ()
1222 Config::instance()->set_win32_console (_win32_console->GetValue ());
1226 wxSpinCtrl* _maximum_j2k_bandwidth;
1227 wxCheckBox* _allow_any_dcp_frame_rate;
1228 wxCheckBox* _log_general;
1229 wxCheckBox* _log_warning;
1230 wxCheckBox* _log_error;
1231 wxCheckBox* _log_timing;
1232 #ifdef DCPOMATIC_WINDOWS
1233 wxCheckBox* _win32_console;
1237 wxPreferencesEditor*
1238 create_config_dialog ()
1240 wxPreferencesEditor* e = new wxPreferencesEditor ();
1242 #ifdef DCPOMATIC_OSX
1243 /* Width that we force some of the config panels to be on OSX so that
1244 the containing window doesn't shrink too much when we select those panels.
1245 This is obviously an unpleasant hack.
1247 wxSize ps = wxSize (520, -1);
1248 int const border = 16;
1250 wxSize ps = wxSize (-1, -1);
1251 int const border = 8;
1254 e->AddPage (new GeneralPage (ps, border));
1255 e->AddPage (new DefaultsPage (ps, border));
1256 e->AddPage (new EncodingServersPage (ps, border));
1257 e->AddPage (new KeysPage (ps, border));
1258 e->AddPage (new TMSPage (ps, border));
1259 e->AddPage (new KDMEmailPage (ps, border));
1260 e->AddPage (new AdvancedPage (ps, border));