2 Copyright (C) 2012-2014 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"
58 using boost::shared_ptr;
59 using boost::lexical_cast;
64 Page (wxSize panel_size, int border)
65 : _panel_size (panel_size)
74 class GeneralPage : public wxStockPreferencesPage, public Page
77 GeneralPage (wxSize panel_size, int border)
78 : wxStockPreferencesPage (Kind_General)
79 , Page (panel_size, border)
82 wxWindow* CreateWindow (wxWindow* parent)
84 wxPanel* panel = new wxPanel (parent);
85 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
88 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
89 table->AddGrowableCol (1, 1);
90 s->Add (table, 1, wxALL | wxEXPAND, _border);
92 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
93 table->Add (_set_language, 1);
94 _language = new wxChoice (panel, wxID_ANY);
95 _language->Append (wxT ("Deutsch"));
96 _language->Append (wxT ("English"));
97 _language->Append (wxT ("Español"));
98 _language->Append (wxT ("Français"));
99 _language->Append (wxT ("Italiano"));
100 _language->Append (wxT ("Nederlands"));
101 _language->Append (wxT ("Svenska"));
102 table->Add (_language);
104 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
105 wxFont font = restart->GetFont();
106 font.SetStyle (wxFONTSTYLE_ITALIC);
107 font.SetPointSize (font.GetPointSize() - 1);
108 restart->SetFont (font);
109 table->AddSpacer (0);
111 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
112 _num_local_encoding_threads = new wxSpinCtrl (panel);
113 table->Add (_num_local_encoding_threads, 1);
115 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
116 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
117 table->AddSpacer (0);
119 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
120 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
121 table->AddSpacer (0);
123 Config* config = Config::instance ();
125 _set_language->SetValue (config->language ());
127 if (config->language().get_value_or ("") == "fr") {
128 _language->SetSelection (3);
129 } else if (config->language().get_value_or ("") == "it") {
130 _language->SetSelection (4);
131 } else if (config->language().get_value_or ("") == "es") {
132 _language->SetSelection (2);
133 } else if (config->language().get_value_or ("") == "sv") {
134 _language->SetSelection (6);
135 } else if (config->language().get_value_or ("") == "de") {
136 _language->SetSelection (0);
137 } else if (config->language().get_value_or ("") == "nl") {
138 _language->SetSelection (5);
140 _language->SetSelection (1);
143 setup_language_sensitivity ();
145 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
146 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
148 _num_local_encoding_threads->SetRange (1, 128);
149 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
150 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
152 _check_for_updates->SetValue (config->check_for_updates ());
153 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
154 _check_for_test_updates->SetValue (config->check_for_test_updates ());
155 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
161 void setup_language_sensitivity ()
163 _language->Enable (_set_language->GetValue ());
166 void set_language_changed ()
168 setup_language_sensitivity ();
169 if (_set_language->GetValue ()) {
172 Config::instance()->unset_language ();
176 void language_changed ()
178 switch (_language->GetSelection ()) {
180 Config::instance()->set_language ("de");
183 Config::instance()->set_language ("en");
186 Config::instance()->set_language ("es");
189 Config::instance()->set_language ("fr");
192 Config::instance()->set_language ("it");
195 Config::instance()->set_language ("nl");
198 Config::instance()->set_language ("sv");
203 void check_for_updates_changed ()
205 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
208 void check_for_test_updates_changed ()
210 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
213 void num_local_encoding_threads_changed ()
215 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
218 wxCheckBox* _set_language;
220 wxSpinCtrl* _num_local_encoding_threads;
221 wxCheckBox* _check_for_updates;
222 wxCheckBox* _check_for_test_updates;
225 class DefaultsPage : public wxPreferencesPage, public Page
228 DefaultsPage (wxSize panel_size, int border)
229 : Page (panel_size, border)
232 wxString GetName () const
234 return _("Defaults");
238 wxBitmap GetLargeIcon () const
240 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
244 wxWindow* CreateWindow (wxWindow* parent)
246 wxPanel* panel = new wxPanel (parent);
247 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
250 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
251 table->AddGrowableCol (1, 1);
252 s->Add (table, 1, wxALL | wxEXPAND, _border);
255 add_label_to_sizer (table, panel, _("Default duration of still images"), true);
256 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
257 _still_length = new wxSpinCtrl (panel);
258 s->Add (_still_length);
259 add_label_to_sizer (s, panel, _("s"), false);
263 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
264 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
265 _directory = new DirPickerCtrl (panel);
267 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
269 table->Add (_directory, 1, wxEXPAND);
271 add_label_to_sizer (table, panel, _("Default ISDCF name details"), true);
272 _isdcf_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
273 table->Add (_isdcf_metadata_button);
275 add_label_to_sizer (table, panel, _("Default scale to"), true);
276 _scale = new wxChoice (panel, wxID_ANY);
279 add_label_to_sizer (table, panel, _("Default container"), true);
280 _container = new wxChoice (panel, wxID_ANY);
281 table->Add (_container);
283 add_label_to_sizer (table, panel, _("Default content type"), true);
284 _dcp_content_type = new wxChoice (panel, wxID_ANY);
285 table->Add (_dcp_content_type);
288 add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
289 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
290 _j2k_bandwidth = new wxSpinCtrl (panel);
291 s->Add (_j2k_bandwidth);
292 add_label_to_sizer (s, panel, _("Mbit/s"), false);
297 add_label_to_sizer (table, panel, _("Default audio delay"), true);
298 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
299 _audio_delay = new wxSpinCtrl (panel);
300 s->Add (_audio_delay);
301 add_label_to_sizer (s, panel, _("ms"), false);
305 add_label_to_sizer (table, panel, _("Default issuer"), true);
306 _issuer = new wxTextCtrl (panel, wxID_ANY);
307 table->Add (_issuer, 1, wxEXPAND);
309 add_label_to_sizer (table, panel, _("Default creator"), true);
310 _creator = new wxTextCtrl (panel, wxID_ANY);
311 table->Add (_creator, 1, wxEXPAND);
313 Config* config = Config::instance ();
315 _still_length->SetRange (1, 3600);
316 _still_length->SetValue (config->default_still_length ());
317 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
319 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
320 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
322 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this, parent));
324 vector<Ratio const *> ratio = Ratio::all ();
326 for (vector<Ratio const *>::iterator i = ratio.begin(); i != ratio.end(); ++i) {
327 _scale->Append (std_to_wx ((*i)->nickname ()));
328 if (*i == config->default_scale ()) {
329 _scale->SetSelection (n);
331 _container->Append (std_to_wx ((*i)->nickname ()));
332 if (*i == config->default_container ()) {
333 _container->SetSelection (n);
338 _scale->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::scale_changed, this));
339 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
341 vector<DCPContentType const *> const ct = DCPContentType::all ();
343 for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
344 _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
345 if (*i == config->default_dcp_content_type ()) {
346 _dcp_content_type->SetSelection (n);
351 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
353 _j2k_bandwidth->SetRange (50, 250);
354 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
355 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
357 _audio_delay->SetRange (-1000, 1000);
358 _audio_delay->SetValue (config->default_audio_delay ());
359 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
361 _issuer->SetValue (std_to_wx (config->dcp_metadata().issuer));
362 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
363 _creator->SetValue (std_to_wx (config->dcp_metadata().creator));
364 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::creator_changed, this));
370 void j2k_bandwidth_changed ()
372 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
375 void audio_delay_changed ()
377 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
380 void directory_changed ()
382 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
385 void edit_isdcf_metadata_clicked (wxWindow* parent)
387 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (parent, Config::instance()->default_isdcf_metadata ());
389 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
393 void still_length_changed ()
395 Config::instance()->set_default_still_length (_still_length->GetValue ());
398 void scale_changed ()
400 vector<Ratio const *> ratio = Ratio::all ();
401 Config::instance()->set_default_scale (ratio[_scale->GetSelection()]);
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 dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
419 m.issuer = wx_to_std (_issuer->GetValue ());
420 Config::instance()->set_dcp_metadata (m);
423 void creator_changed ()
425 dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
426 m.creator = wx_to_std (_creator->GetValue ());
427 Config::instance()->set_dcp_metadata (m);
430 wxSpinCtrl* _j2k_bandwidth;
431 wxSpinCtrl* _audio_delay;
432 wxButton* _isdcf_metadata_button;
433 wxSpinCtrl* _still_length;
434 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
435 DirPickerCtrl* _directory;
437 wxDirPickerCtrl* _directory;
440 wxChoice* _container;
441 wxChoice* _dcp_content_type;
443 wxTextCtrl* _creator;
446 class EncodingServersPage : public wxPreferencesPage, public Page
449 EncodingServersPage (wxSize panel_size, int border)
450 : Page (panel_size, border)
453 wxString GetName () const
459 wxBitmap GetLargeIcon () const
461 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
465 wxWindow* CreateWindow (wxWindow* parent)
467 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
468 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
471 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
472 s->Add (_use_any_servers, 0, wxALL, _border);
474 vector<string> columns;
475 columns.push_back (wx_to_std (_("IP address / host name")));
476 _servers_list = new EditableList<string, ServerDialog> (
479 boost::bind (&Config::servers, Config::instance()),
480 boost::bind (&Config::set_servers, Config::instance(), _1),
481 boost::bind (&EncodingServersPage::server_column, this, _1)
484 s->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
486 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
487 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
494 void use_any_servers_changed ()
496 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
499 string server_column (string s)
504 wxCheckBox* _use_any_servers;
505 EditableList<string, ServerDialog>* _servers_list;
508 class ColourConversionsPage : public wxPreferencesPage, public Page
511 ColourConversionsPage (wxSize panel_size, int border)
512 : Page (panel_size, border)
515 wxString GetName () const
517 return _("Colour Conversions");
521 wxBitmap GetLargeIcon () const
523 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
526 wxWindow* CreateWindow (wxWindow* parent)
528 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
529 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
532 vector<string> columns;
533 columns.push_back (wx_to_std (_("Name")));
534 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
537 boost::bind (&Config::colour_conversions, Config::instance()),
538 boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
539 boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
543 s->Add (list, 1, wxEXPAND | wxALL, _border);
548 string colour_conversion_column (PresetColourConversion c)
554 class KeysPage : public wxPreferencesPage, public Page
557 KeysPage (wxSize panel_size, int border)
558 : Page (panel_size, border)
561 wxString GetName () const
567 wxBitmap GetLargeIcon () const
569 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
573 wxWindow* CreateWindow (wxWindow* parent)
575 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
576 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
577 _panel->SetSizer (overall_sizer);
579 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
580 overall_sizer->Add (m, 0, wxALL, _border);
582 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
583 overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
585 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
590 ip.SetText (_("Type"));
592 _certificates->InsertColumn (0, ip);
598 ip.SetText (_("Thumbprint"));
601 wxFont font = ip.GetFont ();
602 font.SetFamily (wxFONTFAMILY_TELETYPE);
605 _certificates->InsertColumn (1, ip);
608 certificates_sizer->Add (_certificates, 1, wxEXPAND);
611 wxSizer* s = new wxBoxSizer (wxVERTICAL);
612 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
613 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
614 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
615 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
616 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
619 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
620 table->AddGrowableCol (1, 1);
621 overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
623 add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
625 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
626 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
627 wxFont font = _signer_private_key->GetFont ();
628 font.SetFamily (wxFONTFAMILY_TELETYPE);
629 _signer_private_key->SetFont (font);
630 s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
631 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
632 s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
636 add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
638 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
639 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
640 wxFont font = _decryption_certificate->GetFont ();
641 font.SetFamily (wxFONTFAMILY_TELETYPE);
642 _decryption_certificate->SetFont (font);
643 s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
644 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
645 s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
649 add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
651 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
652 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
653 wxFont font = _decryption_private_key->GetFont ();
654 font.SetFamily (wxFONTFAMILY_TELETYPE);
655 _decryption_private_key->SetFont (font);
656 s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
657 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
658 s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
662 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
663 table->Add (_export_decryption_certificate);
664 table->AddSpacer (0);
666 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
667 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
668 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
669 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
670 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
671 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
672 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
673 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
675 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
677 update_certificate_list ();
678 update_signer_private_key ();
679 update_decryption_certificate ();
680 update_decryption_private_key ();
681 update_sensitivity ();
687 void add_certificate ()
689 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
691 if (d->ShowModal() == wxID_OK) {
693 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
694 _signer->certificates().add (c);
695 Config::instance()->set_signer (_signer);
696 update_certificate_list ();
697 } catch (dcp::MiscError& e) {
698 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
704 update_sensitivity ();
707 void remove_certificate ()
709 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
714 _certificates->DeleteItem (i);
715 _signer->certificates().remove (i);
716 Config::instance()->set_signer (_signer);
718 update_sensitivity ();
721 void update_certificate_list ()
723 _certificates->DeleteAllItems ();
724 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
726 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
729 _certificates->InsertItem (item);
730 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
733 _certificates->SetItem (n, 0, _("Root"));
734 } else if (n == (certs.size() - 1)) {
735 _certificates->SetItem (n, 0, _("Leaf"));
737 _certificates->SetItem (n, 0, _("Intermediate"));
744 void update_sensitivity ()
746 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
749 void update_signer_private_key ()
751 _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
754 void load_signer_private_key ()
756 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
758 if (d->ShowModal() == wxID_OK) {
760 boost::filesystem::path p (wx_to_std (d->GetPath ()));
761 if (boost::filesystem::file_size (p) > 1024) {
762 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
766 _signer->set_key (dcp::file_to_string (p));
767 Config::instance()->set_signer (_signer);
768 update_signer_private_key ();
769 } catch (dcp::MiscError& e) {
770 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
776 update_sensitivity ();
780 void load_decryption_certificate ()
782 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
784 if (d->ShowModal() == wxID_OK) {
786 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
787 Config::instance()->set_decryption_certificate (c);
788 update_decryption_certificate ();
789 } catch (dcp::MiscError& e) {
790 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
797 void update_decryption_certificate ()
799 _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
802 void load_decryption_private_key ()
804 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
806 if (d->ShowModal() == wxID_OK) {
808 boost::filesystem::path p (wx_to_std (d->GetPath ()));
809 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
810 update_decryption_private_key ();
811 } catch (dcp::MiscError& e) {
812 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
819 void update_decryption_private_key ()
821 _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
824 void export_decryption_certificate ()
826 wxFileDialog* d = new wxFileDialog (
827 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
828 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
831 if (d->ShowModal () == wxID_OK) {
832 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
834 throw OpenFileError (wx_to_std (d->GetPath ()));
837 string const s = Config::instance()->decryption_certificate().certificate (true);
838 fwrite (s.c_str(), 1, s.length(), f);
845 wxListCtrl* _certificates;
846 wxButton* _add_certificate;
847 wxButton* _remove_certificate;
848 wxStaticText* _signer_private_key;
849 wxButton* _load_signer_private_key;
850 wxStaticText* _decryption_certificate;
851 wxButton* _load_decryption_certificate;
852 wxStaticText* _decryption_private_key;
853 wxButton* _load_decryption_private_key;
854 wxButton* _export_decryption_certificate;
855 shared_ptr<dcp::Signer> _signer;
858 class TMSPage : public wxPreferencesPage, public Page
861 TMSPage (wxSize panel_size, int border)
862 : Page (panel_size, border)
865 wxString GetName () const
871 wxBitmap GetLargeIcon () const
873 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
877 wxWindow* CreateWindow (wxWindow* parent)
879 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
880 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
883 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
884 table->AddGrowableCol (1, 1);
885 s->Add (table, 1, wxALL | wxEXPAND, _border);
887 add_label_to_sizer (table, panel, _("IP address"), true);
888 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
889 table->Add (_tms_ip, 1, wxEXPAND);
891 add_label_to_sizer (table, panel, _("Target path"), true);
892 _tms_path = new wxTextCtrl (panel, wxID_ANY);
893 table->Add (_tms_path, 1, wxEXPAND);
895 add_label_to_sizer (table, panel, _("User name"), true);
896 _tms_user = new wxTextCtrl (panel, wxID_ANY);
897 table->Add (_tms_user, 1, wxEXPAND);
899 add_label_to_sizer (table, panel, _("Password"), true);
900 _tms_password = new wxTextCtrl (panel, wxID_ANY);
901 table->Add (_tms_password, 1, wxEXPAND);
903 Config* config = Config::instance ();
905 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
906 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
907 _tms_path->SetValue (std_to_wx (config->tms_path ()));
908 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
909 _tms_user->SetValue (std_to_wx (config->tms_user ()));
910 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
911 _tms_password->SetValue (std_to_wx (config->tms_password ()));
912 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
918 void tms_ip_changed ()
920 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
923 void tms_path_changed ()
925 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
928 void tms_user_changed ()
930 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
933 void tms_password_changed ()
935 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
939 wxTextCtrl* _tms_path;
940 wxTextCtrl* _tms_user;
941 wxTextCtrl* _tms_password;
944 class KDMEmailPage : public wxPreferencesPage, public Page
948 KDMEmailPage (wxSize panel_size, int border)
949 : Page (panel_size, border)
952 wxString GetName () const
954 return _("KDM Email");
958 wxBitmap GetLargeIcon () const
960 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
964 wxWindow* CreateWindow (wxWindow* parent)
966 /* We have to force both width and height of this one */
968 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
970 wxPanel* panel = new wxPanel (parent);
972 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
975 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
976 table->AddGrowableCol (1, 1);
977 s->Add (table, 1, wxEXPAND | wxALL, _border);
979 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
980 _mail_server = new wxTextCtrl (panel, wxID_ANY);
981 table->Add (_mail_server, 1, wxEXPAND | wxALL);
983 add_label_to_sizer (table, panel, _("Mail user name"), true);
984 _mail_user = new wxTextCtrl (panel, wxID_ANY);
985 table->Add (_mail_user, 1, wxEXPAND | wxALL);
987 add_label_to_sizer (table, panel, _("Mail password"), true);
988 _mail_password = new wxTextCtrl (panel, wxID_ANY);
989 table->Add (_mail_password, 1, wxEXPAND | wxALL);
991 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
992 wxFont font = plain->GetFont();
993 font.SetStyle (wxFONTSTYLE_ITALIC);
994 font.SetPointSize (font.GetPointSize() - 1);
995 plain->SetFont (font);
996 table->AddSpacer (0);
998 add_label_to_sizer (table, panel, _("Subject"), true);
999 _kdm_subject = new wxTextCtrl (panel, wxID_ANY);
1000 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1002 add_label_to_sizer (table, panel, _("From address"), true);
1003 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
1004 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1006 add_label_to_sizer (table, panel, _("CC address"), true);
1007 _kdm_cc = new wxTextCtrl (panel, wxID_ANY);
1008 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1010 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1011 s->Add (_kdm_email, 1.5, wxEXPAND | wxALL, _border);
1013 _reset_kdm_email = new wxButton (panel, wxID_ANY, _("Reset to default text"));
1014 s->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1016 Config* config = Config::instance ();
1017 _mail_server->SetValue (std_to_wx (config->mail_server ()));
1018 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1019 _mail_user->SetValue (std_to_wx (config->mail_user ()));
1020 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1021 _mail_password->SetValue (std_to_wx (config->mail_password ()));
1022 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1023 _kdm_subject->SetValue (std_to_wx (config->kdm_subject ()));
1024 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1025 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
1026 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1027 _kdm_cc->SetValue (std_to_wx (config->kdm_cc ()));
1028 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1029 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1030 _kdm_email->SetValue (std_to_wx (Config::instance()->kdm_email ()));
1031 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1037 void mail_server_changed ()
1039 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1042 void mail_user_changed ()
1044 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1047 void mail_password_changed ()
1049 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1052 void kdm_subject_changed ()
1054 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1057 void kdm_from_changed ()
1059 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1062 void kdm_cc_changed ()
1064 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1067 void kdm_email_changed ()
1069 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1072 void reset_kdm_email ()
1074 Config::instance()->reset_kdm_email ();
1075 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
1078 wxTextCtrl* _mail_server;
1079 wxTextCtrl* _mail_user;
1080 wxTextCtrl* _mail_password;
1081 wxTextCtrl* _kdm_subject;
1082 wxTextCtrl* _kdm_from;
1083 wxTextCtrl* _kdm_cc;
1084 wxTextCtrl* _kdm_email;
1085 wxButton* _reset_kdm_email;
1088 class AdvancedPage : public wxStockPreferencesPage, public Page
1092 AdvancedPage (wxSize panel_size, int border)
1093 : wxStockPreferencesPage (Kind_Advanced)
1094 , Page (panel_size, border)
1097 wxWindow* CreateWindow (wxWindow* parent)
1099 wxPanel* panel = new wxPanel (parent);
1101 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
1102 panel->SetSizer (s);
1104 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1105 table->AddGrowableCol (1, 1);
1106 s->Add (table, 1, wxALL | wxEXPAND, _border);
1109 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
1110 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1111 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
1112 s->Add (_maximum_j2k_bandwidth, 1);
1113 add_label_to_sizer (s, panel, _("Mbit/s"), false);
1117 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
1118 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1119 table->AddSpacer (0);
1121 add_label_to_sizer (table, panel, _("Log"), true);
1122 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
1123 table->Add (_log_general, 1, wxEXPAND | wxALL);
1124 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
1125 table->AddSpacer (0);
1126 table->Add (_log_warning, 1, wxEXPAND | wxALL);
1127 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
1128 table->AddSpacer (0);
1129 table->Add (_log_error, 1, wxEXPAND | wxALL);
1130 _log_timing = new wxCheckBox (panel, wxID_ANY, S_("Config|Timing"));
1131 table->AddSpacer (0);
1132 table->Add (_log_timing, 1, wxEXPAND | wxALL);
1134 Config* config = Config::instance ();
1136 _maximum_j2k_bandwidth->SetRange (1, 500);
1137 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
1138 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1139 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
1140 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1141 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
1142 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1143 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
1144 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1145 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
1146 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1147 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
1148 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1155 void maximum_j2k_bandwidth_changed ()
1157 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1160 void allow_any_dcp_frame_rate_changed ()
1162 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1168 if (_log_general->GetValue ()) {
1169 types |= Log::TYPE_GENERAL;
1171 if (_log_warning->GetValue ()) {
1172 types |= Log::TYPE_WARNING;
1174 if (_log_error->GetValue ()) {
1175 types |= Log::TYPE_ERROR;
1177 if (_log_timing->GetValue ()) {
1178 types |= Log::TYPE_TIMING;
1180 Config::instance()->set_log_types (types);
1183 wxSpinCtrl* _maximum_j2k_bandwidth;
1184 wxCheckBox* _allow_any_dcp_frame_rate;
1185 wxCheckBox* _log_general;
1186 wxCheckBox* _log_warning;
1187 wxCheckBox* _log_error;
1188 wxCheckBox* _log_timing;
1191 wxPreferencesEditor*
1192 create_config_dialog ()
1194 wxPreferencesEditor* e = new wxPreferencesEditor ();
1196 #ifdef DCPOMATIC_OSX
1197 /* Width that we force some of the config panels to be on OSX so that
1198 the containing window doesn't shrink too much when we select those panels.
1199 This is obviously an unpleasant hack.
1201 wxSize ps = wxSize (480, -1);
1202 int const border = 16;
1204 wxSize ps = wxSize (-1, -1);
1205 int const border = 8;
1208 e->AddPage (new GeneralPage (ps, border));
1209 e->AddPage (new DefaultsPage (ps, border));
1210 e->AddPage (new EncodingServersPage (ps, border));
1211 e->AddPage (new ColourConversionsPage (ps, border));
1212 e->AddPage (new KeysPage (ps, border));
1213 e->AddPage (new TMSPage (ps, border));
1214 e->AddPage (new KDMEmailPage (ps, border));
1215 e->AddPage (new AdvancedPage (ps, border));