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 *> containers = Ratio::containers ();
619 for (size_t i = 0; i < containers.size(); ++i) {
620 if (containers[i] == config->default_container ()) {
621 _container->SetSelection (i);
625 vector<Ratio const *> ratios = Ratio::all ();
626 for (size_t i = 0; i < ratios.size(); ++i) {
627 if (ratios[i] == config->default_scale_to ()) {
628 _scale_to->SetSelection (i + 1);
632 if (!config->default_scale_to()) {
633 _scale_to->SetSelection (0);
636 vector<DCPContentType const *> const ct = DCPContentType::all ();
637 for (size_t i = 0; i < ct.size(); ++i) {
638 if (ct[i] == config->default_dcp_content_type ()) {
639 _dcp_content_type->SetSelection (i);
643 checked_set (_still_length, config->default_still_length ());
644 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
645 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
646 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
647 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
648 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
649 checked_set (_audio_delay, config->default_audio_delay ());
650 checked_set (_standard, config->default_interop() ? 1 : 0);
653 void j2k_bandwidth_changed ()
655 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
658 void audio_delay_changed ()
660 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
663 void dcp_audio_channels_changed ()
665 int const s = _dcp_audio_channels->GetSelection ();
666 if (s != wxNOT_FOUND) {
667 Config::instance()->set_default_dcp_audio_channels (
668 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
673 void directory_changed ()
675 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
678 void kdm_directory_changed ()
680 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
683 void edit_isdcf_metadata_clicked ()
685 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
687 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
691 void still_length_changed ()
693 Config::instance()->set_default_still_length (_still_length->GetValue ());
696 void container_changed ()
698 vector<Ratio const *> ratio = Ratio::containers ();
699 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
702 void scale_to_changed ()
704 int const s = _scale_to->GetSelection ();
706 Config::instance()->set_default_scale_to (0);
708 vector<Ratio const *> ratio = Ratio::all ();
709 Config::instance()->set_default_scale_to (ratio[s - 1]);
713 void dcp_content_type_changed ()
715 vector<DCPContentType const *> ct = DCPContentType::all ();
716 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
719 void standard_changed ()
721 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
724 wxSpinCtrl* _j2k_bandwidth;
725 wxSpinCtrl* _audio_delay;
726 wxButton* _isdcf_metadata_button;
727 wxSpinCtrl* _still_length;
728 #ifdef DCPOMATIC_USE_OWN_PICKER
729 DirPickerCtrl* _directory;
730 DirPickerCtrl* _kdm_directory;
732 wxDirPickerCtrl* _directory;
733 wxDirPickerCtrl* _kdm_directory;
735 wxChoice* _container;
737 wxChoice* _dcp_content_type;
738 wxChoice* _dcp_audio_channels;
742 class EncodingServersPage : public StandardPage
745 EncodingServersPage (wxSize panel_size, int border)
746 : StandardPage (panel_size, border)
749 wxString GetName () const
755 wxBitmap GetLargeIcon () const
757 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
764 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
765 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
767 vector<string> columns;
768 columns.push_back (wx_to_std (_("IP address / host name")));
769 _servers_list = new EditableList<string, ServerDialog> (
772 boost::bind (&Config::servers, Config::instance()),
773 boost::bind (&Config::set_servers, Config::instance(), _1),
774 boost::bind (&EncodingServersPage::server_column, this, _1)
777 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
779 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
782 void config_changed ()
784 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
785 _servers_list->refresh ();
788 void use_any_servers_changed ()
790 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
793 string server_column (string s)
798 wxCheckBox* _use_any_servers;
799 EditableList<string, ServerDialog>* _servers_list;
802 class CertificateChainEditor : public wxPanel
805 CertificateChainEditor (
809 function<void (shared_ptr<dcp::CertificateChain>)> set,
810 function<shared_ptr<const dcp::CertificateChain> (void)> get,
811 function<void (void)> nag_remake
816 , _nag_remake (nag_remake)
818 wxFont subheading_font (*wxNORMAL_FONT);
819 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
821 _sizer = new wxBoxSizer (wxVERTICAL);
824 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
825 m->SetFont (subheading_font);
826 _sizer->Add (m, 0, wxALL, border);
829 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
830 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
832 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
837 ip.SetText (_("Type"));
839 _certificates->InsertColumn (0, ip);
845 ip.SetText (_("Thumbprint"));
848 wxFont font = ip.GetFont ();
849 font.SetFamily (wxFONTFAMILY_TELETYPE);
852 _certificates->InsertColumn (1, ip);
855 certificates_sizer->Add (_certificates, 1, wxEXPAND);
858 wxSizer* s = new wxBoxSizer (wxVERTICAL);
859 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
860 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
861 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
862 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
863 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
864 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
865 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
868 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
869 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
872 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
873 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
874 wxFont font = _private_key->GetFont ();
875 font.SetFamily (wxFONTFAMILY_TELETYPE);
876 _private_key->SetFont (font);
877 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
878 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
879 table->Add (_load_private_key, wxGBPosition (r, 2));
880 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
881 table->Add (_export_private_key, wxGBPosition (r, 3));
884 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
885 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
886 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
887 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
890 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
891 font = *wxSMALL_FONT;
892 font.SetWeight (wxFONTWEIGHT_BOLD);
893 _private_key_bad->SetFont (font);
894 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
897 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
898 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
899 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
900 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
901 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
902 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
903 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
904 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
906 SetSizerAndFit (_sizer);
909 void config_changed ()
911 _chain.reset (new dcp::CertificateChain (*_get().get ()));
913 update_certificate_list ();
914 update_private_key ();
915 update_sensitivity ();
918 void add_button (wxWindow* button)
920 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
925 void add_certificate ()
927 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
929 if (d->ShowModal() == wxID_OK) {
934 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
935 } catch (boost::filesystem::filesystem_error& e) {
936 error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
941 if (!extra.empty ()) {
944 _("This file contains other certificates (or other data) after its first certificate. "
945 "Only the first certificate will be used.")
949 if (!_chain->chain_valid ()) {
952 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
953 "Add certificates in order from root to intermediate to leaf.")
958 update_certificate_list ();
960 } catch (dcp::MiscError& e) {
961 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
967 update_sensitivity ();
970 void remove_certificate ()
972 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
977 _certificates->DeleteItem (i);
981 update_sensitivity ();
984 void export_certificate ()
986 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
991 wxFileDialog* d = new wxFileDialog (
992 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
993 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
996 dcp::CertificateChain::List all = _chain->root_to_leaf ();
997 dcp::CertificateChain::List::iterator j = all.begin ();
998 for (int k = 0; k < i; ++k) {
1002 if (d->ShowModal () == wxID_OK) {
1003 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1005 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1008 string const s = j->certificate (true);
1009 fwrite (s.c_str(), 1, s.length(), f);
1015 void update_certificate_list ()
1017 _certificates->DeleteAllItems ();
1019 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
1020 BOOST_FOREACH (dcp::Certificate const & i, certs) {
1023 _certificates->InsertItem (item);
1024 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
1027 _certificates->SetItem (n, 0, _("Root"));
1028 } else if (n == (certs.size() - 1)) {
1029 _certificates->SetItem (n, 0, _("Leaf"));
1031 _certificates->SetItem (n, 0, _("Intermediate"));
1037 static wxColour normal = _private_key_bad->GetForegroundColour ();
1039 if (_chain->private_key_valid ()) {
1040 _private_key_bad->Hide ();
1041 _private_key_bad->SetForegroundColour (normal);
1043 _private_key_bad->Show ();
1044 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
1048 void remake_certificates ()
1050 shared_ptr<const dcp::CertificateChain> chain = _get ();
1052 string subject_organization_name;
1053 string subject_organizational_unit_name;
1054 string root_common_name;
1055 string intermediate_common_name;
1056 string leaf_common_name;
1058 dcp::CertificateChain::List all = chain->root_to_leaf ();
1060 if (all.size() >= 1) {
1062 subject_organization_name = chain->root().subject_organization_name ();
1063 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1064 root_common_name = chain->root().subject_common_name ();
1067 if (all.size() >= 2) {
1069 leaf_common_name = chain->leaf().subject_common_name ();
1072 if (all.size() >= 3) {
1073 /* Have an intermediate */
1074 dcp::CertificateChain::List::iterator i = all.begin ();
1076 intermediate_common_name = i->subject_common_name ();
1081 MakeChainDialog* d = new MakeChainDialog (
1083 subject_organization_name,
1084 subject_organizational_unit_name,
1086 intermediate_common_name,
1090 if (d->ShowModal () == wxID_OK) {
1092 new dcp::CertificateChain (
1095 d->organisational_unit (),
1096 d->root_common_name (),
1097 d->intermediate_common_name (),
1098 d->leaf_common_name ()
1103 update_certificate_list ();
1104 update_private_key ();
1110 void update_sensitivity ()
1112 /* We can only remove the leaf certificate */
1113 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
1114 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1117 void update_private_key ()
1119 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1123 void load_private_key ()
1125 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1127 if (d->ShowModal() == wxID_OK) {
1129 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1130 if (boost::filesystem::file_size (p) > 8192) {
1133 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1138 _chain->set_key (dcp::file_to_string (p));
1140 update_private_key ();
1141 } catch (dcp::MiscError& e) {
1142 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1148 update_sensitivity ();
1151 void export_private_key ()
1153 optional<string> key = _chain->key ();
1158 wxFileDialog* d = new wxFileDialog (
1159 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1160 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1163 if (d->ShowModal () == wxID_OK) {
1164 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1166 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1169 string const s = _chain->key().get ();
1170 fwrite (s.c_str(), 1, s.length(), f);
1176 wxListCtrl* _certificates;
1177 wxButton* _add_certificate;
1178 wxButton* _export_certificate;
1179 wxButton* _remove_certificate;
1180 wxButton* _remake_certificates;
1181 wxStaticText* _private_key;
1182 wxButton* _load_private_key;
1183 wxButton* _export_private_key;
1184 wxStaticText* _private_key_bad;
1186 wxBoxSizer* _button_sizer;
1187 shared_ptr<dcp::CertificateChain> _chain;
1188 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1189 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1190 boost::function<void (void)> _nag_remake;
1193 class KeysPage : public StandardPage
1196 KeysPage (wxSize panel_size, int border)
1197 : StandardPage (panel_size, border)
1200 wxString GetName () const
1205 #ifdef DCPOMATIC_OSX
1206 wxBitmap GetLargeIcon () const
1208 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1216 _signer = new CertificateChainEditor (
1217 _panel, _("Signing DCPs and KDMs"), _border,
1218 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1219 boost::bind (&Config::signer_chain, Config::instance ()),
1220 boost::bind (&do_nothing)
1223 _panel->GetSizer()->Add (_signer);
1225 _decryption = new CertificateChainEditor (
1226 _panel, _("Decrypting KDMs"), _border,
1227 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1228 boost::bind (&Config::decryption_chain, Config::instance ()),
1229 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1232 _panel->GetSizer()->Add (_decryption);
1234 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1235 _decryption->add_button (_export_decryption_certificate);
1236 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1237 _decryption->add_button (_export_decryption_chain);
1239 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1240 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1243 void export_decryption_certificate ()
1245 wxFileDialog* d = new wxFileDialog (
1246 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1247 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1250 if (d->ShowModal () == wxID_OK) {
1251 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1253 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1256 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1257 fwrite (s.c_str(), 1, s.length(), f);
1263 void export_decryption_chain ()
1265 wxFileDialog* d = new wxFileDialog (
1266 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1267 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1270 if (d->ShowModal () == wxID_OK) {
1271 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1273 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1276 string const s = Config::instance()->decryption_chain()->chain();
1277 fwrite (s.c_str(), 1, s.length(), f);
1283 void config_changed ()
1285 _signer->config_changed ();
1286 _decryption->config_changed ();
1289 void nag_remake_decryption_chain ()
1291 NagDialog::maybe_nag (
1293 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1294 _("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!")
1298 CertificateChainEditor* _signer;
1299 CertificateChainEditor* _decryption;
1300 wxButton* _export_decryption_certificate;
1301 wxButton* _export_decryption_chain;
1304 class TMSPage : public StandardPage
1307 TMSPage (wxSize panel_size, int border)
1308 : StandardPage (panel_size, border)
1311 wxString GetName () const
1316 #ifdef DCPOMATIC_OSX
1317 wxBitmap GetLargeIcon () const
1319 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1326 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1327 table->AddGrowableCol (1, 1);
1328 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1330 add_label_to_sizer (table, _panel, _("Protocol"), true);
1331 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1332 table->Add (_tms_protocol, 1, wxEXPAND);
1334 add_label_to_sizer (table, _panel, _("IP address"), true);
1335 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1336 table->Add (_tms_ip, 1, wxEXPAND);
1338 add_label_to_sizer (table, _panel, _("Target path"), true);
1339 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1340 table->Add (_tms_path, 1, wxEXPAND);
1342 add_label_to_sizer (table, _panel, _("User name"), true);
1343 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1344 table->Add (_tms_user, 1, wxEXPAND);
1346 add_label_to_sizer (table, _panel, _("Password"), true);
1347 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1348 table->Add (_tms_password, 1, wxEXPAND);
1350 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1351 _tms_protocol->Append (_("FTP (for Dolby)"));
1353 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1354 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1355 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1356 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1357 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1360 void config_changed ()
1362 Config* config = Config::instance ();
1364 checked_set (_tms_protocol, config->tms_protocol ());
1365 checked_set (_tms_ip, config->tms_ip ());
1366 checked_set (_tms_path, config->tms_path ());
1367 checked_set (_tms_user, config->tms_user ());
1368 checked_set (_tms_password, config->tms_password ());
1371 void tms_protocol_changed ()
1373 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1376 void tms_ip_changed ()
1378 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1381 void tms_path_changed ()
1383 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1386 void tms_user_changed ()
1388 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1391 void tms_password_changed ()
1393 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1396 wxChoice* _tms_protocol;
1397 wxTextCtrl* _tms_ip;
1398 wxTextCtrl* _tms_path;
1399 wxTextCtrl* _tms_user;
1400 wxTextCtrl* _tms_password;
1409 class KDMEmailPage : public StandardPage
1413 KDMEmailPage (wxSize panel_size, int border)
1414 #ifdef DCPOMATIC_OSX
1415 /* We have to force both width and height of this one */
1416 : StandardPage (wxSize (480, 128), border)
1418 : StandardPage (panel_size, border)
1422 wxString GetName () const
1424 return _("KDM Email");
1427 #ifdef DCPOMATIC_OSX
1428 wxBitmap GetLargeIcon () const
1430 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1437 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1438 table->AddGrowableCol (1, 1);
1439 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1441 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1443 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1444 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1445 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1446 add_label_to_sizer (s, _panel, _("port"), false);
1447 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1448 _mail_port->SetRange (0, 65535);
1449 s->Add (_mail_port);
1450 table->Add (s, 1, wxEXPAND | wxALL);
1453 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1454 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1455 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1457 add_label_to_sizer (table, _panel, _("Mail password"), true);
1458 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1459 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1461 add_label_to_sizer (table, _panel, _("Subject"), true);
1462 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1463 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1465 add_label_to_sizer (table, _panel, _("From address"), true);
1466 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1467 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1469 vector<string> columns;
1470 columns.push_back (wx_to_std (_("Address")));
1471 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1472 _kdm_cc = new EditableList<string, EmailDialog> (
1475 bind (&Config::kdm_cc, Config::instance()),
1476 bind (&Config::set_kdm_cc, Config::instance(), _1),
1479 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1481 add_label_to_sizer (table, _panel, _("BCC address"), true);
1482 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1483 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1485 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1486 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1488 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1489 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1493 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1494 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1495 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1496 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1497 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1498 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1499 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1500 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1501 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1504 void config_changed ()
1506 Config* config = Config::instance ();
1508 checked_set (_mail_server, config->mail_server ());
1509 checked_set (_mail_port, config->mail_port ());
1510 checked_set (_mail_user, config->mail_user ());
1511 checked_set (_mail_password, config->mail_password ());
1512 checked_set (_kdm_subject, config->kdm_subject ());
1513 checked_set (_kdm_from, config->kdm_from ());
1514 checked_set (_kdm_bcc, config->kdm_bcc ());
1515 checked_set (_kdm_email, Config::instance()->kdm_email ());
1518 void mail_server_changed ()
1520 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1523 void mail_port_changed ()
1525 Config::instance()->set_mail_port (_mail_port->GetValue ());
1528 void mail_user_changed ()
1530 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1533 void mail_password_changed ()
1535 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1538 void kdm_subject_changed ()
1540 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1543 void kdm_from_changed ()
1545 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1548 void kdm_bcc_changed ()
1550 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1553 void kdm_email_changed ()
1555 if (_kdm_email->GetValue().IsEmpty ()) {
1556 /* Sometimes we get sent an erroneous notification that the email
1557 is empty; I don't know why.
1561 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1564 void reset_kdm_email ()
1566 Config::instance()->reset_kdm_email ();
1567 checked_set (_kdm_email, Config::instance()->kdm_email ());
1570 wxTextCtrl* _mail_server;
1571 wxSpinCtrl* _mail_port;
1572 wxTextCtrl* _mail_user;
1573 wxTextCtrl* _mail_password;
1574 wxTextCtrl* _kdm_subject;
1575 wxTextCtrl* _kdm_from;
1576 EditableList<string, EmailDialog>* _kdm_cc;
1577 wxTextCtrl* _kdm_bcc;
1578 wxTextCtrl* _kdm_email;
1579 wxButton* _reset_kdm_email;
1582 class CoverSheetPage : public StandardPage
1586 CoverSheetPage (wxSize panel_size, int border)
1587 #ifdef DCPOMATIC_OSX
1588 /* We have to force both width and height of this one */
1589 : StandardPage (wxSize (480, 128), border)
1591 : StandardPage (panel_size, border)
1595 wxString GetName () const
1597 return _("Cover Sheet");
1600 #ifdef DCPOMATIC_OSX
1601 wxBitmap GetLargeIcon () const
1603 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1610 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1611 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1613 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1614 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1616 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1617 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1620 void config_changed ()
1622 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1625 void cover_sheet_changed ()
1627 if (_cover_sheet->GetValue().IsEmpty ()) {
1628 /* Sometimes we get sent an erroneous notification that the cover sheet
1629 is empty; I don't know why.
1633 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1636 void reset_cover_sheet ()
1638 Config::instance()->reset_cover_sheet ();
1639 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1642 wxTextCtrl* _cover_sheet;
1643 wxButton* _reset_cover_sheet;
1647 /** @class AdvancedPage
1648 * @brief Advanced page of the preferences dialog.
1650 class AdvancedPage : public StockPage
1653 AdvancedPage (wxSize panel_size, int border)
1654 : StockPage (Kind_Advanced, panel_size, border)
1655 , _maximum_j2k_bandwidth (0)
1656 , _allow_any_dcp_frame_rate (0)
1657 , _only_servers_encode (0)
1662 , _log_debug_decode (0)
1663 , _log_debug_encode (0)
1664 , _log_debug_email (0)
1668 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1670 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1672 flags |= wxALIGN_RIGHT;
1675 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1676 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1681 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1682 table->AddGrowableCol (1, 1);
1683 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1686 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1687 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1688 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1689 s->Add (_maximum_j2k_bandwidth, 1);
1690 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1694 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1695 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1696 table->AddSpacer (0);
1698 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1699 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1700 table->AddSpacer (0);
1703 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1704 dcp::NameFormat::Map titles;
1705 titles['t'] = "type (cpl/pkl)";
1706 dcp::NameFormat::Map examples;
1707 examples['t'] = "cpl";
1708 _dcp_metadata_filename_format = new NameFormatEditor (
1709 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1711 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1715 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1716 dcp::NameFormat::Map titles;
1717 titles['t'] = "type (j2c/pcm/sub)";
1718 titles['r'] = "reel number";
1719 titles['n'] = "number of reels";
1720 titles['c'] = "content filename";
1721 dcp::NameFormat::Map examples;
1722 examples['t'] = "j2c";
1723 examples['r'] = "1";
1724 examples['n'] = "4";
1725 examples['c'] = "myfile.mp4";
1726 _dcp_asset_filename_format = new NameFormatEditor (
1727 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1729 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1733 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1734 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1735 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1736 t->Add (_log_general, 1, wxEXPAND | wxALL);
1737 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1738 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1739 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1740 t->Add (_log_error, 1, wxEXPAND | wxALL);
1741 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1742 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1743 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1744 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1745 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1746 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1747 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1748 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1749 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1750 table->Add (t, 0, wxALL, 6);
1753 #ifdef DCPOMATIC_WINDOWS
1754 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1755 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1756 table->AddSpacer (0);
1759 _maximum_j2k_bandwidth->SetRange (1, 1000);
1760 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1761 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1762 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1763 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1764 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1765 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1766 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1767 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1768 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1769 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1770 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1771 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1772 #ifdef DCPOMATIC_WINDOWS
1773 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1777 void config_changed ()
1779 Config* config = Config::instance ();
1781 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1782 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1783 checked_set (_only_servers_encode, config->only_servers_encode ());
1784 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1785 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1786 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1787 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1788 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1789 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1790 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1791 #ifdef DCPOMATIC_WINDOWS
1792 checked_set (_win32_console, config->win32_console());
1796 void maximum_j2k_bandwidth_changed ()
1798 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1801 void allow_any_dcp_frame_rate_changed ()
1803 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1806 void only_servers_encode_changed ()
1808 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1811 void dcp_metadata_filename_format_changed ()
1813 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1816 void dcp_asset_filename_format_changed ()
1818 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1824 if (_log_general->GetValue ()) {
1825 types |= LogEntry::TYPE_GENERAL;
1827 if (_log_warning->GetValue ()) {
1828 types |= LogEntry::TYPE_WARNING;
1830 if (_log_error->GetValue ()) {
1831 types |= LogEntry::TYPE_ERROR;
1833 if (_log_timing->GetValue ()) {
1834 types |= LogEntry::TYPE_TIMING;
1836 if (_log_debug_decode->GetValue ()) {
1837 types |= LogEntry::TYPE_DEBUG_DECODE;
1839 if (_log_debug_encode->GetValue ()) {
1840 types |= LogEntry::TYPE_DEBUG_ENCODE;
1842 if (_log_debug_email->GetValue ()) {
1843 types |= LogEntry::TYPE_DEBUG_EMAIL;
1845 Config::instance()->set_log_types (types);
1848 #ifdef DCPOMATIC_WINDOWS
1849 void win32_console_changed ()
1851 Config::instance()->set_win32_console (_win32_console->GetValue ());
1855 wxSpinCtrl* _maximum_j2k_bandwidth;
1856 wxCheckBox* _allow_any_dcp_frame_rate;
1857 wxCheckBox* _only_servers_encode;
1858 NameFormatEditor* _dcp_metadata_filename_format;
1859 NameFormatEditor* _dcp_asset_filename_format;
1860 wxCheckBox* _log_general;
1861 wxCheckBox* _log_warning;
1862 wxCheckBox* _log_error;
1863 wxCheckBox* _log_timing;
1864 wxCheckBox* _log_debug_decode;
1865 wxCheckBox* _log_debug_encode;
1866 wxCheckBox* _log_debug_email;
1867 #ifdef DCPOMATIC_WINDOWS
1868 wxCheckBox* _win32_console;
1872 wxPreferencesEditor*
1873 create_config_dialog ()
1875 wxPreferencesEditor* e = new wxPreferencesEditor ();
1877 #ifdef DCPOMATIC_OSX
1878 /* Width that we force some of the config panels to be on OSX so that
1879 the containing window doesn't shrink too much when we select those panels.
1880 This is obviously an unpleasant hack.
1882 wxSize ps = wxSize (520, -1);
1883 int const border = 16;
1885 wxSize ps = wxSize (-1, -1);
1886 int const border = 8;
1889 e->AddPage (new GeneralPage (ps, border));
1890 e->AddPage (new DefaultsPage (ps, border));
1891 e->AddPage (new EncodingServersPage (ps, border));
1892 e->AddPage (new KeysPage (ps, border));
1893 e->AddPage (new TMSPage (ps, border));
1894 e->AddPage (new KDMEmailPage (ps, border));
1895 e->AddPage (new CoverSheetPage (ps, border));
1896 e->AddPage (new AdvancedPage (ps, border));