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/colour_matrix.h>
32 #include <dcp/exceptions.h>
33 #include <dcp/signer.h>
34 #include "lib/config.h"
35 #include "lib/ratio.h"
36 #include "lib/scaler.h"
37 #include "lib/filter.h"
38 #include "lib/dcp_content_type.h"
39 #include "lib/colour_conversion.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include "config_dialog.h"
46 #include "editable_list.h"
47 #include "filter_dialog.h"
48 #include "dir_picker_ctrl.h"
49 #include "isdcf_metadata_dialog.h"
50 #include "preset_colour_conversion_dialog.h"
51 #include "server_dialog.h"
52 #include "make_signer_chain_dialog.h"
59 using boost::shared_ptr;
60 using boost::lexical_cast;
65 Page (wxSize panel_size, int border)
66 : _panel_size (panel_size)
71 wxPanel* make_panel (wxWindow* parent)
73 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
74 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
83 class GeneralPage : public wxStockPreferencesPage, public Page
86 GeneralPage (wxSize panel_size, int border)
87 : wxStockPreferencesPage (Kind_General)
88 , Page (panel_size, border)
91 , _num_local_encoding_threads (0)
92 , _check_for_updates (0)
93 , _check_for_test_updates (0)
96 wxWindow* CreateWindow (wxWindow* parent)
98 wxPanel* panel = make_panel (parent);
100 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
101 table->AddGrowableCol (1, 1);
102 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
104 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
105 table->Add (_set_language, 1);
106 _language = new wxChoice (panel, wxID_ANY);
107 _language->Append (wxT ("Deutsch"));
108 _language->Append (wxT ("English"));
109 _language->Append (wxT ("Español"));
110 _language->Append (wxT ("Français"));
111 _language->Append (wxT ("Italiano"));
112 _language->Append (wxT ("Nederlands"));
113 _language->Append (wxT ("Svenska"));
114 table->Add (_language);
116 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
117 wxFont font = restart->GetFont();
118 font.SetStyle (wxFONTSTYLE_ITALIC);
119 font.SetPointSize (font.GetPointSize() - 1);
120 restart->SetFont (font);
121 table->AddSpacer (0);
123 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
124 _num_local_encoding_threads = new wxSpinCtrl (panel);
125 table->Add (_num_local_encoding_threads, 1);
127 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
128 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
129 table->AddSpacer (0);
131 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
132 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
133 table->AddSpacer (0);
135 Config* config = Config::instance ();
137 _set_language->SetValue (config->language ());
139 if (config->language().get_value_or ("") == "fr") {
140 _language->SetSelection (3);
141 } else if (config->language().get_value_or ("") == "it") {
142 _language->SetSelection (4);
143 } else if (config->language().get_value_or ("") == "es") {
144 _language->SetSelection (2);
145 } else if (config->language().get_value_or ("") == "sv") {
146 _language->SetSelection (6);
147 } else if (config->language().get_value_or ("") == "de") {
148 _language->SetSelection (0);
149 } else if (config->language().get_value_or ("") == "nl") {
150 _language->SetSelection (5);
152 _language->SetSelection (1);
155 setup_language_sensitivity ();
157 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
158 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
160 _num_local_encoding_threads->SetRange (1, 128);
161 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
162 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
164 _check_for_updates->SetValue (config->check_for_updates ());
165 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
166 _check_for_test_updates->SetValue (config->check_for_test_updates ());
167 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
173 void setup_language_sensitivity ()
175 _language->Enable (_set_language->GetValue ());
178 void set_language_changed ()
180 setup_language_sensitivity ();
181 if (_set_language->GetValue ()) {
184 Config::instance()->unset_language ();
188 void language_changed ()
190 switch (_language->GetSelection ()) {
192 Config::instance()->set_language ("de");
195 Config::instance()->set_language ("en");
198 Config::instance()->set_language ("es");
201 Config::instance()->set_language ("fr");
204 Config::instance()->set_language ("it");
207 Config::instance()->set_language ("nl");
210 Config::instance()->set_language ("sv");
215 void check_for_updates_changed ()
217 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
220 void check_for_test_updates_changed ()
222 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
225 void num_local_encoding_threads_changed ()
227 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
230 wxCheckBox* _set_language;
232 wxSpinCtrl* _num_local_encoding_threads;
233 wxCheckBox* _check_for_updates;
234 wxCheckBox* _check_for_test_updates;
237 class DefaultsPage : public wxPreferencesPage, public Page
240 DefaultsPage (wxSize panel_size, int border)
241 : Page (panel_size, border)
244 , _isdcf_metadata_button (0)
248 , _dcp_content_type (0)
252 wxString GetName () const
254 return _("Defaults");
258 wxBitmap GetLargeIcon () const
260 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
264 wxWindow* CreateWindow (wxWindow* parent)
266 wxPanel* panel = make_panel (parent);
268 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
269 table->AddGrowableCol (1, 1);
270 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
273 add_label_to_sizer (table, panel, _("Default duration of still images"), true);
274 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
275 _still_length = new wxSpinCtrl (panel);
276 s->Add (_still_length);
277 add_label_to_sizer (s, panel, _("s"), false);
281 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
282 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
283 _directory = new DirPickerCtrl (panel);
285 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
287 table->Add (_directory, 1, wxEXPAND);
289 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
290 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
291 table->Add (_isdcf_metadata_button);
293 add_label_to_sizer (table, panel, _("Default container"), true);
294 _container = new wxChoice (panel, wxID_ANY);
295 table->Add (_container);
297 add_label_to_sizer (table, panel, _("Default content type"), true);
298 _dcp_content_type = new wxChoice (panel, wxID_ANY);
299 table->Add (_dcp_content_type);
302 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
303 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
304 _j2k_bandwidth = new wxSpinCtrl (panel);
305 s->Add (_j2k_bandwidth);
306 add_label_to_sizer (s, panel, _("Mbit/s"), false);
311 add_label_to_sizer (table, panel, _("Default audio delay"), true);
312 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
313 _audio_delay = new wxSpinCtrl (panel);
314 s->Add (_audio_delay);
315 add_label_to_sizer (s, panel, _("ms"), false);
319 add_label_to_sizer (table, panel, _("Default issuer"), true);
320 _issuer = new wxTextCtrl (panel, wxID_ANY);
321 table->Add (_issuer, 1, wxEXPAND);
323 Config* config = Config::instance ();
325 _still_length->SetRange (1, 3600);
326 _still_length->SetValue (config->default_still_length ());
327 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
329 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
330 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
332 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
334 vector<Ratio const *> ratios = Ratio::all ();
335 for (size_t i = 0; i < ratios.size(); ++i) {
336 _container->Append (std_to_wx (ratios[i]->nickname ()));
337 if (ratios[i] == config->default_container ()) {
338 _container->SetSelection (i);
342 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
344 vector<DCPContentType const *> const ct = DCPContentType::all ();
345 for (size_t i = 0; i < ct.size(); ++i) {
346 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
347 if (ct[i] == config->default_dcp_content_type ()) {
348 _dcp_content_type->SetSelection (i);
352 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
354 _j2k_bandwidth->SetRange (50, 250);
355 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
356 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
358 _audio_delay->SetRange (-1000, 1000);
359 _audio_delay->SetValue (config->default_audio_delay ());
360 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
362 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
363 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
365 config->Changed.connect (boost::bind (&DefaultsPage::config_changed, this));
371 void config_changed ()
373 _j2k_bandwidth->SetRange (50, Config::instance()->maximum_j2k_bandwidth() / 1000000);
376 void j2k_bandwidth_changed ()
378 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
381 void audio_delay_changed ()
383 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
386 void directory_changed ()
388 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
391 void edit_isdcf_metadata_clicked (wxWindow* parent)
393 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
395 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
399 void still_length_changed ()
401 Config::instance()->set_default_still_length (_still_length->GetValue ());
404 void container_changed ()
406 vector<Ratio const *> ratio = Ratio::all ();
407 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
410 void dcp_content_type_changed ()
412 vector<DCPContentType const *> ct = DCPContentType::all ();
413 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
416 void issuer_changed ()
418 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
421 wxSpinCtrl* _j2k_bandwidth;
422 wxSpinCtrl* _audio_delay;
423 wxButton* _isdcf_metadata_button;
424 wxSpinCtrl* _still_length;
425 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
426 DirPickerCtrl* _directory;
428 wxDirPickerCtrl* _directory;
430 wxChoice* _container;
431 wxChoice* _dcp_content_type;
435 class EncodingServersPage : public wxPreferencesPage, public Page
438 EncodingServersPage (wxSize panel_size, int border)
439 : Page (panel_size, border)
440 , _use_any_servers (0)
444 wxString GetName () const
450 wxBitmap GetLargeIcon () const
452 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
456 wxWindow* CreateWindow (wxWindow* parent)
458 wxPanel* panel = make_panel (parent);
460 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
461 panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
463 vector<string> columns;
464 columns.push_back (wx_to_std (_("IP address / host name")));
465 _servers_list = new EditableList<string, ServerDialog> (
468 boost::bind (&Config::servers, Config::instance()),
469 boost::bind (&Config::set_servers, Config::instance(), _1),
470 boost::bind (&EncodingServersPage::server_column, this, _1)
473 panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
475 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
476 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
483 void use_any_servers_changed ()
485 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
488 string server_column (string s)
493 wxCheckBox* _use_any_servers;
494 EditableList<string, ServerDialog>* _servers_list;
497 class ColourConversionsPage : public wxPreferencesPage, public Page
500 ColourConversionsPage (wxSize panel_size, int border)
501 : Page (panel_size, border)
504 wxString GetName () const
506 return _("Colour Conversions");
510 wxBitmap GetLargeIcon () const
512 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
515 wxWindow* CreateWindow (wxWindow* parent)
517 wxPanel* panel = make_panel (parent);
519 vector<string> columns;
520 columns.push_back (wx_to_std (_("Name")));
521 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
524 boost::bind (&Config::colour_conversions, Config::instance()),
525 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
526 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
530 panel->GetSizer()->Add (list, 1, wxEXPAND | wxALL, _border);
535 string colour_conversion_column (PresetColourConversion c)
541 class KeysPage : public wxPreferencesPage, public Page
544 KeysPage (wxSize panel_size, int border)
545 : Page (panel_size, border)
548 , _add_certificate (0)
549 , _remove_certificate (0)
550 , _remake_certificates (0)
551 , _signer_private_key (0)
552 , _load_signer_private_key (0)
553 , _decryption_certificate (0)
554 , _load_decryption_certificate (0)
555 , _decryption_private_key (0)
556 , _load_decryption_private_key (0)
557 , _export_decryption_certificate (0)
560 wxString GetName () const
566 wxBitmap GetLargeIcon () const
568 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
572 wxWindow* CreateWindow (wxWindow* parent)
574 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
575 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
576 _panel->SetSizer (overall_sizer);
578 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
579 overall_sizer->Add (m, 0, wxALL, _border);
581 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
582 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
584 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
589 ip.SetText (_("Type"));
591 _certificates->InsertColumn (0, ip);
597 ip.SetText (_("Thumbprint"));
600 wxFont font = ip.GetFont ();
601 font.SetFamily (wxFONTFAMILY_TELETYPE);
604 _certificates->InsertColumn (1, ip);
607 certificates_sizer->Add (_certificates, 1, wxEXPAND);
610 wxSizer* s = new wxBoxSizer (wxVERTICAL);
611 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
612 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
613 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
614 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
615 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
618 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
619 table->AddGrowableCol (1, 1);
620 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
622 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
623 table->Add (_remake_certificates, 0);
624 table->AddSpacer (0);
626 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
628 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
629 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
630 wxFont font = _signer_private_key->GetFont ();
631 font.SetFamily (wxFONTFAMILY_TELETYPE);
632 _signer_private_key->SetFont (font);
633 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
634 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
635 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
639 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
641 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
642 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
643 wxFont font = _decryption_certificate->GetFont ();
644 font.SetFamily (wxFONTFAMILY_TELETYPE);
645 _decryption_certificate->SetFont (font);
646 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
647 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
648 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
652 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
654 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
655 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
656 wxFont font = _decryption_private_key->GetFont ();
657 font.SetFamily (wxFONTFAMILY_TELETYPE);
658 _decryption_private_key->SetFont (font);
659 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
660 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
661 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
665 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
666 table->Add (_export_decryption_certificate);
667 table->AddSpacer (0);
669 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
670 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
671 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
672 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
673 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
674 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
675 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
676 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
677 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
679 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
681 update_certificate_list ();
682 update_signer_private_key ();
683 update_decryption_certificate ();
684 update_decryption_private_key ();
685 update_sensitivity ();
691 void add_certificate ()
693 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
695 if (d->ShowModal() == wxID_OK) {
697 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
698 _signer->certificates().add (c);
699 Config::instance()->set_signer (_signer);
700 update_certificate_list ();
701 } catch (dcp::MiscError& e) {
702 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
708 update_sensitivity ();
711 void remove_certificate ()
713 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
718 _certificates->DeleteItem (i);
719 _signer->certificates().remove (i);
720 Config::instance()->set_signer (_signer);
722 update_sensitivity ();
725 void update_certificate_list ()
727 _certificates->DeleteAllItems ();
728 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
730 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
733 _certificates->InsertItem (item);
734 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
737 _certificates->SetItem (n, 0, _("Root"));
738 } else if (n == (certs.size() - 1)) {
739 _certificates->SetItem (n, 0, _("Leaf"));
741 _certificates->SetItem (n, 0, _("Intermediate"));
748 void remake_certificates ()
750 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
751 if (d->ShowModal () == wxID_OK) {
756 d->organisational_unit (),
757 d->root_common_name (),
758 d->intermediate_common_name (),
759 d->leaf_common_name ()
763 Config::instance()->set_signer (_signer);
764 update_certificate_list ();
765 update_signer_private_key ();
771 void update_sensitivity ()
773 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
776 void update_signer_private_key ()
778 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
781 void load_signer_private_key ()
783 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
785 if (d->ShowModal() == wxID_OK) {
787 boost::filesystem::path p (wx_to_std (d->GetPath ()));
788 if (boost::filesystem::file_size (p) > 1024) {
789 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
793 _signer->set_key (dcp::file_to_string (p));
794 Config::instance()->set_signer (_signer);
795 update_signer_private_key ();
796 } catch (dcp::MiscError& e) {
797 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
803 update_sensitivity ();
807 void load_decryption_certificate ()
809 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
811 if (d->ShowModal() == wxID_OK) {
813 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
814 Config::instance()->set_decryption_certificate (c);
815 update_decryption_certificate ();
816 } catch (dcp::MiscError& e) {
817 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
824 void update_decryption_certificate ()
826 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
829 void load_decryption_private_key ()
831 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
833 if (d->ShowModal() == wxID_OK) {
835 boost::filesystem::path p (wx_to_std (d->GetPath ()));
836 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
837 update_decryption_private_key ();
838 } catch (dcp::MiscError& e) {
839 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
846 void update_decryption_private_key ()
848 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
851 void export_decryption_certificate ()
853 wxFileDialog* d = new wxFileDialog (
854 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
855 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
858 if (d->ShowModal () == wxID_OK) {
859 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
861 throw OpenFileError (wx_to_std (d->GetPath ()));
864 string const s = Config::instance()->decryption_certificate().certificate (true);
865 fwrite (s.c_str(), 1, s.length(), f);
872 wxListCtrl* _certificates;
873 wxButton* _add_certificate;
874 wxButton* _remove_certificate;
875 wxButton* _remake_certificates;
876 wxStaticText* _signer_private_key;
877 wxButton* _load_signer_private_key;
878 wxStaticText* _decryption_certificate;
879 wxButton* _load_decryption_certificate;
880 wxStaticText* _decryption_private_key;
881 wxButton* _load_decryption_private_key;
882 wxButton* _export_decryption_certificate;
883 shared_ptr<dcp::Signer> _signer;
886 class TMSPage : public wxPreferencesPage, public Page
889 TMSPage (wxSize panel_size, int border)
890 : Page (panel_size, border)
897 wxString GetName () const
903 wxBitmap GetLargeIcon () const
905 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
909 wxWindow* CreateWindow (wxWindow* parent)
911 wxPanel* panel = make_panel (parent);
913 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
914 table->AddGrowableCol (1, 1);
915 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
917 add_label_to_sizer (table, panel, _("IP address"), true);
918 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
919 table->Add (_tms_ip, 1, wxEXPAND);
921 add_label_to_sizer (table, panel, _("Target path"), true);
922 _tms_path = new wxTextCtrl (panel, wxID_ANY);
923 table->Add (_tms_path, 1, wxEXPAND);
925 add_label_to_sizer (table, panel, _("User name"), true);
926 _tms_user = new wxTextCtrl (panel, wxID_ANY);
927 table->Add (_tms_user, 1, wxEXPAND);
929 add_label_to_sizer (table, panel, _("Password"), true);
930 _tms_password = new wxTextCtrl (panel, wxID_ANY);
931 table->Add (_tms_password, 1, wxEXPAND);
933 Config* config = Config::instance ();
935 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
936 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
937 _tms_path->SetValue (std_to_wx (config->tms_path ()));
938 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
939 _tms_user->SetValue (std_to_wx (config->tms_user ()));
940 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
941 _tms_password->SetValue (std_to_wx (config->tms_password ()));
942 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
948 void tms_ip_changed ()
950 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
953 void tms_path_changed ()
955 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
958 void tms_user_changed ()
960 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
963 void tms_password_changed ()
965 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
969 wxTextCtrl* _tms_path;
970 wxTextCtrl* _tms_user;
971 wxTextCtrl* _tms_password;
974 class KDMEmailPage : public wxPreferencesPage, public Page
978 KDMEmailPage (wxSize panel_size, int border)
979 : Page (panel_size, border)
988 , _reset_kdm_email (0)
991 wxString GetName () const
993 return _("KDM Email");
997 wxBitmap GetLargeIcon () const
999 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1003 wxWindow* CreateWindow (wxWindow* parent)
1005 #ifdef DCPOMATIC_OSX
1006 /* We have to force both width and height of this one */
1007 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
1009 wxPanel* panel = new wxPanel (parent);
1011 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
1012 panel->SetSizer (s);
1014 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1015 table->AddGrowableCol (1, 1);
1016 panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1018 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
1019 _mail_server = new wxTextCtrl (panel, wxID_ANY);
1020 table->Add (_mail_server, 1, wxEXPAND | wxALL);
1022 add_label_to_sizer (table, panel, _("Mail user name"), true);
1023 _mail_user = new wxTextCtrl (panel, wxID_ANY);
1024 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1026 add_label_to_sizer (table, panel, _("Mail password"), true);
1027 _mail_password = new wxTextCtrl (panel, wxID_ANY);
1028 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1030 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
1031 wxFont font = plain->GetFont();
1032 font.SetStyle (wxFONTSTYLE_ITALIC);
1033 font.SetPointSize (font.GetPointSize() - 1);
1034 plain->SetFont (font);
1035 table->AddSpacer (0);
1037 add_label_to_sizer (table, panel, _("Subject"), true);
1038 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
1039 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1041 add_label_to_sizer (table, panel, _("From address"), true);
1042 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
1043 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1045 add_label_to_sizer (table, panel, _("CC address"), true);
1046 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
1047 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1049 add_label_to_sizer (table, panel, _("BCC address"), true);
1050 _kdm_bcc = new wxTextCtrl (panel, wxID_ANY);
1051 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1053 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1054 panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1056 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1057 panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1059 Config* config = Config::instance ();
1060 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1061 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1062 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1063 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1064 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1065 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1066 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1067 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1068 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1069 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1070 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1071 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1072 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1073 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1074 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1075 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1076 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1082 void mail_server_changed ()
1084 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1087 void mail_user_changed ()
1089 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1092 void mail_password_changed ()
1094 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1097 void kdm_subject_changed ()
1099 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1102 void kdm_from_changed ()
1104 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1107 void kdm_cc_changed ()
1109 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1112 void kdm_bcc_changed ()
1114 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1117 void kdm_email_changed ()
1119 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1122 void reset_kdm_email ()
1124 Config::instance()->reset_kdm_email ();
1125 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1128 wxTextCtrl* _mail_server;
1129 wxTextCtrl* _mail_user;
1130 wxTextCtrl* _mail_password;
1131 wxTextCtrl* _kdm_subject;
1132 wxTextCtrl* _kdm_from;
1133 wxTextCtrl* _kdm_cc;
1134 wxTextCtrl* _kdm_bcc;
1135 wxTextCtrl* _kdm_email;
1136 wxButton* _reset_kdm_email;
1139 /** @class AdvancedPage
1140 * @brief Advanced page of the preferences dialog.
1142 class AdvancedPage : public wxStockPreferencesPage, public Page
1146 AdvancedPage (wxSize panel_size, int border)
1147 : wxStockPreferencesPage (Kind_Advanced)
1148 , Page (panel_size, border)
1149 , _maximum_j2k_bandwidth (0)
1150 , _allow_any_dcp_frame_rate (0)
1157 wxWindow* CreateWindow (wxWindow* parent)
1159 wxPanel* panel = make_panel (parent);
1161 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1162 table->AddGrowableCol (1, 1);
1163 panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1166 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1167 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1168 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1169 s->Add (_maximum_j2k_bandwidth, 1);
1170 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1174 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1175 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1176 table->AddSpacer (0);
1179 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log:"));
1180 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1182 wxStaticText* m = new wxStaticText (panel, wxID_ANY, _("Log"));
1183 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1187 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1188 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1189 t->Add (_log_general, 1, wxEXPAND | wxALL);
1190 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1191 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1192 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1193 t->Add (_log_error, 1, wxEXPAND | wxALL);
1194 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1195 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1196 table->Add (t, 0, wxALL, 6);
1199 #ifdef DCPOMATIC_WINDOWS
1200 _win32_console = new wxCheckBox (panel, wxID_ANY, _("Open console window"));
1201 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1202 table->AddSpacer (0);
1205 Config* config = Config::instance ();
1207 _maximum_j2k_bandwidth->SetRange (1, 1000);
1208 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1209 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1210 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1211 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1212 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1213 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1214 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1215 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1216 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1217 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1218 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1219 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1220 #ifdef DCPOMATIC_WINDOWS
1221 _win32_console->SetValue (config->win32_console());
1222 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1230 void maximum_j2k_bandwidth_changed ()
1232 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1235 void allow_any_dcp_frame_rate_changed ()
1237 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1243 if (_log_general->GetValue ()) {
1244 types |= Log::TYPE_GENERAL;
1246 if (_log_warning->GetValue ()) {
1247 types |= Log::TYPE_WARNING;
1249 if (_log_error->GetValue ()) {
1250 types |= Log::TYPE_ERROR;
1252 if (_log_timing->GetValue ()) {
1253 types |= Log::TYPE_TIMING;
1255 Config::instance()->set_log_types (types);
1258 #ifdef DCPOMATIC_WINDOWS
1259 void win32_console_changed ()
1261 Config::instance()->set_win32_console (_win32_console->GetValue ());
1265 wxSpinCtrl* _maximum_j2k_bandwidth;
1266 wxCheckBox* _allow_any_dcp_frame_rate;
1267 wxCheckBox* _log_general;
1268 wxCheckBox* _log_warning;
1269 wxCheckBox* _log_error;
1270 wxCheckBox* _log_timing;
1271 #ifdef DCPOMATIC_WINDOWS
1272 wxCheckBox* _win32_console;
1276 wxPreferencesEditor*
1277 create_config_dialog ()
1279 wxPreferencesEditor* e = new wxPreferencesEditor ();
1281 #ifdef DCPOMATIC_OSX
1282 /* Width that we force some of the config panels to be on OSX so that
1283 the containing window doesn't shrink too much when we select those panels.
1284 This is obviously an unpleasant hack.
1286 wxSize ps = wxSize (520, -1);
1287 int const border = 16;
1289 wxSize ps = wxSize (-1, -1);
1290 int const border = 8;
1293 e->AddPage (new GeneralPage (ps, border));
1294 e->AddPage (new DefaultsPage (ps, border));
1295 e->AddPage (new EncodingServersPage (ps, border));
1296 e->AddPage (new ColourConversionsPage (ps, border));
1297 e->AddPage (new KeysPage (ps, border));
1298 e->AddPage (new TMSPage (ps, border));
1299 e->AddPage (new KDMEmailPage (ps, border));
1300 e->AddPage (new AdvancedPage (ps, border));