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 vector<Ratio const *> ratios = Ratio::all ();
581 for (size_t i = 0; i < ratios.size(); ++i) {
582 _container->Append (std_to_wx (ratios[i]->nickname ()));
585 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
587 _scale_to->Append (_("Guess from content"));
589 for (size_t i = 0; i < ratios.size(); ++i) {
590 _scale_to->Append (std_to_wx (ratios[i]->nickname ()));
593 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
595 vector<DCPContentType const *> const ct = DCPContentType::all ();
596 for (size_t i = 0; i < ct.size(); ++i) {
597 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
600 setup_audio_channels_choice (_dcp_audio_channels, 2);
602 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
603 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
605 _j2k_bandwidth->SetRange (50, 250);
606 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
608 _audio_delay->SetRange (-1000, 1000);
609 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
611 _standard->Append (_("SMPTE"));
612 _standard->Append (_("Interop"));
613 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
616 void config_changed ()
618 Config* config = Config::instance ();
620 vector<Ratio const *> ratios = Ratio::all ();
621 for (size_t i = 0; i < ratios.size(); ++i) {
622 if (ratios[i] == config->default_container ()) {
623 _container->SetSelection (i);
625 if (ratios[i] == config->default_scale_to ()) {
626 _scale_to->SetSelection (i + 1);
630 if (!config->default_scale_to()) {
631 _scale_to->SetSelection (0);
634 vector<DCPContentType const *> const ct = DCPContentType::all ();
635 for (size_t i = 0; i < ct.size(); ++i) {
636 if (ct[i] == config->default_dcp_content_type ()) {
637 _dcp_content_type->SetSelection (i);
641 checked_set (_still_length, config->default_still_length ());
642 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
643 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
644 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
645 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
646 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
647 checked_set (_audio_delay, config->default_audio_delay ());
648 checked_set (_standard, config->default_interop() ? 1 : 0);
651 void j2k_bandwidth_changed ()
653 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
656 void audio_delay_changed ()
658 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
661 void dcp_audio_channels_changed ()
663 int const s = _dcp_audio_channels->GetSelection ();
664 if (s != wxNOT_FOUND) {
665 Config::instance()->set_default_dcp_audio_channels (
666 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
671 void directory_changed ()
673 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
676 void kdm_directory_changed ()
678 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
681 void edit_isdcf_metadata_clicked ()
683 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
685 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
689 void still_length_changed ()
691 Config::instance()->set_default_still_length (_still_length->GetValue ());
694 void container_changed ()
696 vector<Ratio const *> ratio = Ratio::all ();
697 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
700 void scale_to_changed ()
702 int const s = _scale_to->GetSelection ();
704 Config::instance()->set_default_scale_to (0);
706 vector<Ratio const *> ratio = Ratio::all ();
707 Config::instance()->set_default_scale_to (ratio[s - 1]);
711 void dcp_content_type_changed ()
713 vector<DCPContentType const *> ct = DCPContentType::all ();
714 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
717 void standard_changed ()
719 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
722 wxSpinCtrl* _j2k_bandwidth;
723 wxSpinCtrl* _audio_delay;
724 wxButton* _isdcf_metadata_button;
725 wxSpinCtrl* _still_length;
726 #ifdef DCPOMATIC_USE_OWN_PICKER
727 DirPickerCtrl* _directory;
728 DirPickerCtrl* _kdm_directory;
730 wxDirPickerCtrl* _directory;
731 wxDirPickerCtrl* _kdm_directory;
733 wxChoice* _container;
735 wxChoice* _dcp_content_type;
736 wxChoice* _dcp_audio_channels;
740 class EncodingServersPage : public StandardPage
743 EncodingServersPage (wxSize panel_size, int border)
744 : StandardPage (panel_size, border)
747 wxString GetName () const
753 wxBitmap GetLargeIcon () const
755 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
762 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
763 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
765 vector<string> columns;
766 columns.push_back (wx_to_std (_("IP address / host name")));
767 _servers_list = new EditableList<string, ServerDialog> (
770 boost::bind (&Config::servers, Config::instance()),
771 boost::bind (&Config::set_servers, Config::instance(), _1),
772 boost::bind (&EncodingServersPage::server_column, this, _1)
775 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
777 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
780 void config_changed ()
782 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
783 _servers_list->refresh ();
786 void use_any_servers_changed ()
788 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
791 string server_column (string s)
796 wxCheckBox* _use_any_servers;
797 EditableList<string, ServerDialog>* _servers_list;
800 class CertificateChainEditor : public wxPanel
803 CertificateChainEditor (
807 function<void (shared_ptr<dcp::CertificateChain>)> set,
808 function<shared_ptr<const dcp::CertificateChain> (void)> get,
809 function<void (void)> nag_remake
814 , _nag_remake (nag_remake)
816 wxFont subheading_font (*wxNORMAL_FONT);
817 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
819 _sizer = new wxBoxSizer (wxVERTICAL);
822 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
823 m->SetFont (subheading_font);
824 _sizer->Add (m, 0, wxALL, border);
827 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
828 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
830 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
835 ip.SetText (_("Type"));
837 _certificates->InsertColumn (0, ip);
843 ip.SetText (_("Thumbprint"));
846 wxFont font = ip.GetFont ();
847 font.SetFamily (wxFONTFAMILY_TELETYPE);
850 _certificates->InsertColumn (1, ip);
853 certificates_sizer->Add (_certificates, 1, wxEXPAND);
856 wxSizer* s = new wxBoxSizer (wxVERTICAL);
857 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
858 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
859 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
860 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
861 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
862 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
863 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
866 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
867 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
870 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
871 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
872 wxFont font = _private_key->GetFont ();
873 font.SetFamily (wxFONTFAMILY_TELETYPE);
874 _private_key->SetFont (font);
875 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
876 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
877 table->Add (_load_private_key, wxGBPosition (r, 2));
878 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
879 table->Add (_export_private_key, wxGBPosition (r, 3));
882 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
883 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
884 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
885 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
888 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
889 font = *wxSMALL_FONT;
890 font.SetWeight (wxFONTWEIGHT_BOLD);
891 _private_key_bad->SetFont (font);
892 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
895 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
896 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
897 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
898 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
899 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
900 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
901 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
902 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
904 SetSizerAndFit (_sizer);
907 void config_changed ()
909 _chain.reset (new dcp::CertificateChain (*_get().get ()));
911 update_certificate_list ();
912 update_private_key ();
913 update_sensitivity ();
916 void add_button (wxWindow* button)
918 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
923 void add_certificate ()
925 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
927 if (d->ShowModal() == wxID_OK) {
932 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
933 } catch (boost::filesystem::filesystem_error& e) {
934 error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
939 if (!extra.empty ()) {
942 _("This file contains other certificates (or other data) after its first certificate. "
943 "Only the first certificate will be used.")
947 if (!_chain->chain_valid ()) {
950 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
951 "Add certificates in order from root to intermediate to leaf.")
956 update_certificate_list ();
958 } catch (dcp::MiscError& e) {
959 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
965 update_sensitivity ();
968 void remove_certificate ()
970 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
975 _certificates->DeleteItem (i);
979 update_sensitivity ();
982 void export_certificate ()
984 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
989 wxFileDialog* d = new wxFileDialog (
990 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
991 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
994 dcp::CertificateChain::List all = _chain->root_to_leaf ();
995 dcp::CertificateChain::List::iterator j = all.begin ();
996 for (int k = 0; k < i; ++k) {
1000 if (d->ShowModal () == wxID_OK) {
1001 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1003 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1006 string const s = j->certificate (true);
1007 fwrite (s.c_str(), 1, s.length(), f);
1013 void update_certificate_list ()
1015 _certificates->DeleteAllItems ();
1017 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
1018 BOOST_FOREACH (dcp::Certificate const & i, certs) {
1021 _certificates->InsertItem (item);
1022 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
1025 _certificates->SetItem (n, 0, _("Root"));
1026 } else if (n == (certs.size() - 1)) {
1027 _certificates->SetItem (n, 0, _("Leaf"));
1029 _certificates->SetItem (n, 0, _("Intermediate"));
1035 static wxColour normal = _private_key_bad->GetForegroundColour ();
1037 if (_chain->private_key_valid ()) {
1038 _private_key_bad->Hide ();
1039 _private_key_bad->SetForegroundColour (normal);
1041 _private_key_bad->Show ();
1042 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
1046 void remake_certificates ()
1048 shared_ptr<const dcp::CertificateChain> chain = _get ();
1050 string subject_organization_name;
1051 string subject_organizational_unit_name;
1052 string root_common_name;
1053 string intermediate_common_name;
1054 string leaf_common_name;
1056 dcp::CertificateChain::List all = chain->root_to_leaf ();
1058 if (all.size() >= 1) {
1060 subject_organization_name = chain->root().subject_organization_name ();
1061 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1062 root_common_name = chain->root().subject_common_name ();
1065 if (all.size() >= 2) {
1067 leaf_common_name = chain->leaf().subject_common_name ();
1070 if (all.size() >= 3) {
1071 /* Have an intermediate */
1072 dcp::CertificateChain::List::iterator i = all.begin ();
1074 intermediate_common_name = i->subject_common_name ();
1079 MakeChainDialog* d = new MakeChainDialog (
1081 subject_organization_name,
1082 subject_organizational_unit_name,
1084 intermediate_common_name,
1088 if (d->ShowModal () == wxID_OK) {
1090 new dcp::CertificateChain (
1093 d->organisational_unit (),
1094 d->root_common_name (),
1095 d->intermediate_common_name (),
1096 d->leaf_common_name ()
1101 update_certificate_list ();
1102 update_private_key ();
1108 void update_sensitivity ()
1110 /* We can only remove the leaf certificate */
1111 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
1112 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1115 void update_private_key ()
1117 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1121 void load_private_key ()
1123 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1125 if (d->ShowModal() == wxID_OK) {
1127 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1128 if (boost::filesystem::file_size (p) > 8192) {
1131 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1136 _chain->set_key (dcp::file_to_string (p));
1138 update_private_key ();
1139 } catch (dcp::MiscError& e) {
1140 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1146 update_sensitivity ();
1149 void export_private_key ()
1151 optional<string> key = _chain->key ();
1156 wxFileDialog* d = new wxFileDialog (
1157 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1158 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1161 if (d->ShowModal () == wxID_OK) {
1162 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1164 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1167 string const s = _chain->key().get ();
1168 fwrite (s.c_str(), 1, s.length(), f);
1174 wxListCtrl* _certificates;
1175 wxButton* _add_certificate;
1176 wxButton* _export_certificate;
1177 wxButton* _remove_certificate;
1178 wxButton* _remake_certificates;
1179 wxStaticText* _private_key;
1180 wxButton* _load_private_key;
1181 wxButton* _export_private_key;
1182 wxStaticText* _private_key_bad;
1184 wxBoxSizer* _button_sizer;
1185 shared_ptr<dcp::CertificateChain> _chain;
1186 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1187 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1188 boost::function<void (void)> _nag_remake;
1191 class KeysPage : public StandardPage
1194 KeysPage (wxSize panel_size, int border)
1195 : StandardPage (panel_size, border)
1198 wxString GetName () const
1203 #ifdef DCPOMATIC_OSX
1204 wxBitmap GetLargeIcon () const
1206 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1214 _signer = new CertificateChainEditor (
1215 _panel, _("Signing DCPs and KDMs"), _border,
1216 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1217 boost::bind (&Config::signer_chain, Config::instance ()),
1218 boost::bind (&do_nothing)
1221 _panel->GetSizer()->Add (_signer);
1223 _decryption = new CertificateChainEditor (
1224 _panel, _("Decrypting KDMs"), _border,
1225 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1226 boost::bind (&Config::decryption_chain, Config::instance ()),
1227 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1230 _panel->GetSizer()->Add (_decryption);
1232 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1233 _decryption->add_button (_export_decryption_certificate);
1234 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1235 _decryption->add_button (_export_decryption_chain);
1237 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1238 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1241 void export_decryption_certificate ()
1243 wxFileDialog* d = new wxFileDialog (
1244 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1245 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1248 if (d->ShowModal () == wxID_OK) {
1249 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1251 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1254 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1255 fwrite (s.c_str(), 1, s.length(), f);
1261 void export_decryption_chain ()
1263 wxFileDialog* d = new wxFileDialog (
1264 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1265 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1268 if (d->ShowModal () == wxID_OK) {
1269 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1271 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1274 string const s = Config::instance()->decryption_chain()->chain();
1275 fwrite (s.c_str(), 1, s.length(), f);
1281 void config_changed ()
1283 _signer->config_changed ();
1284 _decryption->config_changed ();
1287 void nag_remake_decryption_chain ()
1289 NagDialog::maybe_nag (
1291 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1292 _("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!")
1296 CertificateChainEditor* _signer;
1297 CertificateChainEditor* _decryption;
1298 wxButton* _export_decryption_certificate;
1299 wxButton* _export_decryption_chain;
1302 class TMSPage : public StandardPage
1305 TMSPage (wxSize panel_size, int border)
1306 : StandardPage (panel_size, border)
1309 wxString GetName () const
1314 #ifdef DCPOMATIC_OSX
1315 wxBitmap GetLargeIcon () const
1317 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1324 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1325 table->AddGrowableCol (1, 1);
1326 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1328 add_label_to_sizer (table, _panel, _("Protocol"), true);
1329 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1330 table->Add (_tms_protocol, 1, wxEXPAND);
1332 add_label_to_sizer (table, _panel, _("IP address"), true);
1333 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1334 table->Add (_tms_ip, 1, wxEXPAND);
1336 add_label_to_sizer (table, _panel, _("Target path"), true);
1337 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1338 table->Add (_tms_path, 1, wxEXPAND);
1340 add_label_to_sizer (table, _panel, _("User name"), true);
1341 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1342 table->Add (_tms_user, 1, wxEXPAND);
1344 add_label_to_sizer (table, _panel, _("Password"), true);
1345 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1346 table->Add (_tms_password, 1, wxEXPAND);
1348 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1349 _tms_protocol->Append (_("FTP (for Dolby)"));
1351 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1352 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1353 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1354 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1355 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1358 void config_changed ()
1360 Config* config = Config::instance ();
1362 checked_set (_tms_protocol, config->tms_protocol ());
1363 checked_set (_tms_ip, config->tms_ip ());
1364 checked_set (_tms_path, config->tms_path ());
1365 checked_set (_tms_user, config->tms_user ());
1366 checked_set (_tms_password, config->tms_password ());
1369 void tms_protocol_changed ()
1371 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1374 void tms_ip_changed ()
1376 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1379 void tms_path_changed ()
1381 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1384 void tms_user_changed ()
1386 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1389 void tms_password_changed ()
1391 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1394 wxChoice* _tms_protocol;
1395 wxTextCtrl* _tms_ip;
1396 wxTextCtrl* _tms_path;
1397 wxTextCtrl* _tms_user;
1398 wxTextCtrl* _tms_password;
1407 class KDMEmailPage : public StandardPage
1411 KDMEmailPage (wxSize panel_size, int border)
1412 #ifdef DCPOMATIC_OSX
1413 /* We have to force both width and height of this one */
1414 : StandardPage (wxSize (480, 128), border)
1416 : StandardPage (panel_size, border)
1420 wxString GetName () const
1422 return _("KDM Email");
1425 #ifdef DCPOMATIC_OSX
1426 wxBitmap GetLargeIcon () const
1428 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1435 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1436 table->AddGrowableCol (1, 1);
1437 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1439 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1441 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1442 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1443 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1444 add_label_to_sizer (s, _panel, _("port"), false);
1445 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1446 _mail_port->SetRange (0, 65535);
1447 s->Add (_mail_port);
1448 table->Add (s, 1, wxEXPAND | wxALL);
1451 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1452 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1453 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1455 add_label_to_sizer (table, _panel, _("Mail password"), true);
1456 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1457 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1459 add_label_to_sizer (table, _panel, _("Subject"), true);
1460 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1461 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1463 add_label_to_sizer (table, _panel, _("From address"), true);
1464 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1465 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1467 vector<string> columns;
1468 columns.push_back (wx_to_std (_("Address")));
1469 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1470 _kdm_cc = new EditableList<string, EmailDialog> (
1473 bind (&Config::kdm_cc, Config::instance()),
1474 bind (&Config::set_kdm_cc, Config::instance(), _1),
1477 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1479 add_label_to_sizer (table, _panel, _("BCC address"), true);
1480 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1481 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1483 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1484 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1486 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1487 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1491 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1492 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1493 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1494 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1495 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1496 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1497 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1498 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1499 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1502 void config_changed ()
1504 Config* config = Config::instance ();
1506 checked_set (_mail_server, config->mail_server ());
1507 checked_set (_mail_port, config->mail_port ());
1508 checked_set (_mail_user, config->mail_user ());
1509 checked_set (_mail_password, config->mail_password ());
1510 checked_set (_kdm_subject, config->kdm_subject ());
1511 checked_set (_kdm_from, config->kdm_from ());
1512 checked_set (_kdm_bcc, config->kdm_bcc ());
1513 checked_set (_kdm_email, Config::instance()->kdm_email ());
1516 void mail_server_changed ()
1518 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1521 void mail_port_changed ()
1523 Config::instance()->set_mail_port (_mail_port->GetValue ());
1526 void mail_user_changed ()
1528 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1531 void mail_password_changed ()
1533 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1536 void kdm_subject_changed ()
1538 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1541 void kdm_from_changed ()
1543 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1546 void kdm_bcc_changed ()
1548 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1551 void kdm_email_changed ()
1553 if (_kdm_email->GetValue().IsEmpty ()) {
1554 /* Sometimes we get sent an erroneous notification that the email
1555 is empty; I don't know why.
1559 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1562 void reset_kdm_email ()
1564 Config::instance()->reset_kdm_email ();
1565 checked_set (_kdm_email, Config::instance()->kdm_email ());
1568 wxTextCtrl* _mail_server;
1569 wxSpinCtrl* _mail_port;
1570 wxTextCtrl* _mail_user;
1571 wxTextCtrl* _mail_password;
1572 wxTextCtrl* _kdm_subject;
1573 wxTextCtrl* _kdm_from;
1574 EditableList<string, EmailDialog>* _kdm_cc;
1575 wxTextCtrl* _kdm_bcc;
1576 wxTextCtrl* _kdm_email;
1577 wxButton* _reset_kdm_email;
1580 class CoverSheetPage : public StandardPage
1584 CoverSheetPage (wxSize panel_size, int border)
1585 #ifdef DCPOMATIC_OSX
1586 /* We have to force both width and height of this one */
1587 : StandardPage (wxSize (480, 128), border)
1589 : StandardPage (panel_size, border)
1593 wxString GetName () const
1595 return _("Cover Sheet");
1598 #ifdef DCPOMATIC_OSX
1599 wxBitmap GetLargeIcon () const
1601 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1608 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1609 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1611 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1612 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1614 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1615 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1618 void config_changed ()
1620 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1623 void cover_sheet_changed ()
1625 if (_cover_sheet->GetValue().IsEmpty ()) {
1626 /* Sometimes we get sent an erroneous notification that the cover sheet
1627 is empty; I don't know why.
1631 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1634 void reset_cover_sheet ()
1636 Config::instance()->reset_cover_sheet ();
1637 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1640 wxTextCtrl* _cover_sheet;
1641 wxButton* _reset_cover_sheet;
1645 /** @class AdvancedPage
1646 * @brief Advanced page of the preferences dialog.
1648 class AdvancedPage : public StockPage
1651 AdvancedPage (wxSize panel_size, int border)
1652 : StockPage (Kind_Advanced, panel_size, border)
1653 , _maximum_j2k_bandwidth (0)
1654 , _allow_any_dcp_frame_rate (0)
1655 , _only_servers_encode (0)
1660 , _log_debug_decode (0)
1661 , _log_debug_encode (0)
1662 , _log_debug_email (0)
1666 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1668 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1670 flags |= wxALIGN_RIGHT;
1673 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1674 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1679 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1680 table->AddGrowableCol (1, 1);
1681 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1684 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1685 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1686 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1687 s->Add (_maximum_j2k_bandwidth, 1);
1688 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1692 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1693 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1694 table->AddSpacer (0);
1696 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1697 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1698 table->AddSpacer (0);
1701 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1702 dcp::NameFormat::Map titles;
1703 titles['t'] = "type (cpl/pkl)";
1704 dcp::NameFormat::Map examples;
1705 examples['t'] = "cpl";
1706 _dcp_metadata_filename_format = new NameFormatEditor (
1707 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1709 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1713 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1714 dcp::NameFormat::Map titles;
1715 titles['t'] = "type (j2c/pcm/sub)";
1716 titles['r'] = "reel number";
1717 titles['n'] = "number of reels";
1718 titles['c'] = "content filename";
1719 dcp::NameFormat::Map examples;
1720 examples['t'] = "j2c";
1721 examples['r'] = "1";
1722 examples['n'] = "4";
1723 examples['c'] = "myfile.mp4";
1724 _dcp_asset_filename_format = new NameFormatEditor (
1725 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1727 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1731 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1732 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1733 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1734 t->Add (_log_general, 1, wxEXPAND | wxALL);
1735 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1736 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1737 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1738 t->Add (_log_error, 1, wxEXPAND | wxALL);
1739 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1740 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1741 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1742 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1743 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1744 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1745 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1746 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1747 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1748 table->Add (t, 0, wxALL, 6);
1751 #ifdef DCPOMATIC_WINDOWS
1752 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1753 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1754 table->AddSpacer (0);
1757 _maximum_j2k_bandwidth->SetRange (1, 1000);
1758 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1759 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1760 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1761 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1762 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1763 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1764 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1765 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1766 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1767 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1768 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1769 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1770 #ifdef DCPOMATIC_WINDOWS
1771 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1775 void config_changed ()
1777 Config* config = Config::instance ();
1779 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1780 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1781 checked_set (_only_servers_encode, config->only_servers_encode ());
1782 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1783 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1784 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1785 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1786 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1787 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1788 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1789 #ifdef DCPOMATIC_WINDOWS
1790 checked_set (_win32_console, config->win32_console());
1794 void maximum_j2k_bandwidth_changed ()
1796 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1799 void allow_any_dcp_frame_rate_changed ()
1801 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1804 void only_servers_encode_changed ()
1806 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1809 void dcp_metadata_filename_format_changed ()
1811 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1814 void dcp_asset_filename_format_changed ()
1816 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1822 if (_log_general->GetValue ()) {
1823 types |= LogEntry::TYPE_GENERAL;
1825 if (_log_warning->GetValue ()) {
1826 types |= LogEntry::TYPE_WARNING;
1828 if (_log_error->GetValue ()) {
1829 types |= LogEntry::TYPE_ERROR;
1831 if (_log_timing->GetValue ()) {
1832 types |= LogEntry::TYPE_TIMING;
1834 if (_log_debug_decode->GetValue ()) {
1835 types |= LogEntry::TYPE_DEBUG_DECODE;
1837 if (_log_debug_encode->GetValue ()) {
1838 types |= LogEntry::TYPE_DEBUG_ENCODE;
1840 if (_log_debug_email->GetValue ()) {
1841 types |= LogEntry::TYPE_DEBUG_EMAIL;
1843 Config::instance()->set_log_types (types);
1846 #ifdef DCPOMATIC_WINDOWS
1847 void win32_console_changed ()
1849 Config::instance()->set_win32_console (_win32_console->GetValue ());
1853 wxSpinCtrl* _maximum_j2k_bandwidth;
1854 wxCheckBox* _allow_any_dcp_frame_rate;
1855 wxCheckBox* _only_servers_encode;
1856 NameFormatEditor* _dcp_metadata_filename_format;
1857 NameFormatEditor* _dcp_asset_filename_format;
1858 wxCheckBox* _log_general;
1859 wxCheckBox* _log_warning;
1860 wxCheckBox* _log_error;
1861 wxCheckBox* _log_timing;
1862 wxCheckBox* _log_debug_decode;
1863 wxCheckBox* _log_debug_encode;
1864 wxCheckBox* _log_debug_email;
1865 #ifdef DCPOMATIC_WINDOWS
1866 wxCheckBox* _win32_console;
1870 wxPreferencesEditor*
1871 create_config_dialog ()
1873 wxPreferencesEditor* e = new wxPreferencesEditor ();
1875 #ifdef DCPOMATIC_OSX
1876 /* Width that we force some of the config panels to be on OSX so that
1877 the containing window doesn't shrink too much when we select those panels.
1878 This is obviously an unpleasant hack.
1880 wxSize ps = wxSize (520, -1);
1881 int const border = 16;
1883 wxSize ps = wxSize (-1, -1);
1884 int const border = 8;
1887 e->AddPage (new GeneralPage (ps, border));
1888 e->AddPage (new DefaultsPage (ps, border));
1889 e->AddPage (new EncodingServersPage (ps, border));
1890 e->AddPage (new KeysPage (ps, border));
1891 e->AddPage (new TMSPage (ps, border));
1892 e->AddPage (new KDMEmailPage (ps, border));
1893 e->AddPage (new CoverSheetPage (ps, border));
1894 e->AddPage (new AdvancedPage (ps, border));