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, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
198 _master_encoding_threads = new wxSpinCtrl (_panel);
199 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
202 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
203 _server_encoding_threads = new wxSpinCtrl (_panel);
204 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
207 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
208 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
209 table->Add (_cinemas_file, wxGBPosition (r, 1));
212 _preview_sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound in the preview via"));
213 table->Add (_preview_sound, wxGBPosition (r, 0));
214 _preview_sound_output = new wxChoice (_panel, wxID_ANY);
215 table->Add (_preview_sound_output, wxGBPosition (r, 1));
218 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
219 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
220 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
224 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
225 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
228 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
229 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
232 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
233 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
236 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
237 bottom_table->AddGrowableCol (1, 1);
239 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
240 _issuer = new wxTextCtrl (_panel, wxID_ANY);
241 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
243 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
244 _creator = new wxTextCtrl (_panel, wxID_ANY);
245 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
247 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
250 RtAudio audio (DCPOMATIC_RTAUDIO_API);
251 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
252 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
253 if (dev.probed && dev.outputChannels > 0) {
254 _preview_sound_output->Append (std_to_wx (dev.name));
258 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
259 _language->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::language_changed, this));
260 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
261 _preview_sound->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::preview_sound_changed, this));
262 _preview_sound_output->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::preview_sound_output_changed, this));
264 _master_encoding_threads->SetRange (1, 128);
265 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
266 _server_encoding_threads->SetRange (1, 128);
267 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::server_encoding_threads_changed, this));
269 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
270 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
272 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
273 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
274 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
276 _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
277 _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
280 void config_changed ()
282 Config* config = Config::instance ();
284 checked_set (_set_language, static_cast<bool>(config->language()));
286 /* Backwards compatibility of config file */
288 map<string, string> compat_map;
289 compat_map["fr"] = "fr_FR";
290 compat_map["it"] = "it_IT";
291 compat_map["es"] = "es_ES";
292 compat_map["sv"] = "sv_SE";
293 compat_map["de"] = "de_DE";
294 compat_map["nl"] = "nl_NL";
295 compat_map["ru"] = "ru_RU";
296 compat_map["pl"] = "pl_PL";
297 compat_map["da"] = "da_DK";
298 compat_map["pt"] = "pt_PT";
299 compat_map["sk"] = "sk_SK";
300 compat_map["cs"] = "cs_CZ";
301 compat_map["uk"] = "uk_UA";
303 string lang = config->language().get_value_or ("en_GB");
304 if (compat_map.find (lang) != compat_map.end ()) {
305 lang = compat_map[lang];
308 checked_set (_language, lang);
310 checked_set (_master_encoding_threads, config->master_encoding_threads ());
311 checked_set (_server_encoding_threads, config->server_encoding_threads ());
312 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
313 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
315 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
316 checked_set (_check_for_updates, config->check_for_updates ());
317 checked_set (_check_for_test_updates, config->check_for_test_updates ());
318 checked_set (_issuer, config->dcp_issuer ());
319 checked_set (_creator, config->dcp_creator ());
320 checked_set (_cinemas_file, config->cinemas_file());
321 checked_set (_preview_sound, config->preview_sound());
323 optional<string> const current_so = get_preview_sound_output ();
324 string configured_so;
326 if (config->preview_sound_output()) {
327 configured_so = config->preview_sound_output().get();
329 /* No configured output means we should use the default */
330 RtAudio audio (DCPOMATIC_RTAUDIO_API);
331 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
334 if (!current_so || *current_so != configured_so) {
335 /* Update _preview_sound_output with the configured value */
337 while (i < _preview_sound_output->GetCount()) {
338 if (_preview_sound_output->GetString(i) == std_to_wx(configured_so)) {
339 _preview_sound_output->SetSelection (i);
346 setup_sensitivity ();
349 /** @return Currently-selected preview sound output in the dialogue */
350 optional<string> get_preview_sound_output ()
352 int const sel = _preview_sound_output->GetSelection ();
353 if (sel == wxNOT_FOUND) {
354 return optional<string> ();
357 return wx_to_std (_preview_sound_output->GetString (sel));
360 void setup_sensitivity ()
362 _language->Enable (_set_language->GetValue ());
363 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
364 _preview_sound_output->Enable (_preview_sound->GetValue ());
367 void set_language_changed ()
369 setup_sensitivity ();
370 if (_set_language->GetValue ()) {
373 Config::instance()->unset_language ();
377 void language_changed ()
379 int const sel = _language->GetSelection ();
381 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
383 Config::instance()->unset_language ();
387 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
388 void analyse_ebur128_changed ()
390 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
394 void automatic_audio_analysis_changed ()
396 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
399 void check_for_updates_changed ()
401 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
404 void check_for_test_updates_changed ()
406 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
409 void master_encoding_threads_changed ()
411 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
414 void server_encoding_threads_changed ()
416 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
419 void issuer_changed ()
421 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
424 void creator_changed ()
426 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
429 void cinemas_file_changed ()
431 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
434 void preview_sound_changed ()
436 Config::instance()->set_preview_sound (_preview_sound->GetValue ());
439 void preview_sound_output_changed ()
441 RtAudio audio (DCPOMATIC_RTAUDIO_API);
442 optional<string> const so = get_preview_sound_output();
443 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
444 Config::instance()->unset_preview_sound_output ();
446 Config::instance()->set_preview_sound_output (*so);
450 wxCheckBox* _set_language;
452 wxSpinCtrl* _master_encoding_threads;
453 wxSpinCtrl* _server_encoding_threads;
454 FilePickerCtrl* _cinemas_file;
455 wxCheckBox* _preview_sound;
456 wxChoice* _preview_sound_output;
457 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
458 wxCheckBox* _analyse_ebur128;
460 wxCheckBox* _automatic_audio_analysis;
461 wxCheckBox* _check_for_updates;
462 wxCheckBox* _check_for_test_updates;
464 wxTextCtrl* _creator;
467 class DefaultsPage : public StandardPage
470 DefaultsPage (wxSize panel_size, int border)
471 : StandardPage (panel_size, border)
474 wxString GetName () const
476 return _("Defaults");
480 wxBitmap GetLargeIcon () const
482 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
489 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
490 table->AddGrowableCol (1, 1);
491 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
494 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
495 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
496 _still_length = new wxSpinCtrl (_panel);
497 s->Add (_still_length);
498 add_label_to_sizer (s, _panel, _("s"), false);
502 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
503 #ifdef DCPOMATIC_USE_OWN_PICKER
504 _directory = new DirPickerCtrl (_panel);
506 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
508 table->Add (_directory, 1, wxEXPAND);
510 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
511 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
512 table->Add (_isdcf_metadata_button);
514 add_label_to_sizer (table, _panel, _("Default container"), true);
515 _container = new wxChoice (_panel, wxID_ANY);
516 table->Add (_container);
518 add_label_to_sizer (table, _panel, _("Default scale-to"), true);
519 _scale_to = new wxChoice (_panel, wxID_ANY);
520 table->Add (_scale_to);
522 add_label_to_sizer (table, _panel, _("Default content type"), true);
523 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
524 table->Add (_dcp_content_type);
526 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
527 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
528 table->Add (_dcp_audio_channels);
531 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
532 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
533 _j2k_bandwidth = new wxSpinCtrl (_panel);
534 s->Add (_j2k_bandwidth);
535 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
540 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
541 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
542 _audio_delay = new wxSpinCtrl (_panel);
543 s->Add (_audio_delay);
544 add_label_to_sizer (s, _panel, _("ms"), false);
548 add_label_to_sizer (table, _panel, _("Default standard"), true);
549 _standard = new wxChoice (_panel, wxID_ANY);
550 table->Add (_standard);
552 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
553 #ifdef DCPOMATIC_USE_OWN_PICKER
554 _kdm_directory = new DirPickerCtrl (_panel);
556 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
558 table->Add (_kdm_directory, 1, wxEXPAND);
560 _still_length->SetRange (1, 3600);
561 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
563 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
564 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
566 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
568 vector<Ratio const *> ratios = Ratio::all ();
569 for (size_t i = 0; i < ratios.size(); ++i) {
570 _container->Append (std_to_wx (ratios[i]->nickname ()));
573 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
575 _scale_to->Append (_("Guess from content"));
577 for (size_t i = 0; i < ratios.size(); ++i) {
578 _scale_to->Append (std_to_wx (ratios[i]->nickname ()));
581 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
583 vector<DCPContentType const *> const ct = DCPContentType::all ();
584 for (size_t i = 0; i < ct.size(); ++i) {
585 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
588 setup_audio_channels_choice (_dcp_audio_channels, 2);
590 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
591 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
593 _j2k_bandwidth->SetRange (50, 250);
594 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
596 _audio_delay->SetRange (-1000, 1000);
597 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
599 _standard->Append (_("SMPTE"));
600 _standard->Append (_("Interop"));
601 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
604 void config_changed ()
606 Config* config = Config::instance ();
608 vector<Ratio const *> ratios = Ratio::all ();
609 for (size_t i = 0; i < ratios.size(); ++i) {
610 if (ratios[i] == config->default_container ()) {
611 _container->SetSelection (i);
613 if (ratios[i] == config->default_scale_to ()) {
614 _scale_to->SetSelection (i + 1);
618 if (!config->default_scale_to()) {
619 _scale_to->SetSelection (0);
622 vector<DCPContentType const *> const ct = DCPContentType::all ();
623 for (size_t i = 0; i < ct.size(); ++i) {
624 if (ct[i] == config->default_dcp_content_type ()) {
625 _dcp_content_type->SetSelection (i);
629 checked_set (_still_length, config->default_still_length ());
630 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
631 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
632 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
633 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
634 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
635 checked_set (_audio_delay, config->default_audio_delay ());
636 checked_set (_standard, config->default_interop() ? 1 : 0);
639 void j2k_bandwidth_changed ()
641 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
644 void audio_delay_changed ()
646 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
649 void dcp_audio_channels_changed ()
651 int const s = _dcp_audio_channels->GetSelection ();
652 if (s != wxNOT_FOUND) {
653 Config::instance()->set_default_dcp_audio_channels (
654 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
659 void directory_changed ()
661 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
664 void kdm_directory_changed ()
666 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
669 void edit_isdcf_metadata_clicked ()
671 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
673 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
677 void still_length_changed ()
679 Config::instance()->set_default_still_length (_still_length->GetValue ());
682 void container_changed ()
684 vector<Ratio const *> ratio = Ratio::all ();
685 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
688 void scale_to_changed ()
690 int const s = _scale_to->GetSelection ();
692 Config::instance()->set_default_scale_to (0);
694 vector<Ratio const *> ratio = Ratio::all ();
695 Config::instance()->set_default_scale_to (ratio[s - 1]);
699 void dcp_content_type_changed ()
701 vector<DCPContentType const *> ct = DCPContentType::all ();
702 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
705 void standard_changed ()
707 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
710 wxSpinCtrl* _j2k_bandwidth;
711 wxSpinCtrl* _audio_delay;
712 wxButton* _isdcf_metadata_button;
713 wxSpinCtrl* _still_length;
714 #ifdef DCPOMATIC_USE_OWN_PICKER
715 DirPickerCtrl* _directory;
716 DirPickerCtrl* _kdm_directory;
718 wxDirPickerCtrl* _directory;
719 wxDirPickerCtrl* _kdm_directory;
721 wxChoice* _container;
723 wxChoice* _dcp_content_type;
724 wxChoice* _dcp_audio_channels;
728 class EncodingServersPage : public StandardPage
731 EncodingServersPage (wxSize panel_size, int border)
732 : StandardPage (panel_size, border)
735 wxString GetName () const
741 wxBitmap GetLargeIcon () const
743 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
750 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
751 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
753 vector<string> columns;
754 columns.push_back (wx_to_std (_("IP address / host name")));
755 _servers_list = new EditableList<string, ServerDialog> (
758 boost::bind (&Config::servers, Config::instance()),
759 boost::bind (&Config::set_servers, Config::instance(), _1),
760 boost::bind (&EncodingServersPage::server_column, this, _1)
763 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
765 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
768 void config_changed ()
770 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
771 _servers_list->refresh ();
774 void use_any_servers_changed ()
776 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
779 string server_column (string s)
784 wxCheckBox* _use_any_servers;
785 EditableList<string, ServerDialog>* _servers_list;
788 class CertificateChainEditor : public wxPanel
791 CertificateChainEditor (
795 function<void (shared_ptr<dcp::CertificateChain>)> set,
796 function<shared_ptr<const dcp::CertificateChain> (void)> get
802 wxFont subheading_font (*wxNORMAL_FONT);
803 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
805 _sizer = new wxBoxSizer (wxVERTICAL);
808 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
809 m->SetFont (subheading_font);
810 _sizer->Add (m, 0, wxALL, border);
813 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
814 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
816 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
821 ip.SetText (_("Type"));
823 _certificates->InsertColumn (0, ip);
829 ip.SetText (_("Thumbprint"));
832 wxFont font = ip.GetFont ();
833 font.SetFamily (wxFONTFAMILY_TELETYPE);
836 _certificates->InsertColumn (1, ip);
839 certificates_sizer->Add (_certificates, 1, wxEXPAND);
842 wxSizer* s = new wxBoxSizer (wxVERTICAL);
843 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
844 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
845 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
846 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
847 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
848 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
849 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
852 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
853 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
856 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
857 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
858 wxFont font = _private_key->GetFont ();
859 font.SetFamily (wxFONTFAMILY_TELETYPE);
860 _private_key->SetFont (font);
861 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
862 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
863 table->Add (_load_private_key, wxGBPosition (r, 2));
864 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
865 table->Add (_export_private_key, wxGBPosition (r, 3));
868 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
869 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
870 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
871 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
874 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
875 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
876 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
877 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
878 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
879 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
880 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
881 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
883 SetSizerAndFit (_sizer);
886 void config_changed ()
888 _chain.reset (new dcp::CertificateChain (*_get().get ()));
890 update_certificate_list ();
891 update_private_key ();
892 update_sensitivity ();
895 void add_button (wxWindow* button)
897 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
902 void add_certificate ()
904 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
906 if (d->ShowModal() == wxID_OK) {
909 string const extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
910 if (!extra.empty ()) {
913 _("This file contains other certificates (or other data) after its first certificate. "
914 "Only the first certificate will be used.")
919 update_certificate_list ();
920 } catch (dcp::MiscError& e) {
921 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
927 update_sensitivity ();
930 void remove_certificate ()
932 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
937 _certificates->DeleteItem (i);
941 update_sensitivity ();
944 void export_certificate ()
946 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
951 wxFileDialog* d = new wxFileDialog (
952 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
953 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
956 dcp::CertificateChain::List all = _chain->root_to_leaf ();
957 dcp::CertificateChain::List::iterator j = all.begin ();
958 for (int k = 0; k < i; ++k) {
962 if (d->ShowModal () == wxID_OK) {
963 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
965 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
968 string const s = j->certificate (true);
969 fwrite (s.c_str(), 1, s.length(), f);
975 void update_certificate_list ()
977 _certificates->DeleteAllItems ();
979 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
980 BOOST_FOREACH (dcp::Certificate const & i, certs) {
983 _certificates->InsertItem (item);
984 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
987 _certificates->SetItem (n, 0, _("Root"));
988 } else if (n == (certs.size() - 1)) {
989 _certificates->SetItem (n, 0, _("Leaf"));
991 _certificates->SetItem (n, 0, _("Intermediate"));
998 void remake_certificates ()
1000 shared_ptr<const dcp::CertificateChain> chain = _get ();
1002 string subject_organization_name;
1003 string subject_organizational_unit_name;
1004 string root_common_name;
1005 string intermediate_common_name;
1006 string leaf_common_name;
1008 dcp::CertificateChain::List all = chain->root_to_leaf ();
1010 if (all.size() >= 1) {
1012 subject_organization_name = chain->root().subject_organization_name ();
1013 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1014 root_common_name = chain->root().subject_common_name ();
1017 if (all.size() >= 2) {
1019 leaf_common_name = chain->leaf().subject_common_name ();
1022 if (all.size() >= 3) {
1023 /* Have an intermediate */
1024 dcp::CertificateChain::List::iterator i = all.begin ();
1026 intermediate_common_name = i->subject_common_name ();
1029 MakeChainDialog* d = new MakeChainDialog (
1031 subject_organization_name,
1032 subject_organizational_unit_name,
1034 intermediate_common_name,
1038 if (d->ShowModal () == wxID_OK) {
1040 new dcp::CertificateChain (
1043 d->organisational_unit (),
1044 d->root_common_name (),
1045 d->intermediate_common_name (),
1046 d->leaf_common_name ()
1051 update_certificate_list ();
1052 update_private_key ();
1058 void update_sensitivity ()
1060 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1061 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1064 void update_private_key ()
1066 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1070 void load_private_key ()
1072 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1074 if (d->ShowModal() == wxID_OK) {
1076 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1077 if (boost::filesystem::file_size (p) > 8192) {
1080 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1085 _chain->set_key (dcp::file_to_string (p));
1087 update_private_key ();
1088 } catch (dcp::MiscError& e) {
1089 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1095 update_sensitivity ();
1098 void export_private_key ()
1100 optional<string> key = _chain->key ();
1105 wxFileDialog* d = new wxFileDialog (
1106 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1107 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1110 if (d->ShowModal () == wxID_OK) {
1111 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1113 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1116 string const s = _chain->key().get ();
1117 fwrite (s.c_str(), 1, s.length(), f);
1123 wxListCtrl* _certificates;
1124 wxButton* _add_certificate;
1125 wxButton* _export_certificate;
1126 wxButton* _remove_certificate;
1127 wxButton* _remake_certificates;
1128 wxStaticText* _private_key;
1129 wxButton* _load_private_key;
1130 wxButton* _export_private_key;
1132 wxBoxSizer* _button_sizer;
1133 shared_ptr<dcp::CertificateChain> _chain;
1134 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1135 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1138 class KeysPage : public StandardPage
1141 KeysPage (wxSize panel_size, int border)
1142 : StandardPage (panel_size, border)
1145 wxString GetName () const
1150 #ifdef DCPOMATIC_OSX
1151 wxBitmap GetLargeIcon () const
1153 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1161 _signer = new CertificateChainEditor (
1162 _panel, _("Signing DCPs and KDMs"), _border,
1163 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1164 boost::bind (&Config::signer_chain, Config::instance ())
1167 _panel->GetSizer()->Add (_signer);
1169 _decryption = new CertificateChainEditor (
1170 _panel, _("Decrypting KDMs"), _border,
1171 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1172 boost::bind (&Config::decryption_chain, Config::instance ())
1175 _panel->GetSizer()->Add (_decryption);
1177 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1178 _decryption->add_button (_export_decryption_certificate);
1179 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1180 _decryption->add_button (_export_decryption_chain);
1182 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1183 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1186 void export_decryption_certificate ()
1188 wxFileDialog* d = new wxFileDialog (
1189 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1190 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1193 if (d->ShowModal () == wxID_OK) {
1194 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1196 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1199 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1200 fwrite (s.c_str(), 1, s.length(), f);
1206 void export_decryption_chain ()
1208 wxFileDialog* d = new wxFileDialog (
1209 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1210 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1213 if (d->ShowModal () == wxID_OK) {
1214 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1216 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1219 string const s = Config::instance()->decryption_chain()->chain();
1220 fwrite (s.c_str(), 1, s.length(), f);
1226 void config_changed ()
1228 _signer->config_changed ();
1229 _decryption->config_changed ();
1232 CertificateChainEditor* _signer;
1233 CertificateChainEditor* _decryption;
1234 wxButton* _export_decryption_certificate;
1235 wxButton* _export_decryption_chain;
1238 class TMSPage : public StandardPage
1241 TMSPage (wxSize panel_size, int border)
1242 : StandardPage (panel_size, border)
1245 wxString GetName () const
1250 #ifdef DCPOMATIC_OSX
1251 wxBitmap GetLargeIcon () const
1253 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1260 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1261 table->AddGrowableCol (1, 1);
1262 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1264 add_label_to_sizer (table, _panel, _("Protocol"), true);
1265 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1266 table->Add (_tms_protocol, 1, wxEXPAND);
1268 add_label_to_sizer (table, _panel, _("IP address"), true);
1269 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1270 table->Add (_tms_ip, 1, wxEXPAND);
1272 add_label_to_sizer (table, _panel, _("Target path"), true);
1273 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1274 table->Add (_tms_path, 1, wxEXPAND);
1276 add_label_to_sizer (table, _panel, _("User name"), true);
1277 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1278 table->Add (_tms_user, 1, wxEXPAND);
1280 add_label_to_sizer (table, _panel, _("Password"), true);
1281 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1282 table->Add (_tms_password, 1, wxEXPAND);
1284 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1285 _tms_protocol->Append (_("FTP (for Dolby)"));
1287 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1288 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1289 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1290 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1291 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1294 void config_changed ()
1296 Config* config = Config::instance ();
1298 checked_set (_tms_protocol, config->tms_protocol ());
1299 checked_set (_tms_ip, config->tms_ip ());
1300 checked_set (_tms_path, config->tms_path ());
1301 checked_set (_tms_user, config->tms_user ());
1302 checked_set (_tms_password, config->tms_password ());
1305 void tms_protocol_changed ()
1307 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1310 void tms_ip_changed ()
1312 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1315 void tms_path_changed ()
1317 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1320 void tms_user_changed ()
1322 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1325 void tms_password_changed ()
1327 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1330 wxChoice* _tms_protocol;
1331 wxTextCtrl* _tms_ip;
1332 wxTextCtrl* _tms_path;
1333 wxTextCtrl* _tms_user;
1334 wxTextCtrl* _tms_password;
1343 class KDMEmailPage : public StandardPage
1347 KDMEmailPage (wxSize panel_size, int border)
1348 #ifdef DCPOMATIC_OSX
1349 /* We have to force both width and height of this one */
1350 : StandardPage (wxSize (480, 128), border)
1352 : StandardPage (panel_size, border)
1356 wxString GetName () const
1358 return _("KDM Email");
1361 #ifdef DCPOMATIC_OSX
1362 wxBitmap GetLargeIcon () const
1364 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1371 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1372 table->AddGrowableCol (1, 1);
1373 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1375 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1377 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1378 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1379 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1380 add_label_to_sizer (s, _panel, _("port"), false);
1381 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1382 _mail_port->SetRange (0, 65535);
1383 s->Add (_mail_port);
1384 table->Add (s, 1, wxEXPAND | wxALL);
1387 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1388 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1389 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1391 add_label_to_sizer (table, _panel, _("Mail password"), true);
1392 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1393 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1395 add_label_to_sizer (table, _panel, _("Subject"), true);
1396 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1397 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1399 add_label_to_sizer (table, _panel, _("From address"), true);
1400 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1401 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1403 vector<string> columns;
1404 columns.push_back (wx_to_std (_("Address")));
1405 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1406 _kdm_cc = new EditableList<string, EmailDialog> (
1409 bind (&Config::kdm_cc, Config::instance()),
1410 bind (&Config::set_kdm_cc, Config::instance(), _1),
1413 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1415 add_label_to_sizer (table, _panel, _("BCC address"), true);
1416 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1417 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1419 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1420 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1422 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1423 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1427 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1428 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1429 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1430 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1431 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1432 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1433 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1434 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1435 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1438 void config_changed ()
1440 Config* config = Config::instance ();
1442 checked_set (_mail_server, config->mail_server ());
1443 checked_set (_mail_port, config->mail_port ());
1444 checked_set (_mail_user, config->mail_user ());
1445 checked_set (_mail_password, config->mail_password ());
1446 checked_set (_kdm_subject, config->kdm_subject ());
1447 checked_set (_kdm_from, config->kdm_from ());
1448 checked_set (_kdm_bcc, config->kdm_bcc ());
1449 checked_set (_kdm_email, Config::instance()->kdm_email ());
1452 void mail_server_changed ()
1454 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1457 void mail_port_changed ()
1459 Config::instance()->set_mail_port (_mail_port->GetValue ());
1462 void mail_user_changed ()
1464 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1467 void mail_password_changed ()
1469 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1472 void kdm_subject_changed ()
1474 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1477 void kdm_from_changed ()
1479 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1482 void kdm_bcc_changed ()
1484 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1487 void kdm_email_changed ()
1489 if (_kdm_email->GetValue().IsEmpty ()) {
1490 /* Sometimes we get sent an erroneous notification that the email
1491 is empty; I don't know why.
1495 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1498 void reset_kdm_email ()
1500 Config::instance()->reset_kdm_email ();
1501 checked_set (_kdm_email, Config::instance()->kdm_email ());
1504 wxTextCtrl* _mail_server;
1505 wxSpinCtrl* _mail_port;
1506 wxTextCtrl* _mail_user;
1507 wxTextCtrl* _mail_password;
1508 wxTextCtrl* _kdm_subject;
1509 wxTextCtrl* _kdm_from;
1510 EditableList<string, EmailDialog>* _kdm_cc;
1511 wxTextCtrl* _kdm_bcc;
1512 wxTextCtrl* _kdm_email;
1513 wxButton* _reset_kdm_email;
1516 class CoverSheetPage : public StandardPage
1520 CoverSheetPage (wxSize panel_size, int border)
1521 #ifdef DCPOMATIC_OSX
1522 /* We have to force both width and height of this one */
1523 : StandardPage (wxSize (480, 128), border)
1525 : StandardPage (panel_size, border)
1529 wxString GetName () const
1531 return _("Cover Sheet");
1534 #ifdef DCPOMATIC_OSX
1535 wxBitmap GetLargeIcon () const
1537 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1544 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1545 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1547 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1548 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1550 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1551 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1554 void config_changed ()
1556 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1559 void cover_sheet_changed ()
1561 if (_cover_sheet->GetValue().IsEmpty ()) {
1562 /* Sometimes we get sent an erroneous notification that the cover sheet
1563 is empty; I don't know why.
1567 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1570 void reset_cover_sheet ()
1572 Config::instance()->reset_cover_sheet ();
1573 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1576 wxTextCtrl* _cover_sheet;
1577 wxButton* _reset_cover_sheet;
1581 /** @class AdvancedPage
1582 * @brief Advanced page of the preferences dialog.
1584 class AdvancedPage : public StockPage
1587 AdvancedPage (wxSize panel_size, int border)
1588 : StockPage (Kind_Advanced, panel_size, border)
1589 , _maximum_j2k_bandwidth (0)
1590 , _allow_any_dcp_frame_rate (0)
1591 , _only_servers_encode (0)
1596 , _log_debug_decode (0)
1597 , _log_debug_encode (0)
1598 , _log_debug_email (0)
1602 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1604 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1606 flags |= wxALIGN_RIGHT;
1609 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1610 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1615 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1616 table->AddGrowableCol (1, 1);
1617 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1620 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1621 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1622 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1623 s->Add (_maximum_j2k_bandwidth, 1);
1624 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1628 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1629 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1630 table->AddSpacer (0);
1632 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1633 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1634 table->AddSpacer (0);
1637 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1638 dcp::NameFormat::Map titles;
1639 titles['t'] = "type (cpl/pkl)";
1640 dcp::NameFormat::Map examples;
1641 examples['t'] = "cpl";
1642 _dcp_metadata_filename_format = new NameFormatEditor (
1643 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1645 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1649 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1650 dcp::NameFormat::Map titles;
1651 titles['t'] = "type (j2c/pcm/sub)";
1652 titles['r'] = "reel number";
1653 titles['n'] = "number of reels";
1654 titles['c'] = "content filename";
1655 dcp::NameFormat::Map examples;
1656 examples['t'] = "j2c";
1657 examples['r'] = "1";
1658 examples['n'] = "4";
1659 examples['c'] = "myfile.mp4";
1660 _dcp_asset_filename_format = new NameFormatEditor (
1661 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1663 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1667 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1668 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1669 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1670 t->Add (_log_general, 1, wxEXPAND | wxALL);
1671 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1672 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1673 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1674 t->Add (_log_error, 1, wxEXPAND | wxALL);
1675 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1676 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1677 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1678 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1679 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1680 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1681 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1682 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1683 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1684 table->Add (t, 0, wxALL, 6);
1687 #ifdef DCPOMATIC_WINDOWS
1688 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1689 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1690 table->AddSpacer (0);
1693 _maximum_j2k_bandwidth->SetRange (1, 1000);
1694 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1695 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1696 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1697 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1698 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1699 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1700 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1701 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1702 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1703 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1704 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1705 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1706 #ifdef DCPOMATIC_WINDOWS
1707 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1711 void config_changed ()
1713 Config* config = Config::instance ();
1715 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1716 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1717 checked_set (_only_servers_encode, config->only_servers_encode ());
1718 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1719 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1720 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1721 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1722 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1723 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1724 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1725 #ifdef DCPOMATIC_WINDOWS
1726 checked_set (_win32_console, config->win32_console());
1730 void maximum_j2k_bandwidth_changed ()
1732 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1735 void allow_any_dcp_frame_rate_changed ()
1737 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1740 void only_servers_encode_changed ()
1742 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1745 void dcp_metadata_filename_format_changed ()
1747 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1750 void dcp_asset_filename_format_changed ()
1752 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1758 if (_log_general->GetValue ()) {
1759 types |= LogEntry::TYPE_GENERAL;
1761 if (_log_warning->GetValue ()) {
1762 types |= LogEntry::TYPE_WARNING;
1764 if (_log_error->GetValue ()) {
1765 types |= LogEntry::TYPE_ERROR;
1767 if (_log_timing->GetValue ()) {
1768 types |= LogEntry::TYPE_TIMING;
1770 if (_log_debug_decode->GetValue ()) {
1771 types |= LogEntry::TYPE_DEBUG_DECODE;
1773 if (_log_debug_encode->GetValue ()) {
1774 types |= LogEntry::TYPE_DEBUG_ENCODE;
1776 if (_log_debug_email->GetValue ()) {
1777 types |= LogEntry::TYPE_DEBUG_EMAIL;
1779 Config::instance()->set_log_types (types);
1782 #ifdef DCPOMATIC_WINDOWS
1783 void win32_console_changed ()
1785 Config::instance()->set_win32_console (_win32_console->GetValue ());
1789 wxSpinCtrl* _maximum_j2k_bandwidth;
1790 wxCheckBox* _allow_any_dcp_frame_rate;
1791 wxCheckBox* _only_servers_encode;
1792 NameFormatEditor* _dcp_metadata_filename_format;
1793 NameFormatEditor* _dcp_asset_filename_format;
1794 wxCheckBox* _log_general;
1795 wxCheckBox* _log_warning;
1796 wxCheckBox* _log_error;
1797 wxCheckBox* _log_timing;
1798 wxCheckBox* _log_debug_decode;
1799 wxCheckBox* _log_debug_encode;
1800 wxCheckBox* _log_debug_email;
1801 #ifdef DCPOMATIC_WINDOWS
1802 wxCheckBox* _win32_console;
1806 wxPreferencesEditor*
1807 create_config_dialog ()
1809 wxPreferencesEditor* e = new wxPreferencesEditor ();
1811 #ifdef DCPOMATIC_OSX
1812 /* Width that we force some of the config panels to be on OSX so that
1813 the containing window doesn't shrink too much when we select those panels.
1814 This is obviously an unpleasant hack.
1816 wxSize ps = wxSize (520, -1);
1817 int const border = 16;
1819 wxSize ps = wxSize (-1, -1);
1820 int const border = 8;
1823 e->AddPage (new GeneralPage (ps, border));
1824 e->AddPage (new DefaultsPage (ps, border));
1825 e->AddPage (new EncodingServersPage (ps, border));
1826 e->AddPage (new KeysPage (ps, border));
1827 e->AddPage (new TMSPage (ps, border));
1828 e->AddPage (new KDMEmailPage (ps, border));
1829 e->AddPage (new CoverSheetPage (ps, border));
1830 e->AddPage (new AdvancedPage (ps, border));