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.
24 #include "config_dialog.h"
26 #include "editable_list.h"
27 #include "filter_dialog.h"
28 #include "dir_picker_ctrl.h"
29 #include "isdcf_metadata_dialog.h"
30 #include "server_dialog.h"
31 #include "make_chain_dialog.h"
32 #include "lib/config.h"
33 #include "lib/ratio.h"
34 #include "lib/filter.h"
35 #include "lib/dcp_content_type.h"
38 #include "lib/raw_convert.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include <dcp/exceptions.h>
42 #include <dcp/certificate_chain.h>
43 #include <wx/stdpaths.h>
44 #include <wx/preferences.h>
45 #include <wx/filepicker.h>
46 #include <wx/spinctrl.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/filesystem.hpp>
49 #include <boost/foreach.hpp>
57 using boost::shared_ptr;
58 using boost::lexical_cast;
59 using boost::function;
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 _language->Append (wxT ("Русский"));
167 _language->Append (wxT ("Polski"));
168 _language->Append (wxT ("Danske"));
169 table->Add (_language);
171 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see language changes)"), false);
172 wxFont font = restart->GetFont();
173 font.SetStyle (wxFONTSTYLE_ITALIC);
174 font.SetPointSize (font.GetPointSize() - 1);
175 restart->SetFont (font);
176 table->AddSpacer (0);
178 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true);
179 _num_local_encoding_threads = new wxSpinCtrl (_panel);
180 table->Add (_num_local_encoding_threads, 1);
182 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
183 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
184 table->AddSpacer (0);
186 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
187 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
188 table->AddSpacer (0);
190 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
191 _language->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&GeneralPage::language_changed, this));
193 _num_local_encoding_threads->SetRange (1, 128);
194 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
196 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
197 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
200 void config_changed ()
202 Config* config = Config::instance ();
204 checked_set (_set_language, config->language ());
206 if (config->language().get_value_or ("") == "fr") {
207 checked_set (_language, 3);
208 } else if (config->language().get_value_or ("") == "it") {
209 checked_set (_language, 4);
210 } else if (config->language().get_value_or ("") == "es") {
211 checked_set (_language, 2);
212 } else if (config->language().get_value_or ("") == "sv") {
213 checked_set (_language, 6);
214 } else if (config->language().get_value_or ("") == "de") {
215 checked_set (_language, 0);
216 } else if (config->language().get_value_or ("") == "nl") {
217 checked_set (_language, 5);
218 } else if (config->language().get_value_or ("") == "ru") {
219 checked_set (_language, 7);
220 } else if (config->language().get_value_or ("") == "pl") {
221 checked_set (_language, 8);
222 } else if (config->language().get_value_or ("") == "da") {
223 checked_set (_language, 9);
225 _language->SetSelection (1);
228 setup_language_sensitivity ();
230 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
231 checked_set (_check_for_updates, config->check_for_updates ());
232 checked_set (_check_for_test_updates, config->check_for_test_updates ());
235 void setup_language_sensitivity ()
237 _language->Enable (_set_language->GetValue ());
240 void set_language_changed ()
242 setup_language_sensitivity ();
243 if (_set_language->GetValue ()) {
246 Config::instance()->unset_language ();
250 void language_changed ()
252 switch (_language->GetSelection ()) {
254 Config::instance()->set_language ("de");
257 Config::instance()->set_language ("en");
260 Config::instance()->set_language ("es");
263 Config::instance()->set_language ("fr");
266 Config::instance()->set_language ("it");
269 Config::instance()->set_language ("nl");
272 Config::instance()->set_language ("sv");
275 Config::instance()->set_language ("ru");
278 Config::instance()->set_language ("pl");
281 Config::instance()->set_language ("da");
286 void check_for_updates_changed ()
288 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
291 void check_for_test_updates_changed ()
293 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
296 void num_local_encoding_threads_changed ()
298 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
301 wxCheckBox* _set_language;
303 wxSpinCtrl* _num_local_encoding_threads;
304 wxCheckBox* _check_for_updates;
305 wxCheckBox* _check_for_test_updates;
308 class DefaultsPage : public StandardPage
311 DefaultsPage (wxSize panel_size, int border)
312 : StandardPage (panel_size, border)
315 wxString GetName () const
317 return _("Defaults");
321 wxBitmap GetLargeIcon () const
323 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
330 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
331 table->AddGrowableCol (1, 1);
332 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
335 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
336 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
337 _still_length = new wxSpinCtrl (_panel);
338 s->Add (_still_length);
339 add_label_to_sizer (s, _panel, _("s"), false);
343 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
344 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
345 _directory = new DirPickerCtrl (_panel);
347 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
349 table->Add (_directory, 1, wxEXPAND);
351 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
352 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
353 table->Add (_isdcf_metadata_button);
355 add_label_to_sizer (table, _panel, _("Default container"), true);
356 _container = new wxChoice (_panel, wxID_ANY);
357 table->Add (_container);
359 add_label_to_sizer (table, _panel, _("Default content type"), true);
360 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
361 table->Add (_dcp_content_type);
364 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
365 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
366 _j2k_bandwidth = new wxSpinCtrl (_panel);
367 s->Add (_j2k_bandwidth);
368 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
373 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
374 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
375 _audio_delay = new wxSpinCtrl (_panel);
376 s->Add (_audio_delay);
377 add_label_to_sizer (s, _panel, _("ms"), false);
381 add_label_to_sizer (table, _panel, _("Default issuer"), true);
382 _issuer = new wxTextCtrl (_panel, wxID_ANY);
383 table->Add (_issuer, 1, wxEXPAND);
385 add_label_to_sizer (table, _panel, _("Default creator"), true);
386 _creator = new wxTextCtrl (_panel, wxID_ANY);
387 table->Add (_creator, 1, wxEXPAND);
389 _still_length->SetRange (1, 3600);
390 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
392 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
394 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
396 vector<Ratio const *> ratios = Ratio::all ();
397 for (size_t i = 0; i < ratios.size(); ++i) {
398 _container->Append (std_to_wx (ratios[i]->nickname ()));
401 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
403 vector<DCPContentType const *> const ct = DCPContentType::all ();
404 for (size_t i = 0; i < ct.size(); ++i) {
405 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
408 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
410 _j2k_bandwidth->SetRange (50, 250);
411 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
413 _audio_delay->SetRange (-1000, 1000);
414 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
416 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
417 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::creator_changed, this));
420 void config_changed ()
422 Config* config = Config::instance ();
424 vector<Ratio const *> ratios = Ratio::all ();
425 for (size_t i = 0; i < ratios.size(); ++i) {
426 if (ratios[i] == config->default_container ()) {
427 _container->SetSelection (i);
431 vector<DCPContentType const *> const ct = DCPContentType::all ();
432 for (size_t i = 0; i < ct.size(); ++i) {
433 if (ct[i] == config->default_dcp_content_type ()) {
434 _dcp_content_type->SetSelection (i);
438 checked_set (_still_length, config->default_still_length ());
439 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
440 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
441 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
442 checked_set (_audio_delay, config->default_audio_delay ());
443 checked_set (_issuer, config->dcp_issuer ());
444 checked_set (_creator, config->dcp_creator ());
447 void j2k_bandwidth_changed ()
449 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
452 void audio_delay_changed ()
454 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
457 void directory_changed ()
459 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
462 void edit_isdcf_metadata_clicked ()
464 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
466 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
470 void still_length_changed ()
472 Config::instance()->set_default_still_length (_still_length->GetValue ());
475 void container_changed ()
477 vector<Ratio const *> ratio = Ratio::all ();
478 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
481 void dcp_content_type_changed ()
483 vector<DCPContentType const *> ct = DCPContentType::all ();
484 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
487 void issuer_changed ()
489 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
492 void creator_changed ()
494 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
497 wxSpinCtrl* _j2k_bandwidth;
498 wxSpinCtrl* _audio_delay;
499 wxButton* _isdcf_metadata_button;
500 wxSpinCtrl* _still_length;
501 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
502 DirPickerCtrl* _directory;
504 wxDirPickerCtrl* _directory;
506 wxChoice* _container;
507 wxChoice* _dcp_content_type;
509 wxTextCtrl* _creator;
512 class EncodingServersPage : public StandardPage
515 EncodingServersPage (wxSize panel_size, int border)
516 : StandardPage (panel_size, border)
519 wxString GetName () const
525 wxBitmap GetLargeIcon () const
527 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
534 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
535 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
537 vector<string> columns;
538 columns.push_back (wx_to_std (_("IP address / host name")));
539 _servers_list = new EditableList<string, ServerDialog> (
542 boost::bind (&Config::servers, Config::instance()),
543 boost::bind (&Config::set_servers, Config::instance(), _1),
544 boost::bind (&EncodingServersPage::server_column, this, _1)
547 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
549 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
552 void config_changed ()
554 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
555 _servers_list->refresh ();
558 void use_any_servers_changed ()
560 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
563 string server_column (string s)
568 wxCheckBox* _use_any_servers;
569 EditableList<string, ServerDialog>* _servers_list;
572 class CertificateChainEditor : public wxPanel
575 CertificateChainEditor (
579 function<void (shared_ptr<dcp::CertificateChain>)> set,
580 function<shared_ptr<const dcp::CertificateChain> (void)> get
586 wxFont subheading_font (*wxNORMAL_FONT);
587 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
589 _sizer = new wxBoxSizer (wxVERTICAL);
592 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
593 m->SetFont (subheading_font);
594 _sizer->Add (m, 0, wxALL, border);
597 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
598 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
600 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
605 ip.SetText (_("Type"));
607 _certificates->InsertColumn (0, ip);
613 ip.SetText (_("Thumbprint"));
616 wxFont font = ip.GetFont ();
617 font.SetFamily (wxFONTFAMILY_TELETYPE);
620 _certificates->InsertColumn (1, ip);
623 certificates_sizer->Add (_certificates, 1, wxEXPAND);
626 wxSizer* s = new wxBoxSizer (wxVERTICAL);
627 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
628 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
629 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
630 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
631 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
634 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
635 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
638 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
639 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
640 wxFont font = _private_key->GetFont ();
641 font.SetFamily (wxFONTFAMILY_TELETYPE);
642 _private_key->SetFont (font);
643 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
644 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
645 table->Add (_load_private_key, wxGBPosition (r, 2));
648 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
649 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
650 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
651 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
654 _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::add_certificate, this));
655 _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remove_certificate, this));
656 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
657 _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
658 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::remake_certificates, this));
659 _load_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&CertificateChainEditor::load_private_key, this));
661 SetSizerAndFit (_sizer);
664 void config_changed ()
666 _chain.reset (new dcp::CertificateChain (*_get().get ()));
668 update_certificate_list ();
669 update_private_key ();
670 update_sensitivity ();
673 void add_button (wxWindow* button)
675 _button_sizer->Add (button);
680 void add_certificate ()
682 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
684 if (d->ShowModal() == wxID_OK) {
686 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
689 update_certificate_list ();
690 } catch (dcp::MiscError& e) {
691 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
697 update_sensitivity ();
700 void remove_certificate ()
702 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
707 _certificates->DeleteItem (i);
711 update_sensitivity ();
714 void update_certificate_list ()
716 _certificates->DeleteAllItems ();
718 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
719 BOOST_FOREACH (dcp::Certificate const & i, certs) {
722 _certificates->InsertItem (item);
723 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
726 _certificates->SetItem (n, 0, _("Root"));
727 } else if (n == (certs.size() - 1)) {
728 _certificates->SetItem (n, 0, _("Leaf"));
730 _certificates->SetItem (n, 0, _("Intermediate"));
737 void remake_certificates ()
739 shared_ptr<const dcp::CertificateChain> chain = _get ();
741 string intermediate_common_name;
742 if (chain->root_to_leaf().size() >= 3) {
743 dcp::CertificateChain::List all = chain->root_to_leaf ();
744 dcp::CertificateChain::List::iterator i = all.begin ();
746 intermediate_common_name = i->subject_common_name ();
749 MakeChainDialog* d = new MakeChainDialog (
751 chain->root().subject_organization_name (),
752 chain->root().subject_organizational_unit_name (),
753 chain->root().subject_common_name (),
754 intermediate_common_name,
755 chain->leaf().subject_common_name ()
758 if (d->ShowModal () == wxID_OK) {
760 new dcp::CertificateChain (
763 d->organisational_unit (),
764 d->root_common_name (),
765 d->intermediate_common_name (),
766 d->leaf_common_name ()
771 update_certificate_list ();
772 update_private_key ();
778 void update_sensitivity ()
780 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
783 void update_private_key ()
785 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
789 void load_private_key ()
791 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
793 if (d->ShowModal() == wxID_OK) {
795 boost::filesystem::path p (wx_to_std (d->GetPath ()));
796 if (boost::filesystem::file_size (p) > 1024) {
797 error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
801 _chain->set_key (dcp::file_to_string (p));
803 update_private_key ();
804 } catch (dcp::MiscError& e) {
805 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
811 update_sensitivity ();
814 wxListCtrl* _certificates;
815 wxButton* _add_certificate;
816 wxButton* _remove_certificate;
817 wxButton* _remake_certificates;
818 wxStaticText* _private_key;
819 wxButton* _load_private_key;
821 wxBoxSizer* _button_sizer;
822 shared_ptr<dcp::CertificateChain> _chain;
823 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
824 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
827 class KeysPage : public StandardPage
830 KeysPage (wxSize panel_size, int border)
831 : StandardPage (panel_size, border)
834 wxString GetName () const
840 wxBitmap GetLargeIcon () const
842 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
850 _signer = new CertificateChainEditor (
851 _panel, _("Signing DCPs and KDMs"), _border,
852 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
853 boost::bind (&Config::signer_chain, Config::instance ())
856 _panel->GetSizer()->Add (_signer);
858 _decryption = new CertificateChainEditor (
859 _panel, _("Decrypting DCPs"), _border,
860 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
861 boost::bind (&Config::decryption_chain, Config::instance ())
864 _panel->GetSizer()->Add (_decryption);
866 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
867 _decryption->add_button (_export_decryption_certificate);
869 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
872 void export_decryption_certificate ()
874 wxFileDialog* d = new wxFileDialog (
875 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
876 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
879 if (d->ShowModal () == wxID_OK) {
880 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
882 throw OpenFileError (wx_to_std (d->GetPath ()));
885 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
886 fwrite (s.c_str(), 1, s.length(), f);
892 void config_changed ()
894 _signer->config_changed ();
895 _decryption->config_changed ();
898 CertificateChainEditor* _signer;
899 CertificateChainEditor* _decryption;
900 wxButton* _export_decryption_certificate;
903 class TMSPage : public StandardPage
906 TMSPage (wxSize panel_size, int border)
907 : StandardPage (panel_size, border)
910 wxString GetName () const
916 wxBitmap GetLargeIcon () const
918 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
925 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
926 table->AddGrowableCol (1, 1);
927 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
929 add_label_to_sizer (table, _panel, _("Protocol"), true);
930 _tms_protocol = new wxChoice (_panel, wxID_ANY);
931 table->Add (_tms_protocol, 1, wxEXPAND);
933 add_label_to_sizer (table, _panel, _("IP address"), true);
934 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
935 table->Add (_tms_ip, 1, wxEXPAND);
937 add_label_to_sizer (table, _panel, _("Target path"), true);
938 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
939 table->Add (_tms_path, 1, wxEXPAND);
941 add_label_to_sizer (table, _panel, _("User name"), true);
942 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
943 table->Add (_tms_user, 1, wxEXPAND);
945 add_label_to_sizer (table, _panel, _("Password"), true);
946 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
947 table->Add (_tms_password, 1, wxEXPAND);
949 _tms_protocol->Append (_("SCP (for AAM)"));
950 _tms_protocol->Append (_("FTP (for Dolby)"));
952 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
953 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
954 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
955 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
956 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
959 void config_changed ()
961 Config* config = Config::instance ();
963 checked_set (_tms_protocol, config->tms_protocol ());
964 checked_set (_tms_ip, config->tms_ip ());
965 checked_set (_tms_path, config->tms_path ());
966 checked_set (_tms_user, config->tms_user ());
967 checked_set (_tms_password, config->tms_password ());
970 void tms_protocol_changed ()
972 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
975 void tms_ip_changed ()
977 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
980 void tms_path_changed ()
982 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
985 void tms_user_changed ()
987 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
990 void tms_password_changed ()
992 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
995 wxChoice* _tms_protocol;
997 wxTextCtrl* _tms_path;
998 wxTextCtrl* _tms_user;
999 wxTextCtrl* _tms_password;
1002 class KDMEmailPage : public StandardPage
1006 KDMEmailPage (wxSize panel_size, int border)
1007 #ifdef DCPOMATIC_OSX
1008 /* We have to force both width and height of this one */
1009 : StandardPage (wxSize (480, 128), border)
1011 : StandardPage (panel_size, border)
1015 wxString GetName () const
1017 return _("KDM Email");
1020 #ifdef DCPOMATIC_OSX
1021 wxBitmap GetLargeIcon () const
1023 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1030 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1031 table->AddGrowableCol (1, 1);
1032 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1034 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1036 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1037 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1038 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1039 add_label_to_sizer (s, _panel, _("port"), false);
1040 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1041 _mail_port->SetRange (0, 65535);
1042 s->Add (_mail_port);
1043 table->Add (s, 1, wxEXPAND | wxALL);
1046 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1047 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1048 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1050 add_label_to_sizer (table, _panel, _("Mail password"), true);
1051 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1052 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1054 add_label_to_sizer (table, _panel, _("Subject"), true);
1055 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1056 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1058 add_label_to_sizer (table, _panel, _("From address"), true);
1059 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1060 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1062 add_label_to_sizer (table, _panel, _("CC address"), true);
1063 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1064 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1066 add_label_to_sizer (table, _panel, _("BCC address"), true);
1067 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1068 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1070 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1071 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1073 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1074 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1076 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1077 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_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 checked_set (_mail_server, config->mail_server ());
1093 checked_set (_mail_port, config->mail_port ());
1094 checked_set (_mail_user, config->mail_user ());
1095 checked_set (_mail_password, config->mail_password ());
1096 checked_set (_kdm_subject, config->kdm_subject ());
1097 checked_set (_kdm_from, config->kdm_from ());
1098 checked_set (_kdm_cc, config->kdm_cc ());
1099 checked_set (_kdm_bcc, config->kdm_bcc ());
1100 checked_set (_kdm_email, Config::instance()->kdm_email ());
1103 void mail_server_changed ()
1105 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1108 void mail_port_changed ()
1110 Config::instance()->set_mail_port (_mail_port->GetValue ());
1113 void mail_user_changed ()
1115 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1118 void mail_password_changed ()
1120 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1123 void kdm_subject_changed ()
1125 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1128 void kdm_from_changed ()
1130 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1133 void kdm_cc_changed ()
1135 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1138 void kdm_bcc_changed ()
1140 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1143 void kdm_email_changed ()
1145 if (_kdm_email->GetValue().IsEmpty ()) {
1146 /* Sometimes we get sent an erroneous notification that the email
1147 is empty; I don't know why.
1151 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1154 void reset_kdm_email ()
1156 Config::instance()->reset_kdm_email ();
1157 checked_set (_kdm_email, Config::instance()->kdm_email ());
1160 wxTextCtrl* _mail_server;
1161 wxSpinCtrl* _mail_port;
1162 wxTextCtrl* _mail_user;
1163 wxTextCtrl* _mail_password;
1164 wxTextCtrl* _kdm_subject;
1165 wxTextCtrl* _kdm_from;
1166 wxTextCtrl* _kdm_cc;
1167 wxTextCtrl* _kdm_bcc;
1168 wxTextCtrl* _kdm_email;
1169 wxButton* _reset_kdm_email;
1172 /** @class AdvancedPage
1173 * @brief Advanced page of the preferences dialog.
1175 class AdvancedPage : public StockPage
1178 AdvancedPage (wxSize panel_size, int border)
1179 : StockPage (Kind_Advanced, panel_size, border)
1180 , _maximum_j2k_bandwidth (0)
1181 , _allow_any_dcp_frame_rate (0)
1182 , _only_servers_encode (0)
1187 , _log_debug_decode (0)
1188 , _log_debug_encode (0)
1194 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1195 table->AddGrowableCol (1, 1);
1196 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1199 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1200 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1201 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1202 s->Add (_maximum_j2k_bandwidth, 1);
1203 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1207 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1208 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1209 table->AddSpacer (0);
1211 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1212 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1213 table->AddSpacer (0);
1216 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1217 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1219 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1220 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1224 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1225 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1226 t->Add (_log_general, 1, wxEXPAND | wxALL);
1227 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1228 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1229 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1230 t->Add (_log_error, 1, wxEXPAND | wxALL);
1231 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1232 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1233 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1234 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1235 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1236 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1237 table->Add (t, 0, wxALL, 6);
1240 #ifdef DCPOMATIC_WINDOWS
1241 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1242 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1243 table->AddSpacer (0);
1246 _maximum_j2k_bandwidth->SetRange (1, 1000);
1247 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1248 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1249 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1250 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1251 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1252 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1253 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1254 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1255 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1256 #ifdef DCPOMATIC_WINDOWS
1257 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1261 void config_changed ()
1263 Config* config = Config::instance ();
1265 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1266 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1267 checked_set (_only_servers_encode, config->only_servers_encode ());
1268 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1269 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1270 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1271 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1272 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1273 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1274 #ifdef DCPOMATIC_WINDOWS
1275 checked_set (_win32_console, config->win32_console());
1279 void maximum_j2k_bandwidth_changed ()
1281 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1284 void allow_any_dcp_frame_rate_changed ()
1286 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1289 void only_servers_encode_changed ()
1291 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1297 if (_log_general->GetValue ()) {
1298 types |= Log::TYPE_GENERAL;
1300 if (_log_warning->GetValue ()) {
1301 types |= Log::TYPE_WARNING;
1303 if (_log_error->GetValue ()) {
1304 types |= Log::TYPE_ERROR;
1306 if (_log_timing->GetValue ()) {
1307 types |= Log::TYPE_TIMING;
1309 if (_log_debug_decode->GetValue ()) {
1310 types |= Log::TYPE_DEBUG_DECODE;
1312 if (_log_debug_encode->GetValue ()) {
1313 types |= Log::TYPE_DEBUG_ENCODE;
1315 Config::instance()->set_log_types (types);
1318 #ifdef DCPOMATIC_WINDOWS
1319 void win32_console_changed ()
1321 Config::instance()->set_win32_console (_win32_console->GetValue ());
1325 wxSpinCtrl* _maximum_j2k_bandwidth;
1326 wxCheckBox* _allow_any_dcp_frame_rate;
1327 wxCheckBox* _only_servers_encode;
1328 wxCheckBox* _log_general;
1329 wxCheckBox* _log_warning;
1330 wxCheckBox* _log_error;
1331 wxCheckBox* _log_timing;
1332 wxCheckBox* _log_debug_decode;
1333 wxCheckBox* _log_debug_encode;
1334 #ifdef DCPOMATIC_WINDOWS
1335 wxCheckBox* _win32_console;
1339 wxPreferencesEditor*
1340 create_config_dialog ()
1342 wxPreferencesEditor* e = new wxPreferencesEditor ();
1344 #ifdef DCPOMATIC_OSX
1345 /* Width that we force some of the config panels to be on OSX so that
1346 the containing window doesn't shrink too much when we select those panels.
1347 This is obviously an unpleasant hack.
1349 wxSize ps = wxSize (520, -1);
1350 int const border = 16;
1352 wxSize ps = wxSize (-1, -1);
1353 int const border = 8;
1356 e->AddPage (new GeneralPage (ps, border));
1357 e->AddPage (new DefaultsPage (ps, border));
1358 e->AddPage (new EncodingServersPage (ps, border));
1359 e->AddPage (new KeysPage (ps, border));
1360 e->AddPage (new TMSPage (ps, border));
1361 e->AddPage (new KDMEmailPage (ps, border));
1362 e->AddPage (new AdvancedPage (ps, border));