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/filter.h"
37 #include "lib/dcp_content_type.h"
38 #include "lib/colour_conversion.h"
41 #include "lib/cross.h"
42 #include "lib/exceptions.h"
43 #include "config_dialog.h"
45 #include "editable_list.h"
46 #include "filter_dialog.h"
47 #include "dir_picker_ctrl.h"
48 #include "isdcf_metadata_dialog.h"
49 #include "preset_colour_conversion_dialog.h"
50 #include "server_dialog.h"
51 #include "make_signer_chain_dialog.h"
58 using boost::shared_ptr;
59 using boost::lexical_cast;
64 Page (wxSize panel_size, int border)
67 , _panel_size (panel_size)
68 , _window_exists (false)
70 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
76 wxWindow* create_window (wxWindow* parent)
78 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
79 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
83 _window_exists = true;
86 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
95 virtual void config_changed () = 0;
96 virtual void setup () = 0;
98 void config_changed_wrapper ()
100 if (_window_exists) {
105 void window_destroyed ()
107 _window_exists = false;
111 boost::signals2::scoped_connection _config_connection;
115 class StockPage : public wxStockPreferencesPage, public Page
118 StockPage (Kind kind, wxSize panel_size, int border)
119 : wxStockPreferencesPage (kind)
120 , Page (panel_size, border)
123 wxWindow* CreateWindow (wxWindow* parent)
125 return create_window (parent);
129 class StandardPage : public wxPreferencesPage, public Page
132 StandardPage (wxSize panel_size, int border)
133 : Page (panel_size, border)
136 wxWindow* CreateWindow (wxWindow* parent)
138 return create_window (parent);
142 class GeneralPage : public StockPage
145 GeneralPage (wxSize panel_size, int border)
146 : StockPage (Kind_General, panel_size, border)
152 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
153 table->AddGrowableCol (1, 1);
154 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
156 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
157 table->Add (_set_language, 1);
158 _language = new wxChoice (_panel, wxID_ANY);
159 _language->Append (wxT ("Deutsch"));
160 _language->Append (wxT ("English"));
161 _language->Append (wxT ("Español"));
162 _language->Append (wxT ("Français"));
163 _language->Append (wxT ("Italiano"));
164 _language->Append (wxT ("Nederlands"));
165 _language->Append (wxT ("Svenska"));
166 table->Add (_language);
168 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
169 wxFont font = restart->GetFont();
170 font.SetStyle (wxFONTSTYLE_ITALIC);
171 font.SetPointSize (font.GetPointSize() - 1);
172 restart->SetFont (font);
173 table->AddSpacer (0);
175 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
176 _num_local_encoding_threads = new wxSpinCtrl (_panel);
177 table->Add (_num_local_encoding_threads, 1);
179 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
180 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
181 table->AddSpacer (0);
183 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
184 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
185 table->AddSpacer (0);
187 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
188 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
190 _num_local_encoding_threads->SetRange (1, 128);
191 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
193 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
194 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
197 void config_changed ()
199 Config* config = Config::instance ();
201 _set_language->SetValue (config->language ());
203 if (config->language().get_value_or ("") == "fr") {
204 _language->SetSelection (3);
205 } else if (config->language().get_value_or ("") == "it") {
206 _language->SetSelection (4);
207 } else if (config->language().get_value_or ("") == "es") {
208 _language->SetSelection (2);
209 } else if (config->language().get_value_or ("") == "sv") {
210 _language->SetSelection (6);
211 } else if (config->language().get_value_or ("") == "de") {
212 _language->SetSelection (0);
213 } else if (config->language().get_value_or ("") == "nl") {
214 _language->SetSelection (5);
216 _language->SetSelection (1);
219 setup_language_sensitivity ();
221 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
222 _check_for_updates->SetValue (config->check_for_updates ());
223 _check_for_test_updates->SetValue (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");
268 void check_for_updates_changed ()
270 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
273 void check_for_test_updates_changed ()
275 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
278 void num_local_encoding_threads_changed ()
280 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
283 wxCheckBox* _set_language;
285 wxSpinCtrl* _num_local_encoding_threads;
286 wxCheckBox* _check_for_updates;
287 wxCheckBox* _check_for_test_updates;
290 class DefaultsPage : public StandardPage
293 DefaultsPage (wxSize panel_size, int border)
294 : StandardPage (panel_size, border)
297 wxString GetName () const
299 return _("Defaults");
303 wxBitmap GetLargeIcon () const
305 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
312 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
313 table->AddGrowableCol (1, 1);
314 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
317 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
318 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
319 _still_length = new wxSpinCtrl (_panel);
320 s->Add (_still_length);
321 add_label_to_sizer (s, _panel, _("s"), false);
325 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
326 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
327 _directory = new DirPickerCtrl (_panel);
329 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
331 table->Add (_directory, 1, wxEXPAND);
333 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
334 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
335 table->Add (_isdcf_metadata_button);
337 add_label_to_sizer (table, _panel, _("Default container"), true);
338 _container = new wxChoice (_panel, wxID_ANY);
339 table->Add (_container);
341 add_label_to_sizer (table, _panel, _("Default content type"), true);
342 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
343 table->Add (_dcp_content_type);
346 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
347 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
348 _j2k_bandwidth = new wxSpinCtrl (_panel);
349 s->Add (_j2k_bandwidth);
350 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
355 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
356 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
357 _audio_delay = new wxSpinCtrl (_panel);
358 s->Add (_audio_delay);
359 add_label_to_sizer (s, _panel, _("ms"), false);
363 add_label_to_sizer (table, _panel, _("Default issuer"), true);
364 _issuer = new wxTextCtrl (_panel, wxID_ANY);
365 table->Add (_issuer, 1, wxEXPAND);
367 _still_length->SetRange (1, 3600);
368 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
370 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
372 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
374 vector<Ratio const *> ratios = Ratio::all ();
375 for (size_t i = 0; i < ratios.size(); ++i) {
376 _container->Append (std_to_wx (ratios[i]->nickname ()));
379 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
381 vector<DCPContentType const *> const ct = DCPContentType::all ();
382 for (size_t i = 0; i < ct.size(); ++i) {
383 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
386 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
388 _j2k_bandwidth->SetRange (50, 250);
389 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
391 _audio_delay->SetRange (-1000, 1000);
392 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
394 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
397 void config_changed ()
399 Config* config = Config::instance ();
401 vector<Ratio const *> ratios = Ratio::all ();
402 for (size_t i = 0; i < ratios.size(); ++i) {
403 if (ratios[i] == config->default_container ()) {
404 _container->SetSelection (i);
408 vector<DCPContentType const *> const ct = DCPContentType::all ();
409 for (size_t i = 0; i < ct.size(); ++i) {
410 if (ct[i] == config->default_dcp_content_type ()) {
411 _dcp_content_type->SetSelection (i);
415 _still_length->SetValue (config->default_still_length ());
416 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
417 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
418 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
419 _audio_delay->SetValue (config->default_audio_delay ());
420 _issuer->SetValue (std_to_wx (config->dcp_issuer ()));
423 void j2k_bandwidth_changed ()
425 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
428 void audio_delay_changed ()
430 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
433 void directory_changed ()
435 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
438 void edit_isdcf_metadata_clicked ()
440 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
442 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
446 void still_length_changed ()
448 Config::instance()->set_default_still_length (_still_length->GetValue ());
451 void container_changed ()
453 vector<Ratio const *> ratio = Ratio::all ();
454 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
457 void dcp_content_type_changed ()
459 vector<DCPContentType const *> ct = DCPContentType::all ();
460 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
463 void issuer_changed ()
465 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
468 wxSpinCtrl* _j2k_bandwidth;
469 wxSpinCtrl* _audio_delay;
470 wxButton* _isdcf_metadata_button;
471 wxSpinCtrl* _still_length;
472 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
473 DirPickerCtrl* _directory;
475 wxDirPickerCtrl* _directory;
477 wxChoice* _container;
478 wxChoice* _dcp_content_type;
482 class EncodingServersPage : public StandardPage
485 EncodingServersPage (wxSize panel_size, int border)
486 : StandardPage (panel_size, border)
489 wxString GetName () const
495 wxBitmap GetLargeIcon () const
497 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
504 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
505 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
507 vector<string> columns;
508 columns.push_back (wx_to_std (_("IP address / host name")));
509 _servers_list = new EditableList<string, ServerDialog> (
512 boost::bind (&Config::servers, Config::instance()),
513 boost::bind (&Config::set_servers, Config::instance(), _1),
514 boost::bind (&EncodingServersPage::server_column, this, _1)
517 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
519 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
522 void config_changed ()
524 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
525 _servers_list->refresh ();
528 void use_any_servers_changed ()
530 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
533 string server_column (string s)
538 wxCheckBox* _use_any_servers;
539 EditableList<string, ServerDialog>* _servers_list;
542 class ColourConversionsPage : public StandardPage
545 ColourConversionsPage (wxSize panel_size, int border)
546 : StandardPage (panel_size, border)
549 wxString GetName () const
551 return _("Colour Conversions");
555 wxBitmap GetLargeIcon () const
557 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
564 vector<string> columns;
565 columns.push_back (wx_to_std (_("Name")));
566 _list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
569 boost::bind (&Config::colour_conversions, Config::instance()),
570 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
571 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
575 _panel->GetSizer()->Add (_list, 1, wxEXPAND | wxALL, _border);
578 void config_changed ()
583 string colour_conversion_column (PresetColourConversion c)
588 EditableList<PresetColourConversion, PresetColourConversionDialog>* _list;
591 class KeysPage : public StandardPage
594 KeysPage (wxSize panel_size, int border)
595 : StandardPage (panel_size, border)
598 wxString GetName () const
604 wxBitmap GetLargeIcon () const
606 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
613 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
614 _panel->GetSizer()->Add (m, 0, wxALL, _border);
616 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
617 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
619 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
624 ip.SetText (_("Type"));
626 _certificates->InsertColumn (0, ip);
632 ip.SetText (_("Thumbprint"));
635 wxFont font = ip.GetFont ();
636 font.SetFamily (wxFONTFAMILY_TELETYPE);
639 _certificates->InsertColumn (1, ip);
642 certificates_sizer->Add (_certificates, 1, wxEXPAND);
645 wxSizer* s = new wxBoxSizer (wxVERTICAL);
646 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
647 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
648 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
649 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
650 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
653 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
654 table->AddGrowableCol (1, 1);
655 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
657 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
658 table->Add (_remake_certificates, 0);
659 table->AddSpacer (0);
661 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
663 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
664 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
665 wxFont font = _signer_private_key->GetFont ();
666 font.SetFamily (wxFONTFAMILY_TELETYPE);
667 _signer_private_key->SetFont (font);
668 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
669 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
670 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
674 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
676 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
677 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
678 wxFont font = _decryption_certificate->GetFont ();
679 font.SetFamily (wxFONTFAMILY_TELETYPE);
680 _decryption_certificate->SetFont (font);
681 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
682 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
683 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
687 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
689 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
690 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
691 wxFont font = _decryption_private_key->GetFont ();
692 font.SetFamily (wxFONTFAMILY_TELETYPE);
693 _decryption_private_key->SetFont (font);
694 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
695 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
696 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
700 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
701 table->Add (_export_decryption_certificate);
702 table->AddSpacer (0);
704 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
705 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
706 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
707 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
708 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
709 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
710 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
711 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
712 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
715 void config_changed ()
717 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
719 update_certificate_list ();
720 update_signer_private_key ();
721 update_decryption_certificate ();
722 update_decryption_private_key ();
723 update_sensitivity ();
726 void add_certificate ()
728 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
730 if (d->ShowModal() == wxID_OK) {
732 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
733 _signer->certificates().add (c);
734 Config::instance()->set_signer (_signer);
735 update_certificate_list ();
736 } catch (dcp::MiscError& e) {
737 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
743 update_sensitivity ();
746 void remove_certificate ()
748 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
753 _certificates->DeleteItem (i);
754 _signer->certificates().remove (i);
755 Config::instance()->set_signer (_signer);
757 update_sensitivity ();
760 void update_certificate_list ()
762 _certificates->DeleteAllItems ();
763 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
765 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
768 _certificates->InsertItem (item);
769 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
772 _certificates->SetItem (n, 0, _("Root"));
773 } else if (n == (certs.size() - 1)) {
774 _certificates->SetItem (n, 0, _("Leaf"));
776 _certificates->SetItem (n, 0, _("Intermediate"));
783 void remake_certificates ()
785 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
786 if (d->ShowModal () == wxID_OK) {
791 d->organisational_unit (),
792 d->root_common_name (),
793 d->intermediate_common_name (),
794 d->leaf_common_name ()
798 Config::instance()->set_signer (_signer);
799 update_certificate_list ();
800 update_signer_private_key ();
806 void update_sensitivity ()
808 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
811 void update_signer_private_key ()
813 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
816 void load_signer_private_key ()
818 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
820 if (d->ShowModal() == wxID_OK) {
822 boost::filesystem::path p (wx_to_std (d->GetPath ()));
823 if (boost::filesystem::file_size (p) > 1024) {
824 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
828 _signer->set_key (dcp::file_to_string (p));
829 Config::instance()->set_signer (_signer);
830 update_signer_private_key ();
831 } catch (dcp::MiscError& e) {
832 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
838 update_sensitivity ();
842 void load_decryption_certificate ()
844 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
846 if (d->ShowModal() == wxID_OK) {
848 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
849 Config::instance()->set_decryption_certificate (c);
850 update_decryption_certificate ();
851 } catch (dcp::MiscError& e) {
852 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
859 void update_decryption_certificate ()
861 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
864 void load_decryption_private_key ()
866 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
868 if (d->ShowModal() == wxID_OK) {
870 boost::filesystem::path p (wx_to_std (d->GetPath ()));
871 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
872 update_decryption_private_key ();
873 } catch (dcp::MiscError& e) {
874 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
881 void update_decryption_private_key ()
883 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
886 void export_decryption_certificate ()
888 wxFileDialog* d = new wxFileDialog (
889 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
890 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
893 if (d->ShowModal () == wxID_OK) {
894 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
896 throw OpenFileError (wx_to_std (d->GetPath ()));
899 string const s = Config::instance()->decryption_certificate().certificate (true);
900 fwrite (s.c_str(), 1, s.length(), f);
906 wxListCtrl* _certificates;
907 wxButton* _add_certificate;
908 wxButton* _remove_certificate;
909 wxButton* _remake_certificates;
910 wxStaticText* _signer_private_key;
911 wxButton* _load_signer_private_key;
912 wxStaticText* _decryption_certificate;
913 wxButton* _load_decryption_certificate;
914 wxStaticText* _decryption_private_key;
915 wxButton* _load_decryption_private_key;
916 wxButton* _export_decryption_certificate;
917 shared_ptr<dcp::Signer> _signer;
920 class TMSPage : public StandardPage
923 TMSPage (wxSize panel_size, int border)
924 : StandardPage (panel_size, border)
927 wxString GetName () const
933 wxBitmap GetLargeIcon () const
935 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
942 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
943 table->AddGrowableCol (1, 1);
944 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
946 add_label_to_sizer (table, _panel, _("IP address"), true);
947 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
948 table->Add (_tms_ip, 1, wxEXPAND);
950 add_label_to_sizer (table, _panel, _("Target path"), true);
951 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
952 table->Add (_tms_path, 1, wxEXPAND);
954 add_label_to_sizer (table, _panel, _("User name"), true);
955 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
956 table->Add (_tms_user, 1, wxEXPAND);
958 add_label_to_sizer (table, _panel, _("Password"), true);
959 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
960 table->Add (_tms_password, 1, wxEXPAND);
962 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
963 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
964 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
965 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
968 void config_changed ()
970 Config* config = Config::instance ();
972 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
973 _tms_path->SetValue (std_to_wx (config->tms_path ()));
974 _tms_user->SetValue (std_to_wx (config->tms_user ()));
975 _tms_password->SetValue (std_to_wx (config->tms_password ()));
978 void tms_ip_changed ()
980 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
983 void tms_path_changed ()
985 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
988 void tms_user_changed ()
990 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
993 void tms_password_changed ()
995 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
999 wxTextCtrl* _tms_path;
1000 wxTextCtrl* _tms_user;
1001 wxTextCtrl* _tms_password;
1004 class KDMEmailPage : public StandardPage
1008 KDMEmailPage (wxSize panel_size, int border)
1009 #ifdef DCPOMATIC_OSX
1010 /* We have to force both width and height of this one */
1011 : StandardPage (wxSize (480, 128), border)
1013 : StandardPage (panel_size, border)
1017 wxString GetName () const
1019 return _("KDM Email");
1022 #ifdef DCPOMATIC_OSX
1023 wxBitmap GetLargeIcon () const
1025 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1032 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1033 table->AddGrowableCol (1, 1);
1034 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1036 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1037 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1038 table->Add (_mail_server, 1, wxEXPAND | wxALL);
1040 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1041 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1042 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1044 add_label_to_sizer (table, _panel, _("Mail password"), true);
1045 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1046 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1048 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
1049 wxFont font = plain->GetFont();
1050 font.SetStyle (wxFONTSTYLE_ITALIC);
1051 font.SetPointSize (font.GetPointSize() - 1);
1052 plain->SetFont (font);
1053 table->AddSpacer (0);
1055 add_label_to_sizer (table, _panel, _("Subject"), true);
1056 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1057 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1059 add_label_to_sizer (table, _panel, _("From address"), true);
1060 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1061 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1063 add_label_to_sizer (table, _panel, _("CC address"), true);
1064 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1065 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1067 add_label_to_sizer (table, _panel, _("BCC address"), true);
1068 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1069 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1071 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1072 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1074 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1075 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1077 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1078 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1079 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1080 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1081 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1082 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1083 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1084 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1085 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1088 void config_changed ()
1090 Config* config = Config::instance ();
1092 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1093 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1094 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1095 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1096 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1097 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1098 _kdm_bcc->SetValue (std_to_wx (config->kdm_bcc ()));
1099 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1102 void mail_server_changed ()
1104 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1107 void mail_user_changed ()
1109 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1112 void mail_password_changed ()
1114 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1117 void kdm_subject_changed ()
1119 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1122 void kdm_from_changed ()
1124 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1127 void kdm_cc_changed ()
1129 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1132 void kdm_bcc_changed ()
1134 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1137 void kdm_email_changed ()
1139 if (_kdm_email->GetValue().IsEmpty ()) {
1140 /* Sometimes we get sent an erroneous notification that the email
1141 is empty; I don't know why.
1145 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1148 void reset_kdm_email ()
1150 Config::instance()->reset_kdm_email ();
1151 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1154 wxTextCtrl* _mail_server;
1155 wxTextCtrl* _mail_user;
1156 wxTextCtrl* _mail_password;
1157 wxTextCtrl* _kdm_subject;
1158 wxTextCtrl* _kdm_from;
1159 wxTextCtrl* _kdm_cc;
1160 wxTextCtrl* _kdm_bcc;
1161 wxTextCtrl* _kdm_email;
1162 wxButton* _reset_kdm_email;
1165 /** @class AdvancedPage
1166 * @brief Advanced page of the preferences dialog.
1168 class AdvancedPage : public StockPage
1171 AdvancedPage (wxSize panel_size, int border)
1172 : StockPage (Kind_Advanced, panel_size, border)
1173 , _maximum_j2k_bandwidth (0)
1174 , _allow_any_dcp_frame_rate (0)
1184 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1185 table->AddGrowableCol (1, 1);
1186 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1189 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1190 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1191 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1192 s->Add (_maximum_j2k_bandwidth, 1);
1193 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1197 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1198 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1199 table->AddSpacer (0);
1202 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1203 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1205 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1206 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1210 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1211 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1212 t->Add (_log_general, 1, wxEXPAND | wxALL);
1213 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1214 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1215 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1216 t->Add (_log_error, 1, wxEXPAND | wxALL);
1217 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1218 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1219 table->Add (t, 0, wxALL, 6);
1222 #ifdef DCPOMATIC_WINDOWS
1223 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1224 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1225 table->AddSpacer (0);
1228 _maximum_j2k_bandwidth->SetRange (1, 1000);
1229 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1230 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1231 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1232 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1233 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1234 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1235 #ifdef DCPOMATIC_WINDOWS
1236 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1240 void config_changed ()
1242 Config* config = Config::instance ();
1244 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1245 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1246 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1247 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1248 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1249 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1250 #ifdef DCPOMATIC_WINDOWS
1251 _win32_console->SetValue (config->win32_console());
1255 void maximum_j2k_bandwidth_changed ()
1257 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1260 void allow_any_dcp_frame_rate_changed ()
1262 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1268 if (_log_general->GetValue ()) {
1269 types |= Log::TYPE_GENERAL;
1271 if (_log_warning->GetValue ()) {
1272 types |= Log::TYPE_WARNING;
1274 if (_log_error->GetValue ()) {
1275 types |= Log::TYPE_ERROR;
1277 if (_log_timing->GetValue ()) {
1278 types |= Log::TYPE_TIMING;
1280 Config::instance()->set_log_types (types);
1283 #ifdef DCPOMATIC_WINDOWS
1284 void win32_console_changed ()
1286 Config::instance()->set_win32_console (_win32_console->GetValue ());
1290 wxSpinCtrl* _maximum_j2k_bandwidth;
1291 wxCheckBox* _allow_any_dcp_frame_rate;
1292 wxCheckBox* _log_general;
1293 wxCheckBox* _log_warning;
1294 wxCheckBox* _log_error;
1295 wxCheckBox* _log_timing;
1296 #ifdef DCPOMATIC_WINDOWS
1297 wxCheckBox* _win32_console;
1301 wxPreferencesEditor*
1302 create_config_dialog ()
1304 wxPreferencesEditor* e = new wxPreferencesEditor ();
1306 #ifdef DCPOMATIC_OSX
1307 /* Width that we force some of the config panels to be on OSX so that
1308 the containing window doesn't shrink too much when we select those panels.
1309 This is obviously an unpleasant hack.
1311 wxSize ps = wxSize (520, -1);
1312 int const border = 16;
1314 wxSize ps = wxSize (-1, -1);
1315 int const border = 8;
1318 e->AddPage (new GeneralPage (ps, border));
1319 e->AddPage (new DefaultsPage (ps, border));
1320 e->AddPage (new EncodingServersPage (ps, border));
1321 e->AddPage (new ColourConversionsPage (ps, border));
1322 e->AddPage (new KeysPage (ps, border));
1323 e->AddPage (new TMSPage (ps, border));
1324 e->AddPage (new KDMEmailPage (ps, border));
1325 e->AddPage (new AdvancedPage (ps, border));