2 Copyright (C) 2012-2017 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 "nag_dialog.h"
37 #include "lib/config.h"
38 #include "lib/ratio.h"
39 #include "lib/filter.h"
40 #include "lib/dcp_content_type.h"
43 #include "lib/cross.h"
44 #include "lib/exceptions.h"
45 #include <dcp/locale_convert.h>
46 #include <dcp/exceptions.h>
47 #include <dcp/certificate_chain.h>
48 #include <wx/stdpaths.h>
49 #include <wx/preferences.h>
50 #include <wx/spinctrl.h>
51 #include <wx/filepicker.h>
53 #include <boost/filesystem.hpp>
54 #include <boost/foreach.hpp>
65 using boost::shared_ptr;
66 using boost::function;
67 using boost::optional;
68 using dcp::locale_convert;
80 Page (wxSize panel_size, int border)
83 , _panel_size (panel_size)
84 , _window_exists (false)
86 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
92 wxWindow* create_window (wxWindow* parent)
94 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
95 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
99 _window_exists = true;
102 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
111 virtual void config_changed () = 0;
112 virtual void setup () = 0;
114 void config_changed_wrapper ()
116 if (_window_exists) {
121 void window_destroyed ()
123 _window_exists = false;
127 boost::signals2::scoped_connection _config_connection;
131 class StockPage : public wxStockPreferencesPage, public Page
134 StockPage (Kind kind, wxSize panel_size, int border)
135 : wxStockPreferencesPage (kind)
136 , Page (panel_size, border)
139 wxWindow* CreateWindow (wxWindow* parent)
141 return create_window (parent);
145 class StandardPage : public wxPreferencesPage, public Page
148 StandardPage (wxSize panel_size, int border)
149 : Page (panel_size, border)
152 wxWindow* CreateWindow (wxWindow* parent)
154 return create_window (parent);
158 class GeneralPage : public StockPage
161 GeneralPage (wxSize panel_size, int border)
162 : StockPage (Kind_General, panel_size, border)
168 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
169 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
172 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
173 table->Add (_set_language, wxGBPosition (r, 0));
174 _language = new wxChoice (_panel, wxID_ANY);
175 vector<pair<string, string> > languages;
176 languages.push_back (make_pair ("Čeština", "cs_CZ"));
177 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
178 languages.push_back (make_pair ("Dansk", "da_DK"));
179 languages.push_back (make_pair ("Deutsch", "de_DE"));
180 languages.push_back (make_pair ("English", "en_GB"));
181 languages.push_back (make_pair ("Español", "es_ES"));
182 languages.push_back (make_pair ("Français", "fr_FR"));
183 languages.push_back (make_pair ("Italiano", "it_IT"));
184 languages.push_back (make_pair ("Nederlands", "nl_NL"));
185 languages.push_back (make_pair ("Русский", "ru_RU"));
186 languages.push_back (make_pair ("Polski", "pl_PL"));
187 languages.push_back (make_pair ("Português europeu", "pt_PT"));
188 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
189 languages.push_back (make_pair ("Svenska", "sv_SE"));
190 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
191 languages.push_back (make_pair ("українська мова", "uk_UA"));
192 checked_set (_language, languages);
193 table->Add (_language, wxGBPosition (r, 1));
196 wxStaticText* restart = add_label_to_sizer (
197 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
199 wxFont font = restart->GetFont();
200 font.SetStyle (wxFONTSTYLE_ITALIC);
201 font.SetPointSize (font.GetPointSize() - 1);
202 restart->SetFont (font);
205 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
206 _master_encoding_threads = new wxSpinCtrl (_panel);
207 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
210 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
211 _server_encoding_threads = new wxSpinCtrl (_panel);
212 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
215 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
216 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
217 table->Add (_cinemas_file, wxGBPosition (r, 1));
220 _preview_sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound in the preview via"));
221 table->Add (_preview_sound, wxGBPosition (r, 0));
222 _preview_sound_output = new wxChoice (_panel, wxID_ANY);
223 table->Add (_preview_sound_output, wxGBPosition (r, 1));
226 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
227 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
228 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
232 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
233 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
236 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
237 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
240 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
241 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
244 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
245 bottom_table->AddGrowableCol (1, 1);
247 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
248 _issuer = new wxTextCtrl (_panel, wxID_ANY);
249 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
251 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
252 _creator = new wxTextCtrl (_panel, wxID_ANY);
253 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
255 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
258 RtAudio audio (DCPOMATIC_RTAUDIO_API);
259 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
260 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
261 if (dev.probed && dev.outputChannels > 0) {
262 _preview_sound_output->Append (std_to_wx (dev.name));
266 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
267 _language->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::language_changed, this));
268 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
269 _preview_sound->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::preview_sound_changed, this));
270 _preview_sound_output->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::preview_sound_output_changed, this));
272 _master_encoding_threads->SetRange (1, 128);
273 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
274 _server_encoding_threads->SetRange (1, 128);
275 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::server_encoding_threads_changed, this));
277 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
278 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
280 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
281 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
282 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
284 _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
285 _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
288 void config_changed ()
290 Config* config = Config::instance ();
292 checked_set (_set_language, static_cast<bool>(config->language()));
294 /* Backwards compatibility of config file */
296 map<string, string> compat_map;
297 compat_map["fr"] = "fr_FR";
298 compat_map["it"] = "it_IT";
299 compat_map["es"] = "es_ES";
300 compat_map["sv"] = "sv_SE";
301 compat_map["de"] = "de_DE";
302 compat_map["nl"] = "nl_NL";
303 compat_map["ru"] = "ru_RU";
304 compat_map["pl"] = "pl_PL";
305 compat_map["da"] = "da_DK";
306 compat_map["pt"] = "pt_PT";
307 compat_map["sk"] = "sk_SK";
308 compat_map["cs"] = "cs_CZ";
309 compat_map["uk"] = "uk_UA";
311 string lang = config->language().get_value_or ("en_GB");
312 if (compat_map.find (lang) != compat_map.end ()) {
313 lang = compat_map[lang];
316 checked_set (_language, lang);
318 checked_set (_master_encoding_threads, config->master_encoding_threads ());
319 checked_set (_server_encoding_threads, config->server_encoding_threads ());
320 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
321 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
323 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
324 checked_set (_check_for_updates, config->check_for_updates ());
325 checked_set (_check_for_test_updates, config->check_for_test_updates ());
326 checked_set (_issuer, config->dcp_issuer ());
327 checked_set (_creator, config->dcp_creator ());
328 checked_set (_cinemas_file, config->cinemas_file());
329 checked_set (_preview_sound, config->preview_sound());
331 optional<string> const current_so = get_preview_sound_output ();
332 optional<string> configured_so;
334 if (config->preview_sound_output()) {
335 configured_so = config->preview_sound_output().get();
337 /* No configured output means we should use the default */
338 RtAudio audio (DCPOMATIC_RTAUDIO_API);
340 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
341 } catch (RtAudioError& e) {
342 /* Probably no audio devices at all */
346 if (configured_so && current_so != configured_so) {
347 /* Update _preview_sound_output with the configured value */
349 while (i < _preview_sound_output->GetCount()) {
350 if (_preview_sound_output->GetString(i) == std_to_wx(*configured_so)) {
351 _preview_sound_output->SetSelection (i);
358 setup_sensitivity ();
361 /** @return Currently-selected preview sound output in the dialogue */
362 optional<string> get_preview_sound_output ()
364 int const sel = _preview_sound_output->GetSelection ();
365 if (sel == wxNOT_FOUND) {
366 return optional<string> ();
369 return wx_to_std (_preview_sound_output->GetString (sel));
372 void setup_sensitivity ()
374 _language->Enable (_set_language->GetValue ());
375 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
376 _preview_sound_output->Enable (_preview_sound->GetValue ());
379 void set_language_changed ()
381 setup_sensitivity ();
382 if (_set_language->GetValue ()) {
385 Config::instance()->unset_language ();
389 void language_changed ()
391 int const sel = _language->GetSelection ();
393 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
395 Config::instance()->unset_language ();
399 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
400 void analyse_ebur128_changed ()
402 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
406 void automatic_audio_analysis_changed ()
408 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
411 void check_for_updates_changed ()
413 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
416 void check_for_test_updates_changed ()
418 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
421 void master_encoding_threads_changed ()
423 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
426 void server_encoding_threads_changed ()
428 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
431 void issuer_changed ()
433 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
436 void creator_changed ()
438 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
441 void cinemas_file_changed ()
443 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
446 void preview_sound_changed ()
448 Config::instance()->set_preview_sound (_preview_sound->GetValue ());
451 void preview_sound_output_changed ()
453 RtAudio audio (DCPOMATIC_RTAUDIO_API);
454 optional<string> const so = get_preview_sound_output();
455 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
456 Config::instance()->unset_preview_sound_output ();
458 Config::instance()->set_preview_sound_output (*so);
462 wxCheckBox* _set_language;
464 wxSpinCtrl* _master_encoding_threads;
465 wxSpinCtrl* _server_encoding_threads;
466 FilePickerCtrl* _cinemas_file;
467 wxCheckBox* _preview_sound;
468 wxChoice* _preview_sound_output;
469 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
470 wxCheckBox* _analyse_ebur128;
472 wxCheckBox* _automatic_audio_analysis;
473 wxCheckBox* _check_for_updates;
474 wxCheckBox* _check_for_test_updates;
476 wxTextCtrl* _creator;
479 class DefaultsPage : public StandardPage
482 DefaultsPage (wxSize panel_size, int border)
483 : StandardPage (panel_size, border)
486 wxString GetName () const
488 return _("Defaults");
492 wxBitmap GetLargeIcon () const
494 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
501 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
502 table->AddGrowableCol (1, 1);
503 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
506 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
507 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
508 _still_length = new wxSpinCtrl (_panel);
509 s->Add (_still_length);
510 add_label_to_sizer (s, _panel, _("s"), false);
514 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
515 #ifdef DCPOMATIC_USE_OWN_PICKER
516 _directory = new DirPickerCtrl (_panel);
518 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
520 table->Add (_directory, 1, wxEXPAND);
522 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
523 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
524 table->Add (_isdcf_metadata_button);
526 add_label_to_sizer (table, _panel, _("Default container"), true);
527 _container = new wxChoice (_panel, wxID_ANY);
528 table->Add (_container);
530 add_label_to_sizer (table, _panel, _("Default scale-to"), true);
531 _scale_to = new wxChoice (_panel, wxID_ANY);
532 table->Add (_scale_to);
534 add_label_to_sizer (table, _panel, _("Default content type"), true);
535 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
536 table->Add (_dcp_content_type);
538 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
539 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
540 table->Add (_dcp_audio_channels);
543 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
544 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
545 _j2k_bandwidth = new wxSpinCtrl (_panel);
546 s->Add (_j2k_bandwidth);
547 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
552 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
553 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
554 _audio_delay = new wxSpinCtrl (_panel);
555 s->Add (_audio_delay);
556 add_label_to_sizer (s, _panel, _("ms"), false);
560 add_label_to_sizer (table, _panel, _("Default standard"), true);
561 _standard = new wxChoice (_panel, wxID_ANY);
562 table->Add (_standard);
564 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
565 #ifdef DCPOMATIC_USE_OWN_PICKER
566 _kdm_directory = new DirPickerCtrl (_panel);
568 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
570 table->Add (_kdm_directory, 1, wxEXPAND);
572 _still_length->SetRange (1, 3600);
573 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
575 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
576 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
578 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
580 BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
581 _container->Append (std_to_wx(i->container_nickname()));
584 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
586 _scale_to->Append (_("Guess from content"));
588 BOOST_FOREACH (Ratio const * i, Ratio::all()) {
589 _scale_to->Append (std_to_wx(i->image_nickname()));
592 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
594 BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
595 _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
598 setup_audio_channels_choice (_dcp_audio_channels, 2);
600 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
601 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
603 _j2k_bandwidth->SetRange (50, 250);
604 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
606 _audio_delay->SetRange (-1000, 1000);
607 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
609 _standard->Append (_("SMPTE"));
610 _standard->Append (_("Interop"));
611 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
614 void config_changed ()
616 Config* config = Config::instance ();
618 vector<Ratio const *> ratios = Ratio::all ();
619 for (size_t i = 0; i < ratios.size(); ++i) {
620 if (ratios[i] == config->default_container ()) {
621 _container->SetSelection (i);
623 if (ratios[i] == config->default_scale_to ()) {
624 _scale_to->SetSelection (i + 1);
628 if (!config->default_scale_to()) {
629 _scale_to->SetSelection (0);
632 vector<DCPContentType const *> const ct = DCPContentType::all ();
633 for (size_t i = 0; i < ct.size(); ++i) {
634 if (ct[i] == config->default_dcp_content_type ()) {
635 _dcp_content_type->SetSelection (i);
639 checked_set (_still_length, config->default_still_length ());
640 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
641 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
642 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
643 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
644 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
645 checked_set (_audio_delay, config->default_audio_delay ());
646 checked_set (_standard, config->default_interop() ? 1 : 0);
649 void j2k_bandwidth_changed ()
651 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
654 void audio_delay_changed ()
656 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
659 void dcp_audio_channels_changed ()
661 int const s = _dcp_audio_channels->GetSelection ();
662 if (s != wxNOT_FOUND) {
663 Config::instance()->set_default_dcp_audio_channels (
664 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
669 void directory_changed ()
671 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
674 void kdm_directory_changed ()
676 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
679 void edit_isdcf_metadata_clicked ()
681 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
683 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
687 void still_length_changed ()
689 Config::instance()->set_default_still_length (_still_length->GetValue ());
692 void container_changed ()
694 vector<Ratio const *> ratio = Ratio::all ();
695 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
698 void scale_to_changed ()
700 int const s = _scale_to->GetSelection ();
702 Config::instance()->set_default_scale_to (0);
704 vector<Ratio const *> ratio = Ratio::all ();
705 Config::instance()->set_default_scale_to (ratio[s - 1]);
709 void dcp_content_type_changed ()
711 vector<DCPContentType const *> ct = DCPContentType::all ();
712 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
715 void standard_changed ()
717 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
720 wxSpinCtrl* _j2k_bandwidth;
721 wxSpinCtrl* _audio_delay;
722 wxButton* _isdcf_metadata_button;
723 wxSpinCtrl* _still_length;
724 #ifdef DCPOMATIC_USE_OWN_PICKER
725 DirPickerCtrl* _directory;
726 DirPickerCtrl* _kdm_directory;
728 wxDirPickerCtrl* _directory;
729 wxDirPickerCtrl* _kdm_directory;
731 wxChoice* _container;
733 wxChoice* _dcp_content_type;
734 wxChoice* _dcp_audio_channels;
738 class EncodingServersPage : public StandardPage
741 EncodingServersPage (wxSize panel_size, int border)
742 : StandardPage (panel_size, border)
745 wxString GetName () const
751 wxBitmap GetLargeIcon () const
753 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
760 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
761 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
763 vector<string> columns;
764 columns.push_back (wx_to_std (_("IP address / host name")));
765 _servers_list = new EditableList<string, ServerDialog> (
768 boost::bind (&Config::servers, Config::instance()),
769 boost::bind (&Config::set_servers, Config::instance(), _1),
770 boost::bind (&EncodingServersPage::server_column, this, _1)
773 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
775 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
778 void config_changed ()
780 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
781 _servers_list->refresh ();
784 void use_any_servers_changed ()
786 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
789 string server_column (string s)
794 wxCheckBox* _use_any_servers;
795 EditableList<string, ServerDialog>* _servers_list;
798 class CertificateChainEditor : public wxPanel
801 CertificateChainEditor (
805 function<void (shared_ptr<dcp::CertificateChain>)> set,
806 function<shared_ptr<const dcp::CertificateChain> (void)> get,
807 function<void (void)> nag_remake
812 , _nag_remake (nag_remake)
814 wxFont subheading_font (*wxNORMAL_FONT);
815 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
817 _sizer = new wxBoxSizer (wxVERTICAL);
820 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
821 m->SetFont (subheading_font);
822 _sizer->Add (m, 0, wxALL, border);
825 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
826 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
828 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
833 ip.SetText (_("Type"));
835 _certificates->InsertColumn (0, ip);
841 ip.SetText (_("Thumbprint"));
844 wxFont font = ip.GetFont ();
845 font.SetFamily (wxFONTFAMILY_TELETYPE);
848 _certificates->InsertColumn (1, ip);
851 certificates_sizer->Add (_certificates, 1, wxEXPAND);
854 wxSizer* s = new wxBoxSizer (wxVERTICAL);
855 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
856 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
857 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
858 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
859 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
860 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
861 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
864 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
865 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
868 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
869 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
870 wxFont font = _private_key->GetFont ();
871 font.SetFamily (wxFONTFAMILY_TELETYPE);
872 _private_key->SetFont (font);
873 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
874 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
875 table->Add (_load_private_key, wxGBPosition (r, 2));
876 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
877 table->Add (_export_private_key, wxGBPosition (r, 3));
880 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
881 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
882 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
883 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
886 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
887 font = *wxSMALL_FONT;
888 font.SetWeight (wxFONTWEIGHT_BOLD);
889 _private_key_bad->SetFont (font);
890 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
893 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
894 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
895 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
896 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
897 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
898 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
899 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
900 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
902 SetSizerAndFit (_sizer);
905 void config_changed ()
907 _chain.reset (new dcp::CertificateChain (*_get().get ()));
909 update_certificate_list ();
910 update_private_key ();
911 update_sensitivity ();
914 void add_button (wxWindow* button)
916 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
921 void add_certificate ()
923 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
925 if (d->ShowModal() == wxID_OK) {
930 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
931 } catch (boost::filesystem::filesystem_error& e) {
932 error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
937 if (!extra.empty ()) {
940 _("This file contains other certificates (or other data) after its first certificate. "
941 "Only the first certificate will be used.")
945 if (!_chain->chain_valid ()) {
948 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
949 "Add certificates in order from root to intermediate to leaf.")
954 update_certificate_list ();
956 } catch (dcp::MiscError& e) {
957 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
963 update_sensitivity ();
966 void remove_certificate ()
968 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
973 _certificates->DeleteItem (i);
977 update_sensitivity ();
980 void export_certificate ()
982 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
987 wxFileDialog* d = new wxFileDialog (
988 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
989 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
992 dcp::CertificateChain::List all = _chain->root_to_leaf ();
993 dcp::CertificateChain::List::iterator j = all.begin ();
994 for (int k = 0; k < i; ++k) {
998 if (d->ShowModal () == wxID_OK) {
999 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1001 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1004 string const s = j->certificate (true);
1005 fwrite (s.c_str(), 1, s.length(), f);
1011 void update_certificate_list ()
1013 _certificates->DeleteAllItems ();
1015 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
1016 BOOST_FOREACH (dcp::Certificate const & i, certs) {
1019 _certificates->InsertItem (item);
1020 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
1023 _certificates->SetItem (n, 0, _("Root"));
1024 } else if (n == (certs.size() - 1)) {
1025 _certificates->SetItem (n, 0, _("Leaf"));
1027 _certificates->SetItem (n, 0, _("Intermediate"));
1033 static wxColour normal = _private_key_bad->GetForegroundColour ();
1035 if (_chain->private_key_valid ()) {
1036 _private_key_bad->Hide ();
1037 _private_key_bad->SetForegroundColour (normal);
1039 _private_key_bad->Show ();
1040 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
1044 void remake_certificates ()
1046 shared_ptr<const dcp::CertificateChain> chain = _get ();
1048 string subject_organization_name;
1049 string subject_organizational_unit_name;
1050 string root_common_name;
1051 string intermediate_common_name;
1052 string leaf_common_name;
1054 dcp::CertificateChain::List all = chain->root_to_leaf ();
1056 if (all.size() >= 1) {
1058 subject_organization_name = chain->root().subject_organization_name ();
1059 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1060 root_common_name = chain->root().subject_common_name ();
1063 if (all.size() >= 2) {
1065 leaf_common_name = chain->leaf().subject_common_name ();
1068 if (all.size() >= 3) {
1069 /* Have an intermediate */
1070 dcp::CertificateChain::List::iterator i = all.begin ();
1072 intermediate_common_name = i->subject_common_name ();
1077 MakeChainDialog* d = new MakeChainDialog (
1079 subject_organization_name,
1080 subject_organizational_unit_name,
1082 intermediate_common_name,
1086 if (d->ShowModal () == wxID_OK) {
1088 new dcp::CertificateChain (
1091 d->organisational_unit (),
1092 d->root_common_name (),
1093 d->intermediate_common_name (),
1094 d->leaf_common_name ()
1099 update_certificate_list ();
1100 update_private_key ();
1106 void update_sensitivity ()
1108 /* We can only remove the leaf certificate */
1109 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
1110 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1113 void update_private_key ()
1115 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1119 void load_private_key ()
1121 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1123 if (d->ShowModal() == wxID_OK) {
1125 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1126 if (boost::filesystem::file_size (p) > 8192) {
1129 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1134 _chain->set_key (dcp::file_to_string (p));
1136 update_private_key ();
1137 } catch (dcp::MiscError& e) {
1138 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1144 update_sensitivity ();
1147 void export_private_key ()
1149 optional<string> key = _chain->key ();
1154 wxFileDialog* d = new wxFileDialog (
1155 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1156 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1159 if (d->ShowModal () == wxID_OK) {
1160 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1162 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1165 string const s = _chain->key().get ();
1166 fwrite (s.c_str(), 1, s.length(), f);
1172 wxListCtrl* _certificates;
1173 wxButton* _add_certificate;
1174 wxButton* _export_certificate;
1175 wxButton* _remove_certificate;
1176 wxButton* _remake_certificates;
1177 wxStaticText* _private_key;
1178 wxButton* _load_private_key;
1179 wxButton* _export_private_key;
1180 wxStaticText* _private_key_bad;
1182 wxBoxSizer* _button_sizer;
1183 shared_ptr<dcp::CertificateChain> _chain;
1184 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1185 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1186 boost::function<void (void)> _nag_remake;
1189 class KeysPage : public StandardPage
1192 KeysPage (wxSize panel_size, int border)
1193 : StandardPage (panel_size, border)
1196 wxString GetName () const
1201 #ifdef DCPOMATIC_OSX
1202 wxBitmap GetLargeIcon () const
1204 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1212 _signer = new CertificateChainEditor (
1213 _panel, _("Signing DCPs and KDMs"), _border,
1214 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1215 boost::bind (&Config::signer_chain, Config::instance ()),
1216 boost::bind (&do_nothing)
1219 _panel->GetSizer()->Add (_signer);
1221 _decryption = new CertificateChainEditor (
1222 _panel, _("Decrypting KDMs"), _border,
1223 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1224 boost::bind (&Config::decryption_chain, Config::instance ()),
1225 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1228 _panel->GetSizer()->Add (_decryption);
1230 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1231 _decryption->add_button (_export_decryption_certificate);
1232 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1233 _decryption->add_button (_export_decryption_chain);
1235 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1236 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1239 void export_decryption_certificate ()
1241 wxFileDialog* d = new wxFileDialog (
1242 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1243 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1246 if (d->ShowModal () == wxID_OK) {
1247 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1249 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1252 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1253 fwrite (s.c_str(), 1, s.length(), f);
1259 void export_decryption_chain ()
1261 wxFileDialog* d = new wxFileDialog (
1262 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1263 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1266 if (d->ShowModal () == wxID_OK) {
1267 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1269 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1272 string const s = Config::instance()->decryption_chain()->chain();
1273 fwrite (s.c_str(), 1, s.length(), f);
1279 void config_changed ()
1281 _signer->config_changed ();
1282 _decryption->config_changed ();
1285 void nag_remake_decryption_chain ()
1287 NagDialog::maybe_nag (
1289 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1290 _("If you continue with this operation you will no longer be able to use any DKDMs that you have created. Also, any KDMs that have been sent to you will become useless. Proceed with caution!")
1294 CertificateChainEditor* _signer;
1295 CertificateChainEditor* _decryption;
1296 wxButton* _export_decryption_certificate;
1297 wxButton* _export_decryption_chain;
1300 class TMSPage : public StandardPage
1303 TMSPage (wxSize panel_size, int border)
1304 : StandardPage (panel_size, border)
1307 wxString GetName () const
1312 #ifdef DCPOMATIC_OSX
1313 wxBitmap GetLargeIcon () const
1315 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1322 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1323 table->AddGrowableCol (1, 1);
1324 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1326 add_label_to_sizer (table, _panel, _("Protocol"), true);
1327 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1328 table->Add (_tms_protocol, 1, wxEXPAND);
1330 add_label_to_sizer (table, _panel, _("IP address"), true);
1331 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1332 table->Add (_tms_ip, 1, wxEXPAND);
1334 add_label_to_sizer (table, _panel, _("Target path"), true);
1335 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1336 table->Add (_tms_path, 1, wxEXPAND);
1338 add_label_to_sizer (table, _panel, _("User name"), true);
1339 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1340 table->Add (_tms_user, 1, wxEXPAND);
1342 add_label_to_sizer (table, _panel, _("Password"), true);
1343 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1344 table->Add (_tms_password, 1, wxEXPAND);
1346 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1347 _tms_protocol->Append (_("FTP (for Dolby)"));
1349 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1350 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1351 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1352 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1353 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1356 void config_changed ()
1358 Config* config = Config::instance ();
1360 checked_set (_tms_protocol, config->tms_protocol ());
1361 checked_set (_tms_ip, config->tms_ip ());
1362 checked_set (_tms_path, config->tms_path ());
1363 checked_set (_tms_user, config->tms_user ());
1364 checked_set (_tms_password, config->tms_password ());
1367 void tms_protocol_changed ()
1369 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1372 void tms_ip_changed ()
1374 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1377 void tms_path_changed ()
1379 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1382 void tms_user_changed ()
1384 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1387 void tms_password_changed ()
1389 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1392 wxChoice* _tms_protocol;
1393 wxTextCtrl* _tms_ip;
1394 wxTextCtrl* _tms_path;
1395 wxTextCtrl* _tms_user;
1396 wxTextCtrl* _tms_password;
1405 class KDMEmailPage : public StandardPage
1409 KDMEmailPage (wxSize panel_size, int border)
1410 #ifdef DCPOMATIC_OSX
1411 /* We have to force both width and height of this one */
1412 : StandardPage (wxSize (480, 128), border)
1414 : StandardPage (panel_size, border)
1418 wxString GetName () const
1420 return _("KDM Email");
1423 #ifdef DCPOMATIC_OSX
1424 wxBitmap GetLargeIcon () const
1426 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1433 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1434 table->AddGrowableCol (1, 1);
1435 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1437 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1439 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1440 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1441 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1442 add_label_to_sizer (s, _panel, _("port"), false);
1443 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1444 _mail_port->SetRange (0, 65535);
1445 s->Add (_mail_port);
1446 table->Add (s, 1, wxEXPAND | wxALL);
1449 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1450 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1451 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1453 add_label_to_sizer (table, _panel, _("Mail password"), true);
1454 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1455 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1457 add_label_to_sizer (table, _panel, _("Subject"), true);
1458 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1459 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1461 add_label_to_sizer (table, _panel, _("From address"), true);
1462 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1463 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1465 vector<string> columns;
1466 columns.push_back (wx_to_std (_("Address")));
1467 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1468 _kdm_cc = new EditableList<string, EmailDialog> (
1471 bind (&Config::kdm_cc, Config::instance()),
1472 bind (&Config::set_kdm_cc, Config::instance(), _1),
1475 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1477 add_label_to_sizer (table, _panel, _("BCC address"), true);
1478 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1479 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1481 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1482 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1484 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1485 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1489 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1490 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1491 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1492 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1493 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1494 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1495 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1496 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1497 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1500 void config_changed ()
1502 Config* config = Config::instance ();
1504 checked_set (_mail_server, config->mail_server ());
1505 checked_set (_mail_port, config->mail_port ());
1506 checked_set (_mail_user, config->mail_user ());
1507 checked_set (_mail_password, config->mail_password ());
1508 checked_set (_kdm_subject, config->kdm_subject ());
1509 checked_set (_kdm_from, config->kdm_from ());
1510 checked_set (_kdm_bcc, config->kdm_bcc ());
1511 checked_set (_kdm_email, Config::instance()->kdm_email ());
1514 void mail_server_changed ()
1516 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1519 void mail_port_changed ()
1521 Config::instance()->set_mail_port (_mail_port->GetValue ());
1524 void mail_user_changed ()
1526 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1529 void mail_password_changed ()
1531 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1534 void kdm_subject_changed ()
1536 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1539 void kdm_from_changed ()
1541 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1544 void kdm_bcc_changed ()
1546 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1549 void kdm_email_changed ()
1551 if (_kdm_email->GetValue().IsEmpty ()) {
1552 /* Sometimes we get sent an erroneous notification that the email
1553 is empty; I don't know why.
1557 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1560 void reset_kdm_email ()
1562 Config::instance()->reset_kdm_email ();
1563 checked_set (_kdm_email, Config::instance()->kdm_email ());
1566 wxTextCtrl* _mail_server;
1567 wxSpinCtrl* _mail_port;
1568 wxTextCtrl* _mail_user;
1569 wxTextCtrl* _mail_password;
1570 wxTextCtrl* _kdm_subject;
1571 wxTextCtrl* _kdm_from;
1572 EditableList<string, EmailDialog>* _kdm_cc;
1573 wxTextCtrl* _kdm_bcc;
1574 wxTextCtrl* _kdm_email;
1575 wxButton* _reset_kdm_email;
1578 class CoverSheetPage : public StandardPage
1582 CoverSheetPage (wxSize panel_size, int border)
1583 #ifdef DCPOMATIC_OSX
1584 /* We have to force both width and height of this one */
1585 : StandardPage (wxSize (480, 128), border)
1587 : StandardPage (panel_size, border)
1591 wxString GetName () const
1593 return _("Cover Sheet");
1596 #ifdef DCPOMATIC_OSX
1597 wxBitmap GetLargeIcon () const
1599 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1606 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1607 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1609 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1610 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1612 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1613 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1616 void config_changed ()
1618 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1621 void cover_sheet_changed ()
1623 if (_cover_sheet->GetValue().IsEmpty ()) {
1624 /* Sometimes we get sent an erroneous notification that the cover sheet
1625 is empty; I don't know why.
1629 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1632 void reset_cover_sheet ()
1634 Config::instance()->reset_cover_sheet ();
1635 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1638 wxTextCtrl* _cover_sheet;
1639 wxButton* _reset_cover_sheet;
1643 /** @class AdvancedPage
1644 * @brief Advanced page of the preferences dialog.
1646 class AdvancedPage : public StockPage
1649 AdvancedPage (wxSize panel_size, int border)
1650 : StockPage (Kind_Advanced, panel_size, border)
1651 , _maximum_j2k_bandwidth (0)
1652 , _allow_any_dcp_frame_rate (0)
1653 , _only_servers_encode (0)
1658 , _log_debug_decode (0)
1659 , _log_debug_encode (0)
1660 , _log_debug_email (0)
1664 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1666 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1668 flags |= wxALIGN_RIGHT;
1671 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1672 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1677 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1678 table->AddGrowableCol (1, 1);
1679 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1682 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1683 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1684 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1685 s->Add (_maximum_j2k_bandwidth, 1);
1686 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1690 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1691 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1692 table->AddSpacer (0);
1694 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1695 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1696 table->AddSpacer (0);
1699 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1700 dcp::NameFormat::Map titles;
1701 titles['t'] = "type (cpl/pkl)";
1702 dcp::NameFormat::Map examples;
1703 examples['t'] = "cpl";
1704 _dcp_metadata_filename_format = new NameFormatEditor (
1705 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1707 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1711 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1712 dcp::NameFormat::Map titles;
1713 titles['t'] = "type (j2c/pcm/sub)";
1714 titles['r'] = "reel number";
1715 titles['n'] = "number of reels";
1716 titles['c'] = "content filename";
1717 dcp::NameFormat::Map examples;
1718 examples['t'] = "j2c";
1719 examples['r'] = "1";
1720 examples['n'] = "4";
1721 examples['c'] = "myfile.mp4";
1722 _dcp_asset_filename_format = new NameFormatEditor (
1723 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1725 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1729 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1730 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1731 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1732 t->Add (_log_general, 1, wxEXPAND | wxALL);
1733 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1734 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1735 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1736 t->Add (_log_error, 1, wxEXPAND | wxALL);
1737 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1738 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1739 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1740 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1741 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1742 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1743 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1744 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1745 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1746 table->Add (t, 0, wxALL, 6);
1749 #ifdef DCPOMATIC_WINDOWS
1750 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1751 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1752 table->AddSpacer (0);
1755 _maximum_j2k_bandwidth->SetRange (1, 1000);
1756 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1757 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1758 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1759 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1760 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1761 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1762 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1763 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1764 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1765 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1766 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1767 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1768 #ifdef DCPOMATIC_WINDOWS
1769 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1773 void config_changed ()
1775 Config* config = Config::instance ();
1777 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1778 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1779 checked_set (_only_servers_encode, config->only_servers_encode ());
1780 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1781 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1782 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1783 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1784 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1785 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1786 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1787 #ifdef DCPOMATIC_WINDOWS
1788 checked_set (_win32_console, config->win32_console());
1792 void maximum_j2k_bandwidth_changed ()
1794 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1797 void allow_any_dcp_frame_rate_changed ()
1799 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1802 void only_servers_encode_changed ()
1804 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1807 void dcp_metadata_filename_format_changed ()
1809 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1812 void dcp_asset_filename_format_changed ()
1814 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1820 if (_log_general->GetValue ()) {
1821 types |= LogEntry::TYPE_GENERAL;
1823 if (_log_warning->GetValue ()) {
1824 types |= LogEntry::TYPE_WARNING;
1826 if (_log_error->GetValue ()) {
1827 types |= LogEntry::TYPE_ERROR;
1829 if (_log_timing->GetValue ()) {
1830 types |= LogEntry::TYPE_TIMING;
1832 if (_log_debug_decode->GetValue ()) {
1833 types |= LogEntry::TYPE_DEBUG_DECODE;
1835 if (_log_debug_encode->GetValue ()) {
1836 types |= LogEntry::TYPE_DEBUG_ENCODE;
1838 if (_log_debug_email->GetValue ()) {
1839 types |= LogEntry::TYPE_DEBUG_EMAIL;
1841 Config::instance()->set_log_types (types);
1844 #ifdef DCPOMATIC_WINDOWS
1845 void win32_console_changed ()
1847 Config::instance()->set_win32_console (_win32_console->GetValue ());
1851 wxSpinCtrl* _maximum_j2k_bandwidth;
1852 wxCheckBox* _allow_any_dcp_frame_rate;
1853 wxCheckBox* _only_servers_encode;
1854 NameFormatEditor* _dcp_metadata_filename_format;
1855 NameFormatEditor* _dcp_asset_filename_format;
1856 wxCheckBox* _log_general;
1857 wxCheckBox* _log_warning;
1858 wxCheckBox* _log_error;
1859 wxCheckBox* _log_timing;
1860 wxCheckBox* _log_debug_decode;
1861 wxCheckBox* _log_debug_encode;
1862 wxCheckBox* _log_debug_email;
1863 #ifdef DCPOMATIC_WINDOWS
1864 wxCheckBox* _win32_console;
1868 wxPreferencesEditor*
1869 create_config_dialog ()
1871 wxPreferencesEditor* e = new wxPreferencesEditor ();
1873 #ifdef DCPOMATIC_OSX
1874 /* Width that we force some of the config panels to be on OSX so that
1875 the containing window doesn't shrink too much when we select those panels.
1876 This is obviously an unpleasant hack.
1878 wxSize ps = wxSize (520, -1);
1879 int const border = 16;
1881 wxSize ps = wxSize (-1, -1);
1882 int const border = 8;
1885 e->AddPage (new GeneralPage (ps, border));
1886 e->AddPage (new DefaultsPage (ps, border));
1887 e->AddPage (new EncodingServersPage (ps, border));
1888 e->AddPage (new KeysPage (ps, border));
1889 e->AddPage (new TMSPage (ps, border));
1890 e->AddPage (new KDMEmailPage (ps, border));
1891 e->AddPage (new CoverSheetPage (ps, border));
1892 e->AddPage (new AdvancedPage (ps, border));