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 table->Add (_language);
166 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
167 wxFont font = restart->GetFont();
168 font.SetStyle (wxFONTSTYLE_ITALIC);
169 font.SetPointSize (font.GetPointSize() - 1);
170 restart->SetFont (font);
171 table->AddSpacer (0);
173 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
174 _num_local_encoding_threads = new wxSpinCtrl (_panel);
175 table->Add (_num_local_encoding_threads, 1);
177 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
178 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
179 table->AddSpacer (0);
181 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
182 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
183 table->AddSpacer (0);
185 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
186 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
188 _num_local_encoding_threads->SetRange (1, 128);
189 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
191 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
192 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
195 void config_changed ()
197 Config* config = Config::instance ();
199 checked_set (_set_language, config->language ());
201 if (config->language().get_value_or ("") == "fr") {
202 _language->SetSelection (3);
203 } else if (config->language().get_value_or ("") == "it") {
204 _language->SetSelection (4);
205 } else if (config->language().get_value_or ("") == "es") {
206 _language->SetSelection (2);
207 } else if (config->language().get_value_or ("") == "sv") {
208 _language->SetSelection (6);
209 } else if (config->language().get_value_or ("") == "de") {
210 _language->SetSelection (0);
211 } else if (config->language().get_value_or ("") == "nl") {
212 _language->SetSelection (5);
213 } else if (config->language().get_value_or ("") == "ru") {
214 _language->SetSelection (7);
216 _language->SetSelection (1);
219 setup_language_sensitivity ();
221 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
222 checked_set (_check_for_updates, config->check_for_updates ());
223 checked_set (_check_for_test_updates, config->check_for_test_updates ());
226 void setup_language_sensitivity ()
228 _language->Enable (_set_language->GetValue ());
231 void set_language_changed ()
233 setup_language_sensitivity ();
234 if (_set_language->GetValue ()) {
237 Config::instance()->unset_language ();
241 void language_changed ()
243 switch (_language->GetSelection ()) {
245 Config::instance()->set_language ("de");
248 Config::instance()->set_language ("en");
251 Config::instance()->set_language ("es");
254 Config::instance()->set_language ("fr");
257 Config::instance()->set_language ("it");
260 Config::instance()->set_language ("nl");
263 Config::instance()->set_language ("sv");
266 Config::instance()->set_language ("ru");
271 void check_for_updates_changed ()
273 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
276 void check_for_test_updates_changed ()
278 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
281 void num_local_encoding_threads_changed ()
283 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
286 wxCheckBox* _set_language;
288 wxSpinCtrl* _num_local_encoding_threads;
289 wxCheckBox* _check_for_updates;
290 wxCheckBox* _check_for_test_updates;
293 class DefaultsPage : public StandardPage
296 DefaultsPage (wxSize panel_size, int border)
297 : StandardPage (panel_size, border)
300 wxString GetName () const
302 return _("Defaults");
306 wxBitmap GetLargeIcon () const
308 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
315 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
316 table->AddGrowableCol (1, 1);
317 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
320 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
321 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
322 _still_length = new wxSpinCtrl (_panel);
323 s->Add (_still_length);
324 add_label_to_sizer (s, _panel, _("s"), false);
328 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
329 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
330 _directory = new DirPickerCtrl (_panel);
332 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
334 table->Add (_directory, 1, wxEXPAND);
336 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
337 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
338 table->Add (_isdcf_metadata_button);
340 add_label_to_sizer (table, _panel, _("Default container"), true);
341 _container = new wxChoice (_panel, wxID_ANY);
342 table->Add (_container);
344 add_label_to_sizer (table, _panel, _("Default content type"), true);
345 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
346 table->Add (_dcp_content_type);
349 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
350 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
351 _j2k_bandwidth = new wxSpinCtrl (_panel);
352 s->Add (_j2k_bandwidth);
353 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
358 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
359 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
360 _audio_delay = new wxSpinCtrl (_panel);
361 s->Add (_audio_delay);
362 add_label_to_sizer (s, _panel, _("ms"), false);
366 add_label_to_sizer (table, _panel, _("Default issuer"), true);
367 _issuer = new wxTextCtrl (_panel, wxID_ANY);
368 table->Add (_issuer, 1, wxEXPAND);
370 _still_length->SetRange (1, 3600);
371 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
373 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
375 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
377 vector<Ratio const *> ratios = Ratio::all ();
378 for (size_t i = 0; i < ratios.size(); ++i) {
379 _container->Append (std_to_wx (ratios[i]->nickname ()));
382 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
384 vector<DCPContentType const *> const ct = DCPContentType::all ();
385 for (size_t i = 0; i < ct.size(); ++i) {
386 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
389 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
391 _j2k_bandwidth->SetRange (50, 250);
392 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
394 _audio_delay->SetRange (-1000, 1000);
395 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
397 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
400 void config_changed ()
402 Config* config = Config::instance ();
404 vector<Ratio const *> ratios = Ratio::all ();
405 for (size_t i = 0; i < ratios.size(); ++i) {
406 if (ratios[i] == config->default_container ()) {
407 _container->SetSelection (i);
411 vector<DCPContentType const *> const ct = DCPContentType::all ();
412 for (size_t i = 0; i < ct.size(); ++i) {
413 if (ct[i] == config->default_dcp_content_type ()) {
414 _dcp_content_type->SetSelection (i);
418 checked_set (_still_length, config->default_still_length ());
419 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
420 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
421 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
422 checked_set (_audio_delay, config->default_audio_delay ());
423 checked_set (_issuer, config->dcp_issuer ());
426 void j2k_bandwidth_changed ()
428 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
431 void audio_delay_changed ()
433 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
436 void directory_changed ()
438 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
441 void edit_isdcf_metadata_clicked ()
443 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
445 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
449 void still_length_changed ()
451 Config::instance()->set_default_still_length (_still_length->GetValue ());
454 void container_changed ()
456 vector<Ratio const *> ratio = Ratio::all ();
457 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
460 void dcp_content_type_changed ()
462 vector<DCPContentType const *> ct = DCPContentType::all ();
463 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
466 void issuer_changed ()
468 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
471 wxSpinCtrl* _j2k_bandwidth;
472 wxSpinCtrl* _audio_delay;
473 wxButton* _isdcf_metadata_button;
474 wxSpinCtrl* _still_length;
475 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
476 DirPickerCtrl* _directory;
478 wxDirPickerCtrl* _directory;
480 wxChoice* _container;
481 wxChoice* _dcp_content_type;
485 class EncodingServersPage : public StandardPage
488 EncodingServersPage (wxSize panel_size, int border)
489 : StandardPage (panel_size, border)
492 wxString GetName () const
498 wxBitmap GetLargeIcon () const
500 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
507 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
508 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
510 vector<string> columns;
511 columns.push_back (wx_to_std (_("IP address / host name")));
512 _servers_list = new EditableList<string, ServerDialog> (
515 boost::bind (&Config::servers, Config::instance()),
516 boost::bind (&Config::set_servers, Config::instance(), _1),
517 boost::bind (&EncodingServersPage::server_column, this, _1)
520 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
522 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
525 void config_changed ()
527 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
528 _servers_list->refresh ();
531 void use_any_servers_changed ()
533 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
536 string server_column (string s)
541 wxCheckBox* _use_any_servers;
542 EditableList<string, ServerDialog>* _servers_list;
545 class KeysPage : public StandardPage
548 KeysPage (wxSize panel_size, int border)
549 : StandardPage (panel_size, border)
552 wxString GetName () const
558 wxBitmap GetLargeIcon () const
560 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
567 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
568 _panel->GetSizer()->Add (m, 0, wxALL, _border);
570 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
571 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
573 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
578 ip.SetText (_("Type"));
580 _certificates->InsertColumn (0, ip);
586 ip.SetText (_("Thumbprint"));
589 wxFont font = ip.GetFont ();
590 font.SetFamily (wxFONTFAMILY_TELETYPE);
593 _certificates->InsertColumn (1, ip);
596 certificates_sizer->Add (_certificates, 1, wxEXPAND);
599 wxSizer* s = new wxBoxSizer (wxVERTICAL);
600 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
601 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
602 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
603 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
604 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
607 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
608 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
612 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
613 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
616 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
617 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
618 wxFont font = _signer_private_key->GetFont ();
619 font.SetFamily (wxFONTFAMILY_TELETYPE);
620 _signer_private_key->SetFont (font);
621 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
622 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
623 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
626 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
627 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
628 _decryption_certificate->SetFont (font);
629 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
630 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
631 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
634 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
635 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
636 _decryption_private_key->SetFont (font);
637 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
638 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
639 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
642 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
643 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
646 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
647 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
648 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
649 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
650 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
651 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
652 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
653 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
654 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
657 void config_changed ()
659 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
661 update_certificate_list ();
662 update_signer_private_key ();
663 update_decryption_certificate ();
664 update_decryption_private_key ();
665 update_sensitivity ();
668 void add_certificate ()
670 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
672 if (d->ShowModal() == wxID_OK) {
674 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
675 _signer->certificates().add (c);
676 Config::instance()->set_signer (_signer);
677 update_certificate_list ();
678 } catch (dcp::MiscError& e) {
679 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
685 update_sensitivity ();
688 void remove_certificate ()
690 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
695 _certificates->DeleteItem (i);
696 _signer->certificates().remove (i);
697 Config::instance()->set_signer (_signer);
699 update_sensitivity ();
702 void update_certificate_list ()
704 _certificates->DeleteAllItems ();
705 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
707 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
710 _certificates->InsertItem (item);
711 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
714 _certificates->SetItem (n, 0, _("Root"));
715 } else if (n == (certs.size() - 1)) {
716 _certificates->SetItem (n, 0, _("Leaf"));
718 _certificates->SetItem (n, 0, _("Intermediate"));
725 void remake_certificates ()
727 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
728 if (d->ShowModal () == wxID_OK) {
733 d->organisational_unit (),
734 d->root_common_name (),
735 d->intermediate_common_name (),
736 d->leaf_common_name ()
740 Config::instance()->set_signer (_signer);
741 update_certificate_list ();
742 update_signer_private_key ();
748 void update_sensitivity ()
750 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
753 void update_signer_private_key ()
755 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
758 void load_signer_private_key ()
760 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
762 if (d->ShowModal() == wxID_OK) {
764 boost::filesystem::path p (wx_to_std (d->GetPath ()));
765 if (boost::filesystem::file_size (p) > 1024) {
766 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
770 _signer->set_key (dcp::file_to_string (p));
771 Config::instance()->set_signer (_signer);
772 update_signer_private_key ();
773 } catch (dcp::MiscError& e) {
774 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
780 update_sensitivity ();
784 void load_decryption_certificate ()
786 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
788 if (d->ShowModal() == wxID_OK) {
790 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
791 Config::instance()->set_decryption_certificate (c);
792 update_decryption_certificate ();
793 } catch (dcp::MiscError& e) {
794 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
801 void update_decryption_certificate ()
803 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
806 void load_decryption_private_key ()
808 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
810 if (d->ShowModal() == wxID_OK) {
812 boost::filesystem::path p (wx_to_std (d->GetPath ()));
813 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
814 update_decryption_private_key ();
815 } catch (dcp::MiscError& e) {
816 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
823 void update_decryption_private_key ()
825 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
828 void export_decryption_certificate ()
830 wxFileDialog* d = new wxFileDialog (
831 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
832 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
835 if (d->ShowModal () == wxID_OK) {
836 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
838 throw OpenFileError (wx_to_std (d->GetPath ()));
841 string const s = Config::instance()->decryption_certificate().certificate (true);
842 fwrite (s.c_str(), 1, s.length(), f);
848 wxListCtrl* _certificates;
849 wxButton* _add_certificate;
850 wxButton* _remove_certificate;
851 wxButton* _remake_certificates;
852 wxStaticText* _signer_private_key;
853 wxButton* _load_signer_private_key;
854 wxStaticText* _decryption_certificate;
855 wxButton* _load_decryption_certificate;
856 wxStaticText* _decryption_private_key;
857 wxButton* _load_decryption_private_key;
858 wxButton* _export_decryption_certificate;
859 shared_ptr<dcp::Signer> _signer;
862 class TMSPage : public StandardPage
865 TMSPage (wxSize panel_size, int border)
866 : StandardPage (panel_size, border)
869 wxString GetName () const
875 wxBitmap GetLargeIcon () const
877 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
884 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
885 table->AddGrowableCol (1, 1);
886 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
888 add_label_to_sizer (table, _panel, _("IP address"), true);
889 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
890 table->Add (_tms_ip, 1, wxEXPAND);
892 add_label_to_sizer (table, _panel, _("Target path"), true);
893 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
894 table->Add (_tms_path, 1, wxEXPAND);
896 add_label_to_sizer (table, _panel, _("User name"), true);
897 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
898 table->Add (_tms_user, 1, wxEXPAND);
900 add_label_to_sizer (table, _panel, _("Password"), true);
901 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
902 table->Add (_tms_password, 1, wxEXPAND);
904 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
905 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
906 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
907 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
910 void config_changed ()
912 Config* config = Config::instance ();
914 checked_set (_tms_ip, config->tms_ip ());
915 checked_set (_tms_path, config->tms_path ());
916 checked_set (_tms_user, config->tms_user ());
917 checked_set (_tms_password, config->tms_password ());
920 void tms_ip_changed ()
922 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
925 void tms_path_changed ()
927 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
930 void tms_user_changed ()
932 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
935 void tms_password_changed ()
937 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
941 wxTextCtrl* _tms_path;
942 wxTextCtrl* _tms_user;
943 wxTextCtrl* _tms_password;
946 class KDMEmailPage : public StandardPage
950 KDMEmailPage (wxSize panel_size, int border)
952 /* We have to force both width and height of this one */
953 : StandardPage (wxSize (480, 128), border)
955 : StandardPage (panel_size, border)
959 wxString GetName () const
961 return _("KDM Email");
965 wxBitmap GetLargeIcon () const
967 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
974 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
975 table->AddGrowableCol (1, 1);
976 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
978 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
979 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
980 table->Add (_mail_server, 1, wxEXPAND | wxALL);
982 add_label_to_sizer (table, _panel, _("Mail user name"), true);
983 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
984 table->Add (_mail_user, 1, wxEXPAND | wxALL);
986 add_label_to_sizer (table, _panel, _("Mail password"), true);
987 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
988 table->Add (_mail_password, 1, wxEXPAND | wxALL);
990 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
991 wxFont font = plain->GetFont();
992 font.SetStyle (wxFONTSTYLE_ITALIC);
993 font.SetPointSize (font.GetPointSize() - 1);
994 plain->SetFont (font);
995 table->AddSpacer (0);
997 add_label_to_sizer (table, _panel, _("Subject"), true);
998 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
999 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1001 add_label_to_sizer (table, _panel, _("From address"), true);
1002 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1003 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1005 add_label_to_sizer (table, _panel, _("CC address"), true);
1006 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1007 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1009 add_label_to_sizer (table, _panel, _("BCC address"), true);
1010 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1011 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1013 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1014 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1016 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1017 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1019 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1020 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1021 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1022 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1023 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1024 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1025 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1026 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1027 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1030 void config_changed ()
1032 Config* config = Config::instance ();
1034 checked_set (_mail_server, config->mail_server ());
1035 checked_set (_mail_user, config->mail_user ());
1036 checked_set (_mail_password, config->mail_password ());
1037 checked_set (_kdm_subject, config->kdm_subject ());
1038 checked_set (_kdm_from, config->kdm_from ());
1039 checked_set (_kdm_cc, config->kdm_cc ());
1040 checked_set (_kdm_bcc, config->kdm_bcc ());
1041 checked_set (_kdm_email, Config::instance()->kdm_email ());
1044 void mail_server_changed ()
1046 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1049 void mail_user_changed ()
1051 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1054 void mail_password_changed ()
1056 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1059 void kdm_subject_changed ()
1061 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1064 void kdm_from_changed ()
1066 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1069 void kdm_cc_changed ()
1071 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1074 void kdm_bcc_changed ()
1076 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1079 void kdm_email_changed ()
1081 if (_kdm_email->GetValue().IsEmpty ()) {
1082 /* Sometimes we get sent an erroneous notification that the email
1083 is empty; I don't know why.
1087 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1090 void reset_kdm_email ()
1092 Config::instance()->reset_kdm_email ();
1093 checked_set (_kdm_email, Config::instance()->kdm_email ());
1096 wxTextCtrl* _mail_server;
1097 wxTextCtrl* _mail_user;
1098 wxTextCtrl* _mail_password;
1099 wxTextCtrl* _kdm_subject;
1100 wxTextCtrl* _kdm_from;
1101 wxTextCtrl* _kdm_cc;
1102 wxTextCtrl* _kdm_bcc;
1103 wxTextCtrl* _kdm_email;
1104 wxButton* _reset_kdm_email;
1107 /** @class AdvancedPage
1108 * @brief Advanced page of the preferences dialog.
1110 class AdvancedPage : public StockPage
1113 AdvancedPage (wxSize panel_size, int border)
1114 : StockPage (Kind_Advanced, panel_size, border)
1115 , _maximum_j2k_bandwidth (0)
1116 , _allow_any_dcp_frame_rate (0)
1121 , _log_debug_decode (0)
1122 , _log_debug_encode (0)
1128 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1129 table->AddGrowableCol (1, 1);
1130 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1133 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1134 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1135 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1136 s->Add (_maximum_j2k_bandwidth, 1);
1137 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1141 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1142 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1143 table->AddSpacer (0);
1146 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1147 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1149 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1150 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1154 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1155 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1156 t->Add (_log_general, 1, wxEXPAND | wxALL);
1157 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1158 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1159 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1160 t->Add (_log_error, 1, wxEXPAND | wxALL);
1161 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1162 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1163 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1164 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1165 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1166 t->Add (_log_debug_encode, 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 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1184 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1185 #ifdef DCPOMATIC_WINDOWS
1186 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1190 void config_changed ()
1192 Config* config = Config::instance ();
1194 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1195 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1196 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1197 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1198 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1199 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1200 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1201 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1202 #ifdef DCPOMATIC_WINDOWS
1203 checked_set (_win32_console, config->win32_console());
1207 void maximum_j2k_bandwidth_changed ()
1209 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1212 void allow_any_dcp_frame_rate_changed ()
1214 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1220 if (_log_general->GetValue ()) {
1221 types |= Log::TYPE_GENERAL;
1223 if (_log_warning->GetValue ()) {
1224 types |= Log::TYPE_WARNING;
1226 if (_log_error->GetValue ()) {
1227 types |= Log::TYPE_ERROR;
1229 if (_log_timing->GetValue ()) {
1230 types |= Log::TYPE_TIMING;
1232 if (_log_debug_decode->GetValue ()) {
1233 types |= Log::TYPE_DEBUG_DECODE;
1235 if (_log_debug_encode->GetValue ()) {
1236 types |= Log::TYPE_DEBUG_ENCODE;
1238 Config::instance()->set_log_types (types);
1241 #ifdef DCPOMATIC_WINDOWS
1242 void win32_console_changed ()
1244 Config::instance()->set_win32_console (_win32_console->GetValue ());
1248 wxSpinCtrl* _maximum_j2k_bandwidth;
1249 wxCheckBox* _allow_any_dcp_frame_rate;
1250 wxCheckBox* _log_general;
1251 wxCheckBox* _log_warning;
1252 wxCheckBox* _log_error;
1253 wxCheckBox* _log_timing;
1254 wxCheckBox* _log_debug_decode;
1255 wxCheckBox* _log_debug_encode;
1256 #ifdef DCPOMATIC_WINDOWS
1257 wxCheckBox* _win32_console;
1261 wxPreferencesEditor*
1262 create_config_dialog ()
1264 wxPreferencesEditor* e = new wxPreferencesEditor ();
1266 #ifdef DCPOMATIC_OSX
1267 /* Width that we force some of the config panels to be on OSX so that
1268 the containing window doesn't shrink too much when we select those panels.
1269 This is obviously an unpleasant hack.
1271 wxSize ps = wxSize (520, -1);
1272 int const border = 16;
1274 wxSize ps = wxSize (-1, -1);
1275 int const border = 8;
1278 e->AddPage (new GeneralPage (ps, border));
1279 e->AddPage (new DefaultsPage (ps, border));
1280 e->AddPage (new EncodingServersPage (ps, border));
1281 e->AddPage (new KeysPage (ps, border));
1282 e->AddPage (new TMSPage (ps, border));
1283 e->AddPage (new KDMEmailPage (ps, border));
1284 e->AddPage (new AdvancedPage (ps, border));