2 Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/config_dialog.cc
21 * @brief A dialogue to edit DCP-o-matic configuration.
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/exceptions.h>
32 #include <dcp/signer.h>
33 #include "lib/config.h"
34 #include "lib/ratio.h"
35 #include "lib/filter.h"
36 #include "lib/dcp_content_type.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include "config_dialog.h"
43 #include "editable_list.h"
44 #include "filter_dialog.h"
45 #include "dir_picker_ctrl.h"
46 #include "isdcf_metadata_dialog.h"
47 #include "server_dialog.h"
48 #include "make_signer_chain_dialog.h"
55 using boost::shared_ptr;
56 using boost::lexical_cast;
61 Page (wxSize panel_size, int border)
64 , _panel_size (panel_size)
65 , _window_exists (false)
67 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
73 wxWindow* create_window (wxWindow* parent)
75 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
76 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
80 _window_exists = true;
83 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
92 virtual void config_changed () = 0;
93 virtual void setup () = 0;
95 void config_changed_wrapper ()
102 void window_destroyed ()
104 _window_exists = false;
108 boost::signals2::scoped_connection _config_connection;
112 class StockPage : public wxStockPreferencesPage, public Page
115 StockPage (Kind kind, wxSize panel_size, int border)
116 : wxStockPreferencesPage (kind)
117 , Page (panel_size, border)
120 wxWindow* CreateWindow (wxWindow* parent)
122 return create_window (parent);
126 class StandardPage : public wxPreferencesPage, public Page
129 StandardPage (wxSize panel_size, int border)
130 : Page (panel_size, border)
133 wxWindow* CreateWindow (wxWindow* parent)
135 return create_window (parent);
139 class GeneralPage : public StockPage
142 GeneralPage (wxSize panel_size, int border)
143 : StockPage (Kind_General, panel_size, border)
149 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
150 table->AddGrowableCol (1, 1);
151 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
153 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
154 table->Add (_set_language, 1);
155 _language = new wxChoice (_panel, wxID_ANY);
156 _language->Append (wxT ("Deutsch"));
157 _language->Append (wxT ("English"));
158 _language->Append (wxT ("Español"));
159 _language->Append (wxT ("Français"));
160 _language->Append (wxT ("Italiano"));
161 _language->Append (wxT ("Nederlands"));
162 _language->Append (wxT ("Svenska"));
163 _language->Append (wxT ("русский"));
164 table->Add (_language);
166 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
167 wxFont font = restart->GetFont();
168 font.SetStyle (wxFONTSTYLE_ITALIC);
169 font.SetPointSize (font.GetPointSize() - 1);
170 restart->SetFont (font);
171 table->AddSpacer (0);
173 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
174 _num_local_encoding_threads = new wxSpinCtrl (_panel);
175 table->Add (_num_local_encoding_threads, 1);
177 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
178 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
179 table->AddSpacer (0);
181 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
182 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
183 table->AddSpacer (0);
185 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
186 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
188 _num_local_encoding_threads->SetRange (1, 128);
189 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
191 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
192 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
195 void config_changed ()
197 Config* config = Config::instance ();
199 checked_set (_set_language, config->language ());
201 if (config->language().get_value_or ("") == "fr") {
202 _language->SetSelection (3);
203 } else if (config->language().get_value_or ("") == "it") {
204 _language->SetSelection (4);
205 } else if (config->language().get_value_or ("") == "es") {
206 _language->SetSelection (2);
207 } else if (config->language().get_value_or ("") == "sv") {
208 _language->SetSelection (6);
209 } else if (config->language().get_value_or ("") == "de") {
210 _language->SetSelection (0);
211 } else if (config->language().get_value_or ("") == "nl") {
212 _language->SetSelection (5);
213 } else if (config->language().get_value_or ("") == "ru") {
214 _language->SetSelection (7);
216 _language->SetSelection (1);
219 setup_language_sensitivity ();
221 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
222 checked_set (_check_for_updates, config->check_for_updates ());
223 checked_set (_check_for_test_updates, config->check_for_test_updates ());
226 void setup_language_sensitivity ()
228 _language->Enable (_set_language->GetValue ());
231 void set_language_changed ()
233 setup_language_sensitivity ();
234 if (_set_language->GetValue ()) {
237 Config::instance()->unset_language ();
241 void language_changed ()
243 switch (_language->GetSelection ()) {
245 Config::instance()->set_language ("de");
248 Config::instance()->set_language ("en");
251 Config::instance()->set_language ("es");
254 Config::instance()->set_language ("fr");
257 Config::instance()->set_language ("it");
260 Config::instance()->set_language ("nl");
263 Config::instance()->set_language ("sv");
266 Config::instance()->set_language ("ru");
271 void check_for_updates_changed ()
273 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
276 void check_for_test_updates_changed ()
278 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
281 void num_local_encoding_threads_changed ()
283 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
286 wxCheckBox* _set_language;
288 wxSpinCtrl* _num_local_encoding_threads;
289 wxCheckBox* _check_for_updates;
290 wxCheckBox* _check_for_test_updates;
293 class DefaultsPage : public StandardPage
296 DefaultsPage (wxSize panel_size, int border)
297 : StandardPage (panel_size, border)
300 wxString GetName () const
302 return _("Defaults");
306 wxBitmap GetLargeIcon () const
308 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
315 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
316 table->AddGrowableCol (1, 1);
317 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
320 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
321 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
322 _still_length = new wxSpinCtrl (_panel);
323 s->Add (_still_length);
324 add_label_to_sizer (s, _panel, _("s"), false);
328 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
329 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
330 _directory = new DirPickerCtrl (_panel);
332 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
334 table->Add (_directory, 1, wxEXPAND);
336 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
337 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
338 table->Add (_isdcf_metadata_button);
340 add_label_to_sizer (table, _panel, _("Default container"), true);
341 _container = new wxChoice (_panel, wxID_ANY);
342 table->Add (_container);
344 add_label_to_sizer (table, _panel, _("Default content type"), true);
345 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
346 table->Add (_dcp_content_type);
349 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
350 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
351 _j2k_bandwidth = new wxSpinCtrl (_panel);
352 s->Add (_j2k_bandwidth);
353 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
358 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
359 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
360 _audio_delay = new wxSpinCtrl (_panel);
361 s->Add (_audio_delay);
362 add_label_to_sizer (s, _panel, _("ms"), false);
366 add_label_to_sizer (table, _panel, _("Default issuer"), true);
367 _issuer = new wxTextCtrl (_panel, wxID_ANY);
368 table->Add (_issuer, 1, wxEXPAND);
370 _still_length->SetRange (1, 3600);
371 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
373 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
375 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
377 vector<Ratio const *> ratios = Ratio::all ();
378 for (size_t i = 0; i < ratios.size(); ++i) {
379 _container->Append (std_to_wx (ratios[i]->nickname ()));
382 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
384 vector<DCPContentType const *> const ct = DCPContentType::all ();
385 for (size_t i = 0; i < ct.size(); ++i) {
386 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
389 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
391 _j2k_bandwidth->SetRange (50, 250);
392 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
394 _audio_delay->SetRange (-1000, 1000);
395 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
397 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
400 void config_changed ()
402 Config* config = Config::instance ();
404 vector<Ratio const *> ratios = Ratio::all ();
405 for (size_t i = 0; i < ratios.size(); ++i) {
406 if (ratios[i] == config->default_container ()) {
407 _container->SetSelection (i);
411 vector<DCPContentType const *> const ct = DCPContentType::all ();
412 for (size_t i = 0; i < ct.size(); ++i) {
413 if (ct[i] == config->default_dcp_content_type ()) {
414 _dcp_content_type->SetSelection (i);
418 checked_set (_still_length, config->default_still_length ());
419 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
420 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
421 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
422 checked_set (_audio_delay, config->default_audio_delay ());
423 checked_set (_issuer, config->dcp_issuer ());
426 void j2k_bandwidth_changed ()
428 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
431 void audio_delay_changed ()
433 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
436 void directory_changed ()
438 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
441 void edit_isdcf_metadata_clicked ()
443 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata ());
445 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
449 void still_length_changed ()
451 Config::instance()->set_default_still_length (_still_length->GetValue ());
454 void container_changed ()
456 vector<Ratio const *> ratio = Ratio::all ();
457 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
460 void dcp_content_type_changed ()
462 vector<DCPContentType const *> ct = DCPContentType::all ();
463 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
466 void issuer_changed ()
468 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
471 wxSpinCtrl* _j2k_bandwidth;
472 wxSpinCtrl* _audio_delay;
473 wxButton* _isdcf_metadata_button;
474 wxSpinCtrl* _still_length;
475 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
476 DirPickerCtrl* _directory;
478 wxDirPickerCtrl* _directory;
480 wxChoice* _container;
481 wxChoice* _dcp_content_type;
485 class EncodingServersPage : public StandardPage
488 EncodingServersPage (wxSize panel_size, int border)
489 : StandardPage (panel_size, border)
492 wxString GetName () const
498 wxBitmap GetLargeIcon () const
500 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
507 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
508 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
510 vector<string> columns;
511 columns.push_back (wx_to_std (_("IP address / host name")));
512 _servers_list = new EditableList<string, ServerDialog> (
515 boost::bind (&Config::servers, Config::instance()),
516 boost::bind (&Config::set_servers, Config::instance(), _1),
517 boost::bind (&EncodingServersPage::server_column, this, _1)
520 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
522 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
525 void config_changed ()
527 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
528 _servers_list->refresh ();
531 void use_any_servers_changed ()
533 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
536 string server_column (string s)
541 wxCheckBox* _use_any_servers;
542 EditableList<string, ServerDialog>* _servers_list;
545 class KeysPage : public StandardPage
548 KeysPage (wxSize panel_size, int border)
549 : StandardPage (panel_size, border)
552 wxString GetName () const
558 wxBitmap GetLargeIcon () const
560 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
567 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
568 _panel->GetSizer()->Add (m, 0, wxALL, _border);
570 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
571 _panel->GetSizer()->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
573 _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
578 ip.SetText (_("Type"));
580 _certificates->InsertColumn (0, ip);
586 ip.SetText (_("Thumbprint"));
589 wxFont font = ip.GetFont ();
590 font.SetFamily (wxFONTFAMILY_TELETYPE);
593 _certificates->InsertColumn (1, ip);
596 certificates_sizer->Add (_certificates, 1, wxEXPAND);
599 wxSizer* s = new wxBoxSizer (wxVERTICAL);
600 _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
601 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
602 _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
603 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
604 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
607 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
608 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
612 _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
613 table->Add (_remake_certificates, wxGBPosition (r, 0), wxGBSpan (1, 3));
616 add_label_to_grid_bag_sizer (table, _panel, _("Private key for leaf certificate"), true, wxGBPosition (r, 0));
617 _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
618 wxFont font = _signer_private_key->GetFont ();
619 font.SetFamily (wxFONTFAMILY_TELETYPE);
620 _signer_private_key->SetFont (font);
621 table->Add (_signer_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
622 _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
623 table->Add (_load_signer_private_key, wxGBPosition (r, 2));
626 add_label_to_grid_bag_sizer (table, _panel, _("Certificate for decrypting DCPs"), true, wxGBPosition (r, 0));
627 _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
628 _decryption_certificate->SetFont (font);
629 table->Add (_decryption_certificate, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
630 _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
631 table->Add (_load_decryption_certificate, wxGBPosition (r, 2));
634 add_label_to_grid_bag_sizer (table, _panel, _("Private key for decrypting DCPs"), true, wxGBPosition (r, 0));
635 _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
636 _decryption_private_key->SetFont (font);
637 table->Add (_decryption_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
638 _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
639 table->Add (_load_decryption_private_key, wxGBPosition (r, 2));
642 _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
643 table->Add (_export_decryption_certificate, wxGBPosition (r, 0), wxGBSpan (1, 3));
646 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
647 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
648 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
649 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
650 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
651 _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
652 _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
653 _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
654 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
657 void config_changed ()
659 _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
661 update_certificate_list ();
662 update_signer_private_key ();
663 update_decryption_certificate ();
664 update_decryption_private_key ();
665 update_sensitivity ();
668 void add_certificate ()
670 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
672 if (d->ShowModal() == wxID_OK) {
674 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
675 _signer->certificates().add (c);
676 Config::instance()->set_signer (_signer);
677 update_certificate_list ();
678 } catch (dcp::MiscError& e) {
679 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
685 update_sensitivity ();
688 void remove_certificate ()
690 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
695 _certificates->DeleteItem (i);
696 _signer->certificates().remove (i);
697 Config::instance()->set_signer (_signer);
699 update_sensitivity ();
702 void update_certificate_list ()
704 _certificates->DeleteAllItems ();
705 dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
707 for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
710 _certificates->InsertItem (item);
711 _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
714 _certificates->SetItem (n, 0, _("Root"));
715 } else if (n == (certs.size() - 1)) {
716 _certificates->SetItem (n, 0, _("Leaf"));
718 _certificates->SetItem (n, 0, _("Intermediate"));
725 void remake_certificates ()
727 MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
728 if (d->ShowModal () == wxID_OK) {
733 d->organisational_unit (),
734 d->root_common_name (),
735 d->intermediate_common_name (),
736 d->leaf_common_name ()
740 Config::instance()->set_signer (_signer);
741 update_certificate_list ();
742 update_signer_private_key ();
748 void update_sensitivity ()
750 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
753 void update_signer_private_key ()
755 checked_set (_signer_private_key, dcp::private_key_fingerprint (_signer->key ()));
758 void load_signer_private_key ()
760 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
762 if (d->ShowModal() == wxID_OK) {
764 boost::filesystem::path p (wx_to_std (d->GetPath ()));
765 if (boost::filesystem::file_size (p) > 1024) {
766 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
770 _signer->set_key (dcp::file_to_string (p));
771 Config::instance()->set_signer (_signer);
772 update_signer_private_key ();
773 } catch (dcp::MiscError& e) {
774 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
780 update_sensitivity ();
784 void load_decryption_certificate ()
786 wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
788 if (d->ShowModal() == wxID_OK) {
790 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
791 Config::instance()->set_decryption_certificate (c);
792 update_decryption_certificate ();
793 } catch (dcp::MiscError& e) {
794 error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
801 void update_decryption_certificate ()
803 checked_set (_decryption_certificate, Config::instance()->decryption_certificate().thumbprint ());
806 void load_decryption_private_key ()
808 wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
810 if (d->ShowModal() == wxID_OK) {
812 boost::filesystem::path p (wx_to_std (d->GetPath ()));
813 Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
814 update_decryption_private_key ();
815 } catch (dcp::MiscError& e) {
816 error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
823 void update_decryption_private_key ()
825 checked_set (_decryption_private_key, dcp::private_key_fingerprint (Config::instance()->decryption_private_key()));
828 void export_decryption_certificate ()
830 wxFileDialog* d = new wxFileDialog (
831 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
832 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
835 if (d->ShowModal () == wxID_OK) {
836 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
838 throw OpenFileError (wx_to_std (d->GetPath ()));
841 string const s = Config::instance()->decryption_certificate().certificate (true);
842 fwrite (s.c_str(), 1, s.length(), f);
848 wxListCtrl* _certificates;
849 wxButton* _add_certificate;
850 wxButton* _remove_certificate;
851 wxButton* _remake_certificates;
852 wxStaticText* _signer_private_key;
853 wxButton* _load_signer_private_key;
854 wxStaticText* _decryption_certificate;
855 wxButton* _load_decryption_certificate;
856 wxStaticText* _decryption_private_key;
857 wxButton* _load_decryption_private_key;
858 wxButton* _export_decryption_certificate;
859 shared_ptr<dcp::Signer> _signer;
862 class TMSPage : public StandardPage
865 TMSPage (wxSize panel_size, int border)
866 : StandardPage (panel_size, border)
869 wxString GetName () const
875 wxBitmap GetLargeIcon () const
877 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
884 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
885 table->AddGrowableCol (1, 1);
886 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
888 add_label_to_sizer (table, _panel, _("IP address"), true);
889 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
890 table->Add (_tms_ip, 1, wxEXPAND);
892 add_label_to_sizer (table, _panel, _("Target path"), true);
893 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
894 table->Add (_tms_path, 1, wxEXPAND);
896 add_label_to_sizer (table, _panel, _("User name"), true);
897 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
898 table->Add (_tms_user, 1, wxEXPAND);
900 add_label_to_sizer (table, _panel, _("Password"), true);
901 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
902 table->Add (_tms_password, 1, wxEXPAND);
904 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
905 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
906 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
907 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
910 void config_changed ()
912 Config* config = Config::instance ();
914 checked_set (_tms_ip, config->tms_ip ());
915 checked_set (_tms_path, config->tms_path ());
916 checked_set (_tms_user, config->tms_user ());
917 checked_set (_tms_password, config->tms_password ());
920 void tms_ip_changed ()
922 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
925 void tms_path_changed ()
927 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
930 void tms_user_changed ()
932 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
935 void tms_password_changed ()
937 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
941 wxTextCtrl* _tms_path;
942 wxTextCtrl* _tms_user;
943 wxTextCtrl* _tms_password;
946 class KDMEmailPage : public StandardPage
950 KDMEmailPage (wxSize panel_size, int border)
952 /* We have to force both width and height of this one */
953 : StandardPage (wxSize (480, 128), border)
955 : StandardPage (panel_size, border)
959 wxString GetName () const
961 return _("KDM Email");
965 wxBitmap GetLargeIcon () const
967 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
974 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
975 table->AddGrowableCol (1, 1);
976 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
978 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
979 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
980 table->Add (_mail_server, 1, wxEXPAND | wxALL);
982 add_label_to_sizer (table, _panel, _("Mail user name"), true);
983 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
984 table->Add (_mail_user, 1, wxEXPAND | wxALL);
986 add_label_to_sizer (table, _panel, _("Mail password"), true);
987 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
988 table->Add (_mail_password, 1, wxEXPAND | wxALL);
990 wxStaticText* plain = add_label_to_sizer (table, _panel, _("(password will be stored on disk in plaintext)"), false);
991 wxFont font = plain->GetFont();
992 font.SetStyle (wxFONTSTYLE_ITALIC);
993 font.SetPointSize (font.GetPointSize() - 1);
994 plain->SetFont (font);
995 table->AddSpacer (0);
997 add_label_to_sizer (table, _panel, _("Subject"), true);
998 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
999 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1001 add_label_to_sizer (table, _panel, _("From address"), true);
1002 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1003 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1005 add_label_to_sizer (table, _panel, _("CC address"), true);
1006 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1007 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1009 add_label_to_sizer (table, _panel, _("BCC address"), true);
1010 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1011 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1013 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1014 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1016 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1017 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1019 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1020 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1021 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1022 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1023 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1024 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1025 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1026 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1027 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1030 void config_changed ()
1032 Config* config = Config::instance ();
1034 checked_set (_mail_server, config->mail_server ());
1035 checked_set (_mail_user, config->mail_user ());
1036 checked_set (_mail_password, config->mail_password ());
1037 checked_set (_kdm_subject, config->kdm_subject ());
1038 checked_set (_kdm_from, config->kdm_from ());
1039 checked_set (_kdm_cc, config->kdm_cc ());
1040 checked_set (_kdm_bcc, config->kdm_bcc ());
1041 checked_set (_kdm_email, Config::instance()->kdm_email ());
1044 void mail_server_changed ()
1046 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1049 void mail_user_changed ()
1051 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1054 void mail_password_changed ()
1056 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1059 void kdm_subject_changed ()
1061 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1064 void kdm_from_changed ()
1066 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1069 void kdm_cc_changed ()
1071 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1074 void kdm_bcc_changed ()
1076 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1079 void kdm_email_changed ()
1081 if (_kdm_email->GetValue().IsEmpty ()) {
1082 /* Sometimes we get sent an erroneous notification that the email
1083 is empty; I don't know why.
1087 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1090 void reset_kdm_email ()
1092 Config::instance()->reset_kdm_email ();
1093 checked_set (_kdm_email, Config::instance()->kdm_email ());
1096 wxTextCtrl* _mail_server;
1097 wxTextCtrl* _mail_user;
1098 wxTextCtrl* _mail_password;
1099 wxTextCtrl* _kdm_subject;
1100 wxTextCtrl* _kdm_from;
1101 wxTextCtrl* _kdm_cc;
1102 wxTextCtrl* _kdm_bcc;
1103 wxTextCtrl* _kdm_email;
1104 wxButton* _reset_kdm_email;
1107 /** @class AdvancedPage
1108 * @brief Advanced page of the preferences dialog.
1110 class AdvancedPage : public StockPage
1113 AdvancedPage (wxSize panel_size, int border)
1114 : StockPage (Kind_Advanced, panel_size, border)
1115 , _maximum_j2k_bandwidth (0)
1116 , _allow_any_dcp_frame_rate (0)
1126 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1127 table->AddGrowableCol (1, 1);
1128 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1131 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1132 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1133 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1134 s->Add (_maximum_j2k_bandwidth, 1);
1135 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1139 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1140 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1141 table->AddSpacer (0);
1144 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1145 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1147 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1148 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1152 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1153 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1154 t->Add (_log_general, 1, wxEXPAND | wxALL);
1155 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1156 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1157 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1158 t->Add (_log_error, 1, wxEXPAND | wxALL);
1159 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1160 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1161 table->Add (t, 0, wxALL, 6);
1164 #ifdef DCPOMATIC_WINDOWS
1165 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1166 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1167 table->AddSpacer (0);
1170 _maximum_j2k_bandwidth->SetRange (1, 1000);
1171 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1172 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1173 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1174 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1175 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1176 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1177 #ifdef DCPOMATIC_WINDOWS
1178 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1182 void config_changed ()
1184 Config* config = Config::instance ();
1186 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1187 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1188 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1189 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1190 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1191 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1192 #ifdef DCPOMATIC_WINDOWS
1193 checked_set (_win32_console, config->win32_console());
1197 void maximum_j2k_bandwidth_changed ()
1199 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1202 void allow_any_dcp_frame_rate_changed ()
1204 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1210 if (_log_general->GetValue ()) {
1211 types |= Log::TYPE_GENERAL;
1213 if (_log_warning->GetValue ()) {
1214 types |= Log::TYPE_WARNING;
1216 if (_log_error->GetValue ()) {
1217 types |= Log::TYPE_ERROR;
1219 if (_log_timing->GetValue ()) {
1220 types |= Log::TYPE_TIMING;
1222 Config::instance()->set_log_types (types);
1225 #ifdef DCPOMATIC_WINDOWS
1226 void win32_console_changed ()
1228 Config::instance()->set_win32_console (_win32_console->GetValue ());
1232 wxSpinCtrl* _maximum_j2k_bandwidth;
1233 wxCheckBox* _allow_any_dcp_frame_rate;
1234 wxCheckBox* _log_general;
1235 wxCheckBox* _log_warning;
1236 wxCheckBox* _log_error;
1237 wxCheckBox* _log_timing;
1238 #ifdef DCPOMATIC_WINDOWS
1239 wxCheckBox* _win32_console;
1243 wxPreferencesEditor*
1244 create_config_dialog ()
1246 wxPreferencesEditor* e = new wxPreferencesEditor ();
1248 #ifdef DCPOMATIC_OSX
1249 /* Width that we force some of the config panels to be on OSX so that
1250 the containing window doesn't shrink too much when we select those panels.
1251 This is obviously an unpleasant hack.
1253 wxSize ps = wxSize (520, -1);
1254 int const border = 16;
1256 wxSize ps = wxSize (-1, -1);
1257 int const border = 8;
1260 e->AddPage (new GeneralPage (ps, border));
1261 e->AddPage (new DefaultsPage (ps, border));
1262 e->AddPage (new EncodingServersPage (ps, border));
1263 e->AddPage (new KeysPage (ps, border));
1264 e->AddPage (new TMSPage (ps, border));
1265 e->AddPage (new KDMEmailPage (ps, border));
1266 e->AddPage (new AdvancedPage (ps, border));