2 Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 /** @file src/config_dialog.cc
22 * @brief A dialogue to edit DCP-o-matic configuration.
25 #include "config_dialog.h"
27 #include "editable_list.h"
28 #include "filter_dialog.h"
29 #include "dir_picker_ctrl.h"
30 #include "file_picker_ctrl.h"
31 #include "isdcf_metadata_dialog.h"
32 #include "server_dialog.h"
33 #include "make_chain_dialog.h"
34 #include "email_dialog.h"
35 #include "name_format_editor.h"
36 #include "lib/config.h"
37 #include "lib/ratio.h"
38 #include "lib/filter.h"
39 #include "lib/dcp_content_type.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include <dcp/locale_convert.h>
45 #include <dcp/exceptions.h>
46 #include <dcp/certificate_chain.h>
47 #include <wx/stdpaths.h>
48 #include <wx/preferences.h>
49 #include <wx/spinctrl.h>
50 #include <wx/filepicker.h>
52 #include <boost/filesystem.hpp>
53 #include <boost/foreach.hpp>
64 using boost::shared_ptr;
65 using boost::function;
66 using boost::optional;
67 using dcp::locale_convert;
72 Page (wxSize panel_size, int border)
75 , _panel_size (panel_size)
76 , _window_exists (false)
78 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
84 wxWindow* create_window (wxWindow* parent)
86 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
87 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
91 _window_exists = true;
94 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
103 virtual void config_changed () = 0;
104 virtual void setup () = 0;
106 void config_changed_wrapper ()
108 if (_window_exists) {
113 void window_destroyed ()
115 _window_exists = false;
119 boost::signals2::scoped_connection _config_connection;
123 class StockPage : public wxStockPreferencesPage, public Page
126 StockPage (Kind kind, wxSize panel_size, int border)
127 : wxStockPreferencesPage (kind)
128 , Page (panel_size, border)
131 wxWindow* CreateWindow (wxWindow* parent)
133 return create_window (parent);
137 class StandardPage : public wxPreferencesPage, public Page
140 StandardPage (wxSize panel_size, int border)
141 : Page (panel_size, border)
144 wxWindow* CreateWindow (wxWindow* parent)
146 return create_window (parent);
150 class GeneralPage : public StockPage
153 GeneralPage (wxSize panel_size, int border)
154 : StockPage (Kind_General, panel_size, border)
160 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
161 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
164 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
165 table->Add (_set_language, wxGBPosition (r, 0));
166 _language = new wxChoice (_panel, wxID_ANY);
167 vector<pair<string, string> > languages;
168 languages.push_back (make_pair ("Čeština", "cs_CZ"));
169 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
170 languages.push_back (make_pair ("Dansk", "da_DK"));
171 languages.push_back (make_pair ("Deutsch", "de_DE"));
172 languages.push_back (make_pair ("English", "en_GB"));
173 languages.push_back (make_pair ("Español", "es_ES"));
174 languages.push_back (make_pair ("Français", "fr_FR"));
175 languages.push_back (make_pair ("Italiano", "it_IT"));
176 languages.push_back (make_pair ("Nederlands", "nl_NL"));
177 languages.push_back (make_pair ("Русский", "ru_RU"));
178 languages.push_back (make_pair ("Polski", "pl_PL"));
179 languages.push_back (make_pair ("Português europeu", "pt_PT"));
180 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
181 languages.push_back (make_pair ("Svenska", "sv_SE"));
182 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
183 languages.push_back (make_pair ("українська мова", "uk_UA"));
184 checked_set (_language, languages);
185 table->Add (_language, wxGBPosition (r, 1));
188 wxStaticText* restart = add_label_to_sizer (
189 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
191 wxFont font = restart->GetFont();
192 font.SetStyle (wxFONTSTYLE_ITALIC);
193 font.SetPointSize (font.GetPointSize() - 1);
194 restart->SetFont (font);
197 add_label_to_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
198 _num_local_encoding_threads = new wxSpinCtrl (_panel);
199 table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
202 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
203 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml");
204 table->Add (_cinemas_file, wxGBPosition (r, 1));
207 add_label_to_sizer (table, _panel, _("Sound output"), true, wxGBPosition (r, 0));
208 _sound_output = new wxChoice (_panel, wxID_ANY);
209 table->Add (_sound_output, wxGBPosition (r, 1));
212 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
213 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
214 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
218 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
219 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
222 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
223 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
226 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
227 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
230 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
231 bottom_table->AddGrowableCol (1, 1);
233 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
234 _issuer = new wxTextCtrl (_panel, wxID_ANY);
235 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
237 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
238 _creator = new wxTextCtrl (_panel, wxID_ANY);
239 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
241 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
244 RtAudio audio (DCPOMATIC_RTAUDIO_API);
245 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
246 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
247 if (dev.probed && dev.outputChannels > 0) {
248 _sound_output->Append (std_to_wx (dev.name));
252 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
253 _language->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::language_changed, this));
254 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
255 _sound_output->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::sound_output_changed, this));
257 _num_local_encoding_threads->SetRange (1, 128);
258 _num_local_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
260 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
261 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
263 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
264 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
265 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
267 _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
268 _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
271 void config_changed ()
273 Config* config = Config::instance ();
275 checked_set (_set_language, static_cast<bool>(config->language()));
277 /* Backwards compatibility of config file */
279 map<string, string> compat_map;
280 compat_map["fr"] = "fr_FR";
281 compat_map["it"] = "it_IT";
282 compat_map["es"] = "es_ES";
283 compat_map["sv"] = "sv_SE";
284 compat_map["de"] = "de_DE";
285 compat_map["nl"] = "nl_NL";
286 compat_map["ru"] = "ru_RU";
287 compat_map["pl"] = "pl_PL";
288 compat_map["da"] = "da_DK";
289 compat_map["pt"] = "pt_PT";
290 compat_map["sk"] = "sk_SK";
291 compat_map["cs"] = "cs_CZ";
292 compat_map["uk"] = "uk_UA";
294 string lang = config->language().get_value_or ("en_GB");
295 if (compat_map.find (lang) != compat_map.end ()) {
296 lang = compat_map[lang];
299 checked_set (_language, lang);
301 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
302 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
303 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
305 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
306 checked_set (_check_for_updates, config->check_for_updates ());
307 checked_set (_check_for_test_updates, config->check_for_test_updates ());
308 checked_set (_issuer, config->dcp_issuer ());
309 checked_set (_creator, config->dcp_creator ());
310 checked_set (_cinemas_file, config->cinemas_file());
312 optional<string> const current_so = get_sound_output ();
313 string configured_so;
315 if (config->sound_output()) {
316 configured_so = config->sound_output().get();
318 /* No configured output means we should use the default */
319 RtAudio audio (DCPOMATIC_RTAUDIO_API);
320 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
323 if (!current_so || *current_so != configured_so) {
324 /* Update _sound_output with the configured value */
326 while (i < _sound_output->GetCount()) {
327 if (_sound_output->GetString(i) == std_to_wx(configured_so)) {
328 _sound_output->SetSelection (i);
335 setup_sensitivity ();
338 /** @return Currently-selected sound output in the dialogue */
339 optional<string> get_sound_output ()
341 int const sel = _sound_output->GetSelection ();
342 if (sel == wxNOT_FOUND) {
343 return optional<string> ();
346 return wx_to_std (_sound_output->GetString (sel));
349 void setup_sensitivity ()
351 _language->Enable (_set_language->GetValue ());
352 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
355 void set_language_changed ()
357 setup_sensitivity ();
358 if (_set_language->GetValue ()) {
361 Config::instance()->unset_language ();
365 void language_changed ()
367 int const sel = _language->GetSelection ();
369 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
371 Config::instance()->unset_language ();
375 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
376 void analyse_ebur128_changed ()
378 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
382 void automatic_audio_analysis_changed ()
384 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
387 void check_for_updates_changed ()
389 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
392 void check_for_test_updates_changed ()
394 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
397 void num_local_encoding_threads_changed ()
399 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
402 void issuer_changed ()
404 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
407 void creator_changed ()
409 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
412 void cinemas_file_changed ()
414 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
417 void sound_output_changed ()
419 RtAudio audio (DCPOMATIC_RTAUDIO_API);
420 optional<string> const so = get_sound_output();
421 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
422 Config::instance()->unset_sound_output ();
424 Config::instance()->set_sound_output (*so);
428 wxCheckBox* _set_language;
430 wxSpinCtrl* _num_local_encoding_threads;
431 FilePickerCtrl* _cinemas_file;
432 wxChoice* _sound_output;
433 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
434 wxCheckBox* _analyse_ebur128;
436 wxCheckBox* _automatic_audio_analysis;
437 wxCheckBox* _check_for_updates;
438 wxCheckBox* _check_for_test_updates;
440 wxTextCtrl* _creator;
443 class DefaultsPage : public StandardPage
446 DefaultsPage (wxSize panel_size, int border)
447 : StandardPage (panel_size, border)
450 wxString GetName () const
452 return _("Defaults");
456 wxBitmap GetLargeIcon () const
458 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
465 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
466 table->AddGrowableCol (1, 1);
467 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
470 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
471 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
472 _still_length = new wxSpinCtrl (_panel);
473 s->Add (_still_length);
474 add_label_to_sizer (s, _panel, _("s"), false);
478 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
479 #ifdef DCPOMATIC_USE_OWN_PICKER
480 _directory = new DirPickerCtrl (_panel);
482 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
484 table->Add (_directory, 1, wxEXPAND);
486 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
487 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
488 table->Add (_isdcf_metadata_button);
490 add_label_to_sizer (table, _panel, _("Default container"), true);
491 _container = new wxChoice (_panel, wxID_ANY);
492 table->Add (_container);
494 add_label_to_sizer (table, _panel, _("Default content type"), true);
495 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
496 table->Add (_dcp_content_type);
498 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
499 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
500 table->Add (_dcp_audio_channels);
503 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
504 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
505 _j2k_bandwidth = new wxSpinCtrl (_panel);
506 s->Add (_j2k_bandwidth);
507 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
512 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
513 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
514 _audio_delay = new wxSpinCtrl (_panel);
515 s->Add (_audio_delay);
516 add_label_to_sizer (s, _panel, _("ms"), false);
520 add_label_to_sizer (table, _panel, _("Default standard"), true);
521 _standard = new wxChoice (_panel, wxID_ANY);
522 table->Add (_standard);
524 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
525 #ifdef DCPOMATIC_USE_OWN_PICKER
526 _kdm_directory = new DirPickerCtrl (_panel);
528 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
530 table->Add (_kdm_directory, 1, wxEXPAND);
532 _still_length->SetRange (1, 3600);
533 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
535 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
536 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
538 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
540 vector<Ratio const *> ratios = Ratio::all ();
541 for (size_t i = 0; i < ratios.size(); ++i) {
542 _container->Append (std_to_wx (ratios[i]->nickname ()));
545 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
547 vector<DCPContentType const *> const ct = DCPContentType::all ();
548 for (size_t i = 0; i < ct.size(); ++i) {
549 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
552 setup_audio_channels_choice (_dcp_audio_channels, 2);
554 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
555 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
557 _j2k_bandwidth->SetRange (50, 250);
558 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
560 _audio_delay->SetRange (-1000, 1000);
561 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
563 _standard->Append (_("SMPTE"));
564 _standard->Append (_("Interop"));
565 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
568 void config_changed ()
570 Config* config = Config::instance ();
572 vector<Ratio const *> ratios = Ratio::all ();
573 for (size_t i = 0; i < ratios.size(); ++i) {
574 if (ratios[i] == config->default_container ()) {
575 _container->SetSelection (i);
579 vector<DCPContentType const *> const ct = DCPContentType::all ();
580 for (size_t i = 0; i < ct.size(); ++i) {
581 if (ct[i] == config->default_dcp_content_type ()) {
582 _dcp_content_type->SetSelection (i);
586 checked_set (_still_length, config->default_still_length ());
587 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
588 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
589 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
590 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
591 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
592 checked_set (_audio_delay, config->default_audio_delay ());
593 checked_set (_standard, config->default_interop() ? 1 : 0);
596 void j2k_bandwidth_changed ()
598 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
601 void audio_delay_changed ()
603 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
606 void dcp_audio_channels_changed ()
608 int const s = _dcp_audio_channels->GetSelection ();
609 if (s != wxNOT_FOUND) {
610 Config::instance()->set_default_dcp_audio_channels (
611 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
616 void directory_changed ()
618 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
621 void kdm_directory_changed ()
623 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
626 void edit_isdcf_metadata_clicked ()
628 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
630 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
634 void still_length_changed ()
636 Config::instance()->set_default_still_length (_still_length->GetValue ());
639 void container_changed ()
641 vector<Ratio const *> ratio = Ratio::all ();
642 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
645 void dcp_content_type_changed ()
647 vector<DCPContentType const *> ct = DCPContentType::all ();
648 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
651 void standard_changed ()
653 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
656 wxSpinCtrl* _j2k_bandwidth;
657 wxSpinCtrl* _audio_delay;
658 wxButton* _isdcf_metadata_button;
659 wxSpinCtrl* _still_length;
660 #ifdef DCPOMATIC_USE_OWN_PICKER
661 DirPickerCtrl* _directory;
662 DirPickerCtrl* _kdm_directory;
664 wxDirPickerCtrl* _directory;
665 wxDirPickerCtrl* _kdm_directory;
667 wxChoice* _container;
668 wxChoice* _dcp_content_type;
669 wxChoice* _dcp_audio_channels;
673 class EncodingServersPage : public StandardPage
676 EncodingServersPage (wxSize panel_size, int border)
677 : StandardPage (panel_size, border)
680 wxString GetName () const
686 wxBitmap GetLargeIcon () const
688 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
695 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
696 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
698 vector<string> columns;
699 columns.push_back (wx_to_std (_("IP address / host name")));
700 _servers_list = new EditableList<string, ServerDialog> (
703 boost::bind (&Config::servers, Config::instance()),
704 boost::bind (&Config::set_servers, Config::instance(), _1),
705 boost::bind (&EncodingServersPage::server_column, this, _1)
708 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
710 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
713 void config_changed ()
715 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
716 _servers_list->refresh ();
719 void use_any_servers_changed ()
721 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
724 string server_column (string s)
729 wxCheckBox* _use_any_servers;
730 EditableList<string, ServerDialog>* _servers_list;
733 class CertificateChainEditor : public wxPanel
736 CertificateChainEditor (
740 function<void (shared_ptr<dcp::CertificateChain>)> set,
741 function<shared_ptr<const dcp::CertificateChain> (void)> get
747 wxFont subheading_font (*wxNORMAL_FONT);
748 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
750 _sizer = new wxBoxSizer (wxVERTICAL);
753 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
754 m->SetFont (subheading_font);
755 _sizer->Add (m, 0, wxALL, border);
758 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
759 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
761 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
766 ip.SetText (_("Type"));
768 _certificates->InsertColumn (0, ip);
774 ip.SetText (_("Thumbprint"));
777 wxFont font = ip.GetFont ();
778 font.SetFamily (wxFONTFAMILY_TELETYPE);
781 _certificates->InsertColumn (1, ip);
784 certificates_sizer->Add (_certificates, 1, wxEXPAND);
787 wxSizer* s = new wxBoxSizer (wxVERTICAL);
788 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
789 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
790 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
791 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
792 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
793 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
794 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
797 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
798 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
801 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
802 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
803 wxFont font = _private_key->GetFont ();
804 font.SetFamily (wxFONTFAMILY_TELETYPE);
805 _private_key->SetFont (font);
806 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
807 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
808 table->Add (_load_private_key, wxGBPosition (r, 2));
809 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
810 table->Add (_export_private_key, wxGBPosition (r, 3));
813 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
814 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
815 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
816 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
819 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
820 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
821 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
822 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
823 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
824 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
825 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
826 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
828 SetSizerAndFit (_sizer);
831 void config_changed ()
833 _chain.reset (new dcp::CertificateChain (*_get().get ()));
835 update_certificate_list ();
836 update_private_key ();
837 update_sensitivity ();
840 void add_button (wxWindow* button)
842 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
847 void add_certificate ()
849 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
851 if (d->ShowModal() == wxID_OK) {
854 string const extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
855 if (!extra.empty ()) {
858 _("This file contains other certificates (or other data) after its first certificate. "
859 "Only the first certificate will be used.")
864 update_certificate_list ();
865 } catch (dcp::MiscError& e) {
866 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
872 update_sensitivity ();
875 void remove_certificate ()
877 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
882 _certificates->DeleteItem (i);
886 update_sensitivity ();
889 void export_certificate ()
891 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
896 wxFileDialog* d = new wxFileDialog (
897 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
898 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
901 dcp::CertificateChain::List all = _chain->root_to_leaf ();
902 dcp::CertificateChain::List::iterator j = all.begin ();
903 for (int k = 0; k < i; ++k) {
907 if (d->ShowModal () == wxID_OK) {
908 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
910 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
913 string const s = j->certificate (true);
914 fwrite (s.c_str(), 1, s.length(), f);
920 void update_certificate_list ()
922 _certificates->DeleteAllItems ();
924 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
925 BOOST_FOREACH (dcp::Certificate const & i, certs) {
928 _certificates->InsertItem (item);
929 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
932 _certificates->SetItem (n, 0, _("Root"));
933 } else if (n == (certs.size() - 1)) {
934 _certificates->SetItem (n, 0, _("Leaf"));
936 _certificates->SetItem (n, 0, _("Intermediate"));
943 void remake_certificates ()
945 shared_ptr<const dcp::CertificateChain> chain = _get ();
947 string subject_organization_name;
948 string subject_organizational_unit_name;
949 string root_common_name;
950 string intermediate_common_name;
951 string leaf_common_name;
953 dcp::CertificateChain::List all = chain->root_to_leaf ();
955 if (all.size() >= 1) {
957 subject_organization_name = chain->root().subject_organization_name ();
958 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
959 root_common_name = chain->root().subject_common_name ();
962 if (all.size() >= 2) {
964 leaf_common_name = chain->leaf().subject_common_name ();
967 if (all.size() >= 3) {
968 /* Have an intermediate */
969 dcp::CertificateChain::List::iterator i = all.begin ();
971 intermediate_common_name = i->subject_common_name ();
974 MakeChainDialog* d = new MakeChainDialog (
976 subject_organization_name,
977 subject_organizational_unit_name,
979 intermediate_common_name,
983 if (d->ShowModal () == wxID_OK) {
985 new dcp::CertificateChain (
988 d->organisational_unit (),
989 d->root_common_name (),
990 d->intermediate_common_name (),
991 d->leaf_common_name ()
996 update_certificate_list ();
997 update_private_key ();
1003 void update_sensitivity ()
1005 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1006 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1009 void update_private_key ()
1011 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1015 void load_private_key ()
1017 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1019 if (d->ShowModal() == wxID_OK) {
1021 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1022 if (boost::filesystem::file_size (p) > 8192) {
1025 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1030 _chain->set_key (dcp::file_to_string (p));
1032 update_private_key ();
1033 } catch (dcp::MiscError& e) {
1034 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1040 update_sensitivity ();
1043 void export_private_key ()
1045 optional<string> key = _chain->key ();
1050 wxFileDialog* d = new wxFileDialog (
1051 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1052 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1055 if (d->ShowModal () == wxID_OK) {
1056 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1058 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1061 string const s = _chain->key().get ();
1062 fwrite (s.c_str(), 1, s.length(), f);
1068 wxListCtrl* _certificates;
1069 wxButton* _add_certificate;
1070 wxButton* _export_certificate;
1071 wxButton* _remove_certificate;
1072 wxButton* _remake_certificates;
1073 wxStaticText* _private_key;
1074 wxButton* _load_private_key;
1075 wxButton* _export_private_key;
1077 wxBoxSizer* _button_sizer;
1078 shared_ptr<dcp::CertificateChain> _chain;
1079 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1080 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1083 class KeysPage : public StandardPage
1086 KeysPage (wxSize panel_size, int border)
1087 : StandardPage (panel_size, border)
1090 wxString GetName () const
1095 #ifdef DCPOMATIC_OSX
1096 wxBitmap GetLargeIcon () const
1098 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1106 _signer = new CertificateChainEditor (
1107 _panel, _("Signing DCPs and KDMs"), _border,
1108 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1109 boost::bind (&Config::signer_chain, Config::instance ())
1112 _panel->GetSizer()->Add (_signer);
1114 _decryption = new CertificateChainEditor (
1115 _panel, _("Decrypting DCPs"), _border,
1116 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1117 boost::bind (&Config::decryption_chain, Config::instance ())
1120 _panel->GetSizer()->Add (_decryption);
1122 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\ncertificate..."));
1123 _decryption->add_button (_export_decryption_certificate);
1124 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\nchain..."));
1125 _decryption->add_button (_export_decryption_chain);
1127 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1128 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1131 void export_decryption_certificate ()
1133 wxFileDialog* d = new wxFileDialog (
1134 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1135 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1138 if (d->ShowModal () == wxID_OK) {
1139 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1141 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1144 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1145 fwrite (s.c_str(), 1, s.length(), f);
1151 void export_decryption_chain ()
1153 wxFileDialog* d = new wxFileDialog (
1154 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1155 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1158 if (d->ShowModal () == wxID_OK) {
1159 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1161 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1164 string const s = Config::instance()->decryption_chain()->chain();
1165 fwrite (s.c_str(), 1, s.length(), f);
1171 void config_changed ()
1173 _signer->config_changed ();
1174 _decryption->config_changed ();
1177 CertificateChainEditor* _signer;
1178 CertificateChainEditor* _decryption;
1179 wxButton* _export_decryption_certificate;
1180 wxButton* _export_decryption_chain;
1183 class TMSPage : public StandardPage
1186 TMSPage (wxSize panel_size, int border)
1187 : StandardPage (panel_size, border)
1190 wxString GetName () const
1195 #ifdef DCPOMATIC_OSX
1196 wxBitmap GetLargeIcon () const
1198 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1205 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1206 table->AddGrowableCol (1, 1);
1207 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1209 add_label_to_sizer (table, _panel, _("Protocol"), true);
1210 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1211 table->Add (_tms_protocol, 1, wxEXPAND);
1213 add_label_to_sizer (table, _panel, _("IP address"), true);
1214 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1215 table->Add (_tms_ip, 1, wxEXPAND);
1217 add_label_to_sizer (table, _panel, _("Target path"), true);
1218 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1219 table->Add (_tms_path, 1, wxEXPAND);
1221 add_label_to_sizer (table, _panel, _("User name"), true);
1222 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1223 table->Add (_tms_user, 1, wxEXPAND);
1225 add_label_to_sizer (table, _panel, _("Password"), true);
1226 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1227 table->Add (_tms_password, 1, wxEXPAND);
1229 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1230 _tms_protocol->Append (_("FTP (for Dolby)"));
1232 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1233 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1234 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1235 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1236 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1239 void config_changed ()
1241 Config* config = Config::instance ();
1243 checked_set (_tms_protocol, config->tms_protocol ());
1244 checked_set (_tms_ip, config->tms_ip ());
1245 checked_set (_tms_path, config->tms_path ());
1246 checked_set (_tms_user, config->tms_user ());
1247 checked_set (_tms_password, config->tms_password ());
1250 void tms_protocol_changed ()
1252 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1255 void tms_ip_changed ()
1257 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1260 void tms_path_changed ()
1262 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1265 void tms_user_changed ()
1267 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1270 void tms_password_changed ()
1272 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1275 wxChoice* _tms_protocol;
1276 wxTextCtrl* _tms_ip;
1277 wxTextCtrl* _tms_path;
1278 wxTextCtrl* _tms_user;
1279 wxTextCtrl* _tms_password;
1288 class KDMEmailPage : public StandardPage
1292 KDMEmailPage (wxSize panel_size, int border)
1293 #ifdef DCPOMATIC_OSX
1294 /* We have to force both width and height of this one */
1295 : StandardPage (wxSize (480, 128), border)
1297 : StandardPage (panel_size, border)
1301 wxString GetName () const
1303 return _("KDM Email");
1306 #ifdef DCPOMATIC_OSX
1307 wxBitmap GetLargeIcon () const
1309 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1316 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1317 table->AddGrowableCol (1, 1);
1318 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1320 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1322 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1323 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1324 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1325 add_label_to_sizer (s, _panel, _("port"), false);
1326 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1327 _mail_port->SetRange (0, 65535);
1328 s->Add (_mail_port);
1329 table->Add (s, 1, wxEXPAND | wxALL);
1332 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1333 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1334 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1336 add_label_to_sizer (table, _panel, _("Mail password"), true);
1337 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1338 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1340 add_label_to_sizer (table, _panel, _("Subject"), true);
1341 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1342 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1344 add_label_to_sizer (table, _panel, _("From address"), true);
1345 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1346 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1348 vector<string> columns;
1349 columns.push_back (wx_to_std (_("Address")));
1350 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1351 _kdm_cc = new EditableList<string, EmailDialog> (
1354 bind (&Config::kdm_cc, Config::instance()),
1355 bind (&Config::set_kdm_cc, Config::instance(), _1),
1358 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1360 add_label_to_sizer (table, _panel, _("BCC address"), true);
1361 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1362 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1364 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1365 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1367 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1368 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1372 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1373 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1374 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1375 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1376 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1377 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1378 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1379 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1380 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1383 void config_changed ()
1385 Config* config = Config::instance ();
1387 checked_set (_mail_server, config->mail_server ());
1388 checked_set (_mail_port, config->mail_port ());
1389 checked_set (_mail_user, config->mail_user ());
1390 checked_set (_mail_password, config->mail_password ());
1391 checked_set (_kdm_subject, config->kdm_subject ());
1392 checked_set (_kdm_from, config->kdm_from ());
1393 checked_set (_kdm_bcc, config->kdm_bcc ());
1394 checked_set (_kdm_email, Config::instance()->kdm_email ());
1397 void mail_server_changed ()
1399 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1402 void mail_port_changed ()
1404 Config::instance()->set_mail_port (_mail_port->GetValue ());
1407 void mail_user_changed ()
1409 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1412 void mail_password_changed ()
1414 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1417 void kdm_subject_changed ()
1419 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1422 void kdm_from_changed ()
1424 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1427 void kdm_bcc_changed ()
1429 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1432 void kdm_email_changed ()
1434 if (_kdm_email->GetValue().IsEmpty ()) {
1435 /* Sometimes we get sent an erroneous notification that the email
1436 is empty; I don't know why.
1440 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1443 void reset_kdm_email ()
1445 Config::instance()->reset_kdm_email ();
1446 checked_set (_kdm_email, Config::instance()->kdm_email ());
1449 wxTextCtrl* _mail_server;
1450 wxSpinCtrl* _mail_port;
1451 wxTextCtrl* _mail_user;
1452 wxTextCtrl* _mail_password;
1453 wxTextCtrl* _kdm_subject;
1454 wxTextCtrl* _kdm_from;
1455 EditableList<string, EmailDialog>* _kdm_cc;
1456 wxTextCtrl* _kdm_bcc;
1457 wxTextCtrl* _kdm_email;
1458 wxButton* _reset_kdm_email;
1461 /** @class AdvancedPage
1462 * @brief Advanced page of the preferences dialog.
1464 class AdvancedPage : public StockPage
1467 AdvancedPage (wxSize panel_size, int border)
1468 : StockPage (Kind_Advanced, panel_size, border)
1469 , _maximum_j2k_bandwidth (0)
1470 , _allow_any_dcp_frame_rate (0)
1471 , _only_servers_encode (0)
1476 , _log_debug_decode (0)
1477 , _log_debug_encode (0)
1478 , _log_debug_email (0)
1482 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1484 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1486 flags |= wxALIGN_RIGHT;
1489 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1490 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1495 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1496 table->AddGrowableCol (1, 1);
1497 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1500 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1501 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1502 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1503 s->Add (_maximum_j2k_bandwidth, 1);
1504 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1508 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1509 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1510 table->AddSpacer (0);
1512 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1513 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1514 table->AddSpacer (0);
1517 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1518 dcp::NameFormat::Map titles;
1519 titles['t'] = "type (cpl/pkl)";
1520 dcp::NameFormat::Map examples;
1521 examples['t'] = "cpl";
1522 _dcp_metadata_filename_format = new NameFormatEditor (
1523 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1525 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1529 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1530 dcp::NameFormat::Map titles;
1531 titles['t'] = "type (j2c/pcm/sub)";
1532 titles['r'] = "reel number";
1533 titles['n'] = "number of reels";
1534 titles['c'] = "content filename";
1535 dcp::NameFormat::Map examples;
1536 examples['t'] = "j2c";
1537 examples['r'] = "1";
1538 examples['n'] = "4";
1539 examples['c'] = "myfile.mp4";
1540 _dcp_asset_filename_format = new NameFormatEditor (
1541 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1543 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1547 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1548 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1549 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1550 t->Add (_log_general, 1, wxEXPAND | wxALL);
1551 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1552 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1553 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1554 t->Add (_log_error, 1, wxEXPAND | wxALL);
1555 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1556 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1557 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1558 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1559 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1560 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1561 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1562 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1563 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1564 table->Add (t, 0, wxALL, 6);
1567 #ifdef DCPOMATIC_WINDOWS
1568 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1569 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1570 table->AddSpacer (0);
1573 _maximum_j2k_bandwidth->SetRange (1, 1000);
1574 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1575 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1576 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1577 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1578 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1579 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1580 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1581 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1582 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1583 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1584 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1585 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1586 #ifdef DCPOMATIC_WINDOWS
1587 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1591 void config_changed ()
1593 Config* config = Config::instance ();
1595 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1596 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1597 checked_set (_only_servers_encode, config->only_servers_encode ());
1598 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1599 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1600 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1601 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1602 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1603 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1604 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1605 #ifdef DCPOMATIC_WINDOWS
1606 checked_set (_win32_console, config->win32_console());
1610 void maximum_j2k_bandwidth_changed ()
1612 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1615 void allow_any_dcp_frame_rate_changed ()
1617 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1620 void only_servers_encode_changed ()
1622 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1625 void dcp_metadata_filename_format_changed ()
1627 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1630 void dcp_asset_filename_format_changed ()
1632 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1638 if (_log_general->GetValue ()) {
1639 types |= LogEntry::TYPE_GENERAL;
1641 if (_log_warning->GetValue ()) {
1642 types |= LogEntry::TYPE_WARNING;
1644 if (_log_error->GetValue ()) {
1645 types |= LogEntry::TYPE_ERROR;
1647 if (_log_timing->GetValue ()) {
1648 types |= LogEntry::TYPE_TIMING;
1650 if (_log_debug_decode->GetValue ()) {
1651 types |= LogEntry::TYPE_DEBUG_DECODE;
1653 if (_log_debug_encode->GetValue ()) {
1654 types |= LogEntry::TYPE_DEBUG_ENCODE;
1656 if (_log_debug_email->GetValue ()) {
1657 types |= LogEntry::TYPE_DEBUG_EMAIL;
1659 Config::instance()->set_log_types (types);
1662 #ifdef DCPOMATIC_WINDOWS
1663 void win32_console_changed ()
1665 Config::instance()->set_win32_console (_win32_console->GetValue ());
1669 wxSpinCtrl* _maximum_j2k_bandwidth;
1670 wxCheckBox* _allow_any_dcp_frame_rate;
1671 wxCheckBox* _only_servers_encode;
1672 NameFormatEditor* _dcp_metadata_filename_format;
1673 NameFormatEditor* _dcp_asset_filename_format;
1674 wxCheckBox* _log_general;
1675 wxCheckBox* _log_warning;
1676 wxCheckBox* _log_error;
1677 wxCheckBox* _log_timing;
1678 wxCheckBox* _log_debug_decode;
1679 wxCheckBox* _log_debug_encode;
1680 wxCheckBox* _log_debug_email;
1681 #ifdef DCPOMATIC_WINDOWS
1682 wxCheckBox* _win32_console;
1686 wxPreferencesEditor*
1687 create_config_dialog ()
1689 wxPreferencesEditor* e = new wxPreferencesEditor ();
1691 #ifdef DCPOMATIC_OSX
1692 /* Width that we force some of the config panels to be on OSX so that
1693 the containing window doesn't shrink too much when we select those panels.
1694 This is obviously an unpleasant hack.
1696 wxSize ps = wxSize (520, -1);
1697 int const border = 16;
1699 wxSize ps = wxSize (-1, -1);
1700 int const border = 8;
1703 e->AddPage (new GeneralPage (ps, border));
1704 e->AddPage (new DefaultsPage (ps, border));
1705 e->AddPage (new EncodingServersPage (ps, border));
1706 e->AddPage (new KeysPage (ps, border));
1707 e->AddPage (new TMSPage (ps, border));
1708 e->AddPage (new KDMEmailPage (ps, border));
1709 e->AddPage (new AdvancedPage (ps, border));