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 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);
339 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
342 if (!current_so || *current_so != configured_so) {
343 /* Update _preview_sound_output with the configured value */
345 while (i < _preview_sound_output->GetCount()) {
346 if (_preview_sound_output->GetString(i) == std_to_wx(configured_so)) {
347 _preview_sound_output->SetSelection (i);
354 setup_sensitivity ();
357 /** @return Currently-selected preview sound output in the dialogue */
358 optional<string> get_preview_sound_output ()
360 int const sel = _preview_sound_output->GetSelection ();
361 if (sel == wxNOT_FOUND) {
362 return optional<string> ();
365 return wx_to_std (_preview_sound_output->GetString (sel));
368 void setup_sensitivity ()
370 _language->Enable (_set_language->GetValue ());
371 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
372 _preview_sound_output->Enable (_preview_sound->GetValue ());
375 void set_language_changed ()
377 setup_sensitivity ();
378 if (_set_language->GetValue ()) {
381 Config::instance()->unset_language ();
385 void language_changed ()
387 int const sel = _language->GetSelection ();
389 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
391 Config::instance()->unset_language ();
395 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
396 void analyse_ebur128_changed ()
398 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
402 void automatic_audio_analysis_changed ()
404 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
407 void check_for_updates_changed ()
409 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
412 void check_for_test_updates_changed ()
414 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
417 void master_encoding_threads_changed ()
419 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
422 void server_encoding_threads_changed ()
424 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
427 void issuer_changed ()
429 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
432 void creator_changed ()
434 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
437 void cinemas_file_changed ()
439 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
442 void preview_sound_changed ()
444 Config::instance()->set_preview_sound (_preview_sound->GetValue ());
447 void preview_sound_output_changed ()
449 RtAudio audio (DCPOMATIC_RTAUDIO_API);
450 optional<string> const so = get_preview_sound_output();
451 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
452 Config::instance()->unset_preview_sound_output ();
454 Config::instance()->set_preview_sound_output (*so);
458 wxCheckBox* _set_language;
460 wxSpinCtrl* _master_encoding_threads;
461 wxSpinCtrl* _server_encoding_threads;
462 FilePickerCtrl* _cinemas_file;
463 wxCheckBox* _preview_sound;
464 wxChoice* _preview_sound_output;
465 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
466 wxCheckBox* _analyse_ebur128;
468 wxCheckBox* _automatic_audio_analysis;
469 wxCheckBox* _check_for_updates;
470 wxCheckBox* _check_for_test_updates;
472 wxTextCtrl* _creator;
475 class DefaultsPage : public StandardPage
478 DefaultsPage (wxSize panel_size, int border)
479 : StandardPage (panel_size, border)
482 wxString GetName () const
484 return _("Defaults");
488 wxBitmap GetLargeIcon () const
490 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
497 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
498 table->AddGrowableCol (1, 1);
499 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
502 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
503 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
504 _still_length = new wxSpinCtrl (_panel);
505 s->Add (_still_length);
506 add_label_to_sizer (s, _panel, _("s"), false);
510 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
511 #ifdef DCPOMATIC_USE_OWN_PICKER
512 _directory = new DirPickerCtrl (_panel);
514 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
516 table->Add (_directory, 1, wxEXPAND);
518 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
519 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
520 table->Add (_isdcf_metadata_button);
522 add_label_to_sizer (table, _panel, _("Default container"), true);
523 _container = new wxChoice (_panel, wxID_ANY);
524 table->Add (_container);
526 add_label_to_sizer (table, _panel, _("Default scale-to"), true);
527 _scale_to = new wxChoice (_panel, wxID_ANY);
528 table->Add (_scale_to);
530 add_label_to_sizer (table, _panel, _("Default content type"), true);
531 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
532 table->Add (_dcp_content_type);
534 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
535 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
536 table->Add (_dcp_audio_channels);
539 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
540 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
541 _j2k_bandwidth = new wxSpinCtrl (_panel);
542 s->Add (_j2k_bandwidth);
543 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
548 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
549 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
550 _audio_delay = new wxSpinCtrl (_panel);
551 s->Add (_audio_delay);
552 add_label_to_sizer (s, _panel, _("ms"), false);
556 add_label_to_sizer (table, _panel, _("Default standard"), true);
557 _standard = new wxChoice (_panel, wxID_ANY);
558 table->Add (_standard);
560 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
561 #ifdef DCPOMATIC_USE_OWN_PICKER
562 _kdm_directory = new DirPickerCtrl (_panel);
564 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
566 table->Add (_kdm_directory, 1, wxEXPAND);
568 _still_length->SetRange (1, 3600);
569 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
571 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
572 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
574 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
576 vector<Ratio const *> ratios = Ratio::all ();
577 for (size_t i = 0; i < ratios.size(); ++i) {
578 _container->Append (std_to_wx (ratios[i]->nickname ()));
581 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
583 _scale_to->Append (_("Guess from content"));
585 for (size_t i = 0; i < ratios.size(); ++i) {
586 _scale_to->Append (std_to_wx (ratios[i]->nickname ()));
589 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
591 vector<DCPContentType const *> const ct = DCPContentType::all ();
592 for (size_t i = 0; i < ct.size(); ++i) {
593 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
596 setup_audio_channels_choice (_dcp_audio_channels, 2);
598 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
599 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
601 _j2k_bandwidth->SetRange (50, 250);
602 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
604 _audio_delay->SetRange (-1000, 1000);
605 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
607 _standard->Append (_("SMPTE"));
608 _standard->Append (_("Interop"));
609 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
612 void config_changed ()
614 Config* config = Config::instance ();
616 vector<Ratio const *> ratios = Ratio::all ();
617 for (size_t i = 0; i < ratios.size(); ++i) {
618 if (ratios[i] == config->default_container ()) {
619 _container->SetSelection (i);
621 if (ratios[i] == config->default_scale_to ()) {
622 _scale_to->SetSelection (i + 1);
626 if (!config->default_scale_to()) {
627 _scale_to->SetSelection (0);
630 vector<DCPContentType const *> const ct = DCPContentType::all ();
631 for (size_t i = 0; i < ct.size(); ++i) {
632 if (ct[i] == config->default_dcp_content_type ()) {
633 _dcp_content_type->SetSelection (i);
637 checked_set (_still_length, config->default_still_length ());
638 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
639 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
640 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
641 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
642 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
643 checked_set (_audio_delay, config->default_audio_delay ());
644 checked_set (_standard, config->default_interop() ? 1 : 0);
647 void j2k_bandwidth_changed ()
649 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
652 void audio_delay_changed ()
654 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
657 void dcp_audio_channels_changed ()
659 int const s = _dcp_audio_channels->GetSelection ();
660 if (s != wxNOT_FOUND) {
661 Config::instance()->set_default_dcp_audio_channels (
662 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
667 void directory_changed ()
669 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
672 void kdm_directory_changed ()
674 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
677 void edit_isdcf_metadata_clicked ()
679 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
681 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
685 void still_length_changed ()
687 Config::instance()->set_default_still_length (_still_length->GetValue ());
690 void container_changed ()
692 vector<Ratio const *> ratio = Ratio::all ();
693 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
696 void scale_to_changed ()
698 int const s = _scale_to->GetSelection ();
700 Config::instance()->set_default_scale_to (0);
702 vector<Ratio const *> ratio = Ratio::all ();
703 Config::instance()->set_default_scale_to (ratio[s - 1]);
707 void dcp_content_type_changed ()
709 vector<DCPContentType const *> ct = DCPContentType::all ();
710 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
713 void standard_changed ()
715 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
718 wxSpinCtrl* _j2k_bandwidth;
719 wxSpinCtrl* _audio_delay;
720 wxButton* _isdcf_metadata_button;
721 wxSpinCtrl* _still_length;
722 #ifdef DCPOMATIC_USE_OWN_PICKER
723 DirPickerCtrl* _directory;
724 DirPickerCtrl* _kdm_directory;
726 wxDirPickerCtrl* _directory;
727 wxDirPickerCtrl* _kdm_directory;
729 wxChoice* _container;
731 wxChoice* _dcp_content_type;
732 wxChoice* _dcp_audio_channels;
736 class EncodingServersPage : public StandardPage
739 EncodingServersPage (wxSize panel_size, int border)
740 : StandardPage (panel_size, border)
743 wxString GetName () const
749 wxBitmap GetLargeIcon () const
751 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
758 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
759 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
761 vector<string> columns;
762 columns.push_back (wx_to_std (_("IP address / host name")));
763 _servers_list = new EditableList<string, ServerDialog> (
766 boost::bind (&Config::servers, Config::instance()),
767 boost::bind (&Config::set_servers, Config::instance(), _1),
768 boost::bind (&EncodingServersPage::server_column, this, _1)
771 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
773 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
776 void config_changed ()
778 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
779 _servers_list->refresh ();
782 void use_any_servers_changed ()
784 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
787 string server_column (string s)
792 wxCheckBox* _use_any_servers;
793 EditableList<string, ServerDialog>* _servers_list;
796 class CertificateChainEditor : public wxPanel
799 CertificateChainEditor (
803 function<void (shared_ptr<dcp::CertificateChain>)> set,
804 function<shared_ptr<const dcp::CertificateChain> (void)> get,
805 function<void (void)> nag_remake
810 , _nag_remake (nag_remake)
812 wxFont subheading_font (*wxNORMAL_FONT);
813 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
815 _sizer = new wxBoxSizer (wxVERTICAL);
818 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
819 m->SetFont (subheading_font);
820 _sizer->Add (m, 0, wxALL, border);
823 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
824 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
826 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
831 ip.SetText (_("Type"));
833 _certificates->InsertColumn (0, ip);
839 ip.SetText (_("Thumbprint"));
842 wxFont font = ip.GetFont ();
843 font.SetFamily (wxFONTFAMILY_TELETYPE);
846 _certificates->InsertColumn (1, ip);
849 certificates_sizer->Add (_certificates, 1, wxEXPAND);
852 wxSizer* s = new wxBoxSizer (wxVERTICAL);
853 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
854 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
855 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
856 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
857 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
858 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
859 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
862 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
863 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
866 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
867 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
868 wxFont font = _private_key->GetFont ();
869 font.SetFamily (wxFONTFAMILY_TELETYPE);
870 _private_key->SetFont (font);
871 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
872 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
873 table->Add (_load_private_key, wxGBPosition (r, 2));
874 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
875 table->Add (_export_private_key, wxGBPosition (r, 3));
878 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
879 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
880 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
881 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
884 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
885 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
886 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
887 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
888 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
889 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
890 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
891 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
893 SetSizerAndFit (_sizer);
896 void config_changed ()
898 _chain.reset (new dcp::CertificateChain (*_get().get ()));
900 update_certificate_list ();
901 update_private_key ();
902 update_sensitivity ();
905 void add_button (wxWindow* button)
907 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
912 void add_certificate ()
914 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
916 if (d->ShowModal() == wxID_OK) {
919 string const extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
920 if (!extra.empty ()) {
923 _("This file contains other certificates (or other data) after its first certificate. "
924 "Only the first certificate will be used.")
929 update_certificate_list ();
930 } catch (dcp::MiscError& e) {
931 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
937 update_sensitivity ();
940 void remove_certificate ()
942 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
947 _certificates->DeleteItem (i);
951 update_sensitivity ();
954 void export_certificate ()
956 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
961 wxFileDialog* d = new wxFileDialog (
962 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
963 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
966 dcp::CertificateChain::List all = _chain->root_to_leaf ();
967 dcp::CertificateChain::List::iterator j = all.begin ();
968 for (int k = 0; k < i; ++k) {
972 if (d->ShowModal () == wxID_OK) {
973 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
975 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
978 string const s = j->certificate (true);
979 fwrite (s.c_str(), 1, s.length(), f);
985 void update_certificate_list ()
987 _certificates->DeleteAllItems ();
989 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
990 BOOST_FOREACH (dcp::Certificate const & i, certs) {
993 _certificates->InsertItem (item);
994 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
997 _certificates->SetItem (n, 0, _("Root"));
998 } else if (n == (certs.size() - 1)) {
999 _certificates->SetItem (n, 0, _("Leaf"));
1001 _certificates->SetItem (n, 0, _("Intermediate"));
1008 void remake_certificates ()
1010 shared_ptr<const dcp::CertificateChain> chain = _get ();
1012 string subject_organization_name;
1013 string subject_organizational_unit_name;
1014 string root_common_name;
1015 string intermediate_common_name;
1016 string leaf_common_name;
1018 dcp::CertificateChain::List all = chain->root_to_leaf ();
1020 if (all.size() >= 1) {
1022 subject_organization_name = chain->root().subject_organization_name ();
1023 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1024 root_common_name = chain->root().subject_common_name ();
1027 if (all.size() >= 2) {
1029 leaf_common_name = chain->leaf().subject_common_name ();
1032 if (all.size() >= 3) {
1033 /* Have an intermediate */
1034 dcp::CertificateChain::List::iterator i = all.begin ();
1036 intermediate_common_name = i->subject_common_name ();
1041 MakeChainDialog* d = new MakeChainDialog (
1043 subject_organization_name,
1044 subject_organizational_unit_name,
1046 intermediate_common_name,
1050 if (d->ShowModal () == wxID_OK) {
1052 new dcp::CertificateChain (
1055 d->organisational_unit (),
1056 d->root_common_name (),
1057 d->intermediate_common_name (),
1058 d->leaf_common_name ()
1063 update_certificate_list ();
1064 update_private_key ();
1070 void update_sensitivity ()
1072 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1073 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1076 void update_private_key ()
1078 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1082 void load_private_key ()
1084 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1086 if (d->ShowModal() == wxID_OK) {
1088 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1089 if (boost::filesystem::file_size (p) > 8192) {
1092 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1097 _chain->set_key (dcp::file_to_string (p));
1099 update_private_key ();
1100 } catch (dcp::MiscError& e) {
1101 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1107 update_sensitivity ();
1110 void export_private_key ()
1112 optional<string> key = _chain->key ();
1117 wxFileDialog* d = new wxFileDialog (
1118 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1119 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1122 if (d->ShowModal () == wxID_OK) {
1123 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1125 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1128 string const s = _chain->key().get ();
1129 fwrite (s.c_str(), 1, s.length(), f);
1135 wxListCtrl* _certificates;
1136 wxButton* _add_certificate;
1137 wxButton* _export_certificate;
1138 wxButton* _remove_certificate;
1139 wxButton* _remake_certificates;
1140 wxStaticText* _private_key;
1141 wxButton* _load_private_key;
1142 wxButton* _export_private_key;
1144 wxBoxSizer* _button_sizer;
1145 shared_ptr<dcp::CertificateChain> _chain;
1146 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1147 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1148 boost::function<void (void)> _nag_remake;
1151 class KeysPage : public StandardPage
1154 KeysPage (wxSize panel_size, int border)
1155 : StandardPage (panel_size, border)
1158 wxString GetName () const
1163 #ifdef DCPOMATIC_OSX
1164 wxBitmap GetLargeIcon () const
1166 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1174 _signer = new CertificateChainEditor (
1175 _panel, _("Signing DCPs and KDMs"), _border,
1176 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1177 boost::bind (&Config::signer_chain, Config::instance ()),
1178 boost::bind (&do_nothing)
1181 _panel->GetSizer()->Add (_signer);
1183 _decryption = new CertificateChainEditor (
1184 _panel, _("Decrypting KDMs"), _border,
1185 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1186 boost::bind (&Config::decryption_chain, Config::instance ()),
1187 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1190 _panel->GetSizer()->Add (_decryption);
1192 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1193 _decryption->add_button (_export_decryption_certificate);
1194 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1195 _decryption->add_button (_export_decryption_chain);
1197 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1198 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1201 void export_decryption_certificate ()
1203 wxFileDialog* d = new wxFileDialog (
1204 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1205 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1208 if (d->ShowModal () == wxID_OK) {
1209 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1211 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1214 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1215 fwrite (s.c_str(), 1, s.length(), f);
1221 void export_decryption_chain ()
1223 wxFileDialog* d = new wxFileDialog (
1224 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1225 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1228 if (d->ShowModal () == wxID_OK) {
1229 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1231 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1234 string const s = Config::instance()->decryption_chain()->chain();
1235 fwrite (s.c_str(), 1, s.length(), f);
1241 void config_changed ()
1243 _signer->config_changed ();
1244 _decryption->config_changed ();
1247 void nag_remake_decryption_chain ()
1249 NagDialog::maybe_nag (
1251 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1252 _("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!")
1256 CertificateChainEditor* _signer;
1257 CertificateChainEditor* _decryption;
1258 wxButton* _export_decryption_certificate;
1259 wxButton* _export_decryption_chain;
1262 class TMSPage : public StandardPage
1265 TMSPage (wxSize panel_size, int border)
1266 : StandardPage (panel_size, border)
1269 wxString GetName () const
1274 #ifdef DCPOMATIC_OSX
1275 wxBitmap GetLargeIcon () const
1277 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1284 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1285 table->AddGrowableCol (1, 1);
1286 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1288 add_label_to_sizer (table, _panel, _("Protocol"), true);
1289 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1290 table->Add (_tms_protocol, 1, wxEXPAND);
1292 add_label_to_sizer (table, _panel, _("IP address"), true);
1293 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1294 table->Add (_tms_ip, 1, wxEXPAND);
1296 add_label_to_sizer (table, _panel, _("Target path"), true);
1297 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1298 table->Add (_tms_path, 1, wxEXPAND);
1300 add_label_to_sizer (table, _panel, _("User name"), true);
1301 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1302 table->Add (_tms_user, 1, wxEXPAND);
1304 add_label_to_sizer (table, _panel, _("Password"), true);
1305 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1306 table->Add (_tms_password, 1, wxEXPAND);
1308 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1309 _tms_protocol->Append (_("FTP (for Dolby)"));
1311 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1312 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1313 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1314 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1315 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1318 void config_changed ()
1320 Config* config = Config::instance ();
1322 checked_set (_tms_protocol, config->tms_protocol ());
1323 checked_set (_tms_ip, config->tms_ip ());
1324 checked_set (_tms_path, config->tms_path ());
1325 checked_set (_tms_user, config->tms_user ());
1326 checked_set (_tms_password, config->tms_password ());
1329 void tms_protocol_changed ()
1331 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1334 void tms_ip_changed ()
1336 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1339 void tms_path_changed ()
1341 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1344 void tms_user_changed ()
1346 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1349 void tms_password_changed ()
1351 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1354 wxChoice* _tms_protocol;
1355 wxTextCtrl* _tms_ip;
1356 wxTextCtrl* _tms_path;
1357 wxTextCtrl* _tms_user;
1358 wxTextCtrl* _tms_password;
1367 class KDMEmailPage : public StandardPage
1371 KDMEmailPage (wxSize panel_size, int border)
1372 #ifdef DCPOMATIC_OSX
1373 /* We have to force both width and height of this one */
1374 : StandardPage (wxSize (480, 128), border)
1376 : StandardPage (panel_size, border)
1380 wxString GetName () const
1382 return _("KDM Email");
1385 #ifdef DCPOMATIC_OSX
1386 wxBitmap GetLargeIcon () const
1388 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1395 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1396 table->AddGrowableCol (1, 1);
1397 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1399 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1401 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1402 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1403 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1404 add_label_to_sizer (s, _panel, _("port"), false);
1405 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1406 _mail_port->SetRange (0, 65535);
1407 s->Add (_mail_port);
1408 table->Add (s, 1, wxEXPAND | wxALL);
1411 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1412 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1413 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1415 add_label_to_sizer (table, _panel, _("Mail password"), true);
1416 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1417 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1419 add_label_to_sizer (table, _panel, _("Subject"), true);
1420 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1421 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1423 add_label_to_sizer (table, _panel, _("From address"), true);
1424 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1425 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1427 vector<string> columns;
1428 columns.push_back (wx_to_std (_("Address")));
1429 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1430 _kdm_cc = new EditableList<string, EmailDialog> (
1433 bind (&Config::kdm_cc, Config::instance()),
1434 bind (&Config::set_kdm_cc, Config::instance(), _1),
1437 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1439 add_label_to_sizer (table, _panel, _("BCC address"), true);
1440 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1441 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1443 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1444 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1446 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1447 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1451 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1452 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1453 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1454 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1455 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1456 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1457 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1458 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1459 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1462 void config_changed ()
1464 Config* config = Config::instance ();
1466 checked_set (_mail_server, config->mail_server ());
1467 checked_set (_mail_port, config->mail_port ());
1468 checked_set (_mail_user, config->mail_user ());
1469 checked_set (_mail_password, config->mail_password ());
1470 checked_set (_kdm_subject, config->kdm_subject ());
1471 checked_set (_kdm_from, config->kdm_from ());
1472 checked_set (_kdm_bcc, config->kdm_bcc ());
1473 checked_set (_kdm_email, Config::instance()->kdm_email ());
1476 void mail_server_changed ()
1478 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1481 void mail_port_changed ()
1483 Config::instance()->set_mail_port (_mail_port->GetValue ());
1486 void mail_user_changed ()
1488 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1491 void mail_password_changed ()
1493 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1496 void kdm_subject_changed ()
1498 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1501 void kdm_from_changed ()
1503 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1506 void kdm_bcc_changed ()
1508 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1511 void kdm_email_changed ()
1513 if (_kdm_email->GetValue().IsEmpty ()) {
1514 /* Sometimes we get sent an erroneous notification that the email
1515 is empty; I don't know why.
1519 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1522 void reset_kdm_email ()
1524 Config::instance()->reset_kdm_email ();
1525 checked_set (_kdm_email, Config::instance()->kdm_email ());
1528 wxTextCtrl* _mail_server;
1529 wxSpinCtrl* _mail_port;
1530 wxTextCtrl* _mail_user;
1531 wxTextCtrl* _mail_password;
1532 wxTextCtrl* _kdm_subject;
1533 wxTextCtrl* _kdm_from;
1534 EditableList<string, EmailDialog>* _kdm_cc;
1535 wxTextCtrl* _kdm_bcc;
1536 wxTextCtrl* _kdm_email;
1537 wxButton* _reset_kdm_email;
1540 class CoverSheetPage : public StandardPage
1544 CoverSheetPage (wxSize panel_size, int border)
1545 #ifdef DCPOMATIC_OSX
1546 /* We have to force both width and height of this one */
1547 : StandardPage (wxSize (480, 128), border)
1549 : StandardPage (panel_size, border)
1553 wxString GetName () const
1555 return _("Cover Sheet");
1558 #ifdef DCPOMATIC_OSX
1559 wxBitmap GetLargeIcon () const
1561 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1568 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1569 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1571 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1572 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1574 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1575 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1578 void config_changed ()
1580 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1583 void cover_sheet_changed ()
1585 if (_cover_sheet->GetValue().IsEmpty ()) {
1586 /* Sometimes we get sent an erroneous notification that the cover sheet
1587 is empty; I don't know why.
1591 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1594 void reset_cover_sheet ()
1596 Config::instance()->reset_cover_sheet ();
1597 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1600 wxTextCtrl* _cover_sheet;
1601 wxButton* _reset_cover_sheet;
1605 /** @class AdvancedPage
1606 * @brief Advanced page of the preferences dialog.
1608 class AdvancedPage : public StockPage
1611 AdvancedPage (wxSize panel_size, int border)
1612 : StockPage (Kind_Advanced, panel_size, border)
1613 , _maximum_j2k_bandwidth (0)
1614 , _allow_any_dcp_frame_rate (0)
1615 , _only_servers_encode (0)
1620 , _log_debug_decode (0)
1621 , _log_debug_encode (0)
1622 , _log_debug_email (0)
1626 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1628 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1630 flags |= wxALIGN_RIGHT;
1633 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1634 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1639 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1640 table->AddGrowableCol (1, 1);
1641 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1644 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1645 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1646 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1647 s->Add (_maximum_j2k_bandwidth, 1);
1648 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1652 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1653 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1654 table->AddSpacer (0);
1656 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1657 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1658 table->AddSpacer (0);
1661 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1662 dcp::NameFormat::Map titles;
1663 titles['t'] = "type (cpl/pkl)";
1664 dcp::NameFormat::Map examples;
1665 examples['t'] = "cpl";
1666 _dcp_metadata_filename_format = new NameFormatEditor (
1667 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1669 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1673 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1674 dcp::NameFormat::Map titles;
1675 titles['t'] = "type (j2c/pcm/sub)";
1676 titles['r'] = "reel number";
1677 titles['n'] = "number of reels";
1678 titles['c'] = "content filename";
1679 dcp::NameFormat::Map examples;
1680 examples['t'] = "j2c";
1681 examples['r'] = "1";
1682 examples['n'] = "4";
1683 examples['c'] = "myfile.mp4";
1684 _dcp_asset_filename_format = new NameFormatEditor (
1685 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1687 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1691 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1692 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1693 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1694 t->Add (_log_general, 1, wxEXPAND | wxALL);
1695 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1696 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1697 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1698 t->Add (_log_error, 1, wxEXPAND | wxALL);
1699 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1700 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1701 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1702 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1703 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1704 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1705 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1706 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1707 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1708 table->Add (t, 0, wxALL, 6);
1711 #ifdef DCPOMATIC_WINDOWS
1712 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1713 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1714 table->AddSpacer (0);
1717 _maximum_j2k_bandwidth->SetRange (1, 1000);
1718 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1719 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1720 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1721 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1722 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1723 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1724 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1725 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1726 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1727 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1728 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1729 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1730 #ifdef DCPOMATIC_WINDOWS
1731 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1735 void config_changed ()
1737 Config* config = Config::instance ();
1739 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1740 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1741 checked_set (_only_servers_encode, config->only_servers_encode ());
1742 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1743 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1744 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1745 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1746 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1747 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1748 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1749 #ifdef DCPOMATIC_WINDOWS
1750 checked_set (_win32_console, config->win32_console());
1754 void maximum_j2k_bandwidth_changed ()
1756 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1759 void allow_any_dcp_frame_rate_changed ()
1761 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1764 void only_servers_encode_changed ()
1766 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1769 void dcp_metadata_filename_format_changed ()
1771 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1774 void dcp_asset_filename_format_changed ()
1776 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1782 if (_log_general->GetValue ()) {
1783 types |= LogEntry::TYPE_GENERAL;
1785 if (_log_warning->GetValue ()) {
1786 types |= LogEntry::TYPE_WARNING;
1788 if (_log_error->GetValue ()) {
1789 types |= LogEntry::TYPE_ERROR;
1791 if (_log_timing->GetValue ()) {
1792 types |= LogEntry::TYPE_TIMING;
1794 if (_log_debug_decode->GetValue ()) {
1795 types |= LogEntry::TYPE_DEBUG_DECODE;
1797 if (_log_debug_encode->GetValue ()) {
1798 types |= LogEntry::TYPE_DEBUG_ENCODE;
1800 if (_log_debug_email->GetValue ()) {
1801 types |= LogEntry::TYPE_DEBUG_EMAIL;
1803 Config::instance()->set_log_types (types);
1806 #ifdef DCPOMATIC_WINDOWS
1807 void win32_console_changed ()
1809 Config::instance()->set_win32_console (_win32_console->GetValue ());
1813 wxSpinCtrl* _maximum_j2k_bandwidth;
1814 wxCheckBox* _allow_any_dcp_frame_rate;
1815 wxCheckBox* _only_servers_encode;
1816 NameFormatEditor* _dcp_metadata_filename_format;
1817 NameFormatEditor* _dcp_asset_filename_format;
1818 wxCheckBox* _log_general;
1819 wxCheckBox* _log_warning;
1820 wxCheckBox* _log_error;
1821 wxCheckBox* _log_timing;
1822 wxCheckBox* _log_debug_decode;
1823 wxCheckBox* _log_debug_encode;
1824 wxCheckBox* _log_debug_email;
1825 #ifdef DCPOMATIC_WINDOWS
1826 wxCheckBox* _win32_console;
1830 wxPreferencesEditor*
1831 create_config_dialog ()
1833 wxPreferencesEditor* e = new wxPreferencesEditor ();
1835 #ifdef DCPOMATIC_OSX
1836 /* Width that we force some of the config panels to be on OSX so that
1837 the containing window doesn't shrink too much when we select those panels.
1838 This is obviously an unpleasant hack.
1840 wxSize ps = wxSize (520, -1);
1841 int const border = 16;
1843 wxSize ps = wxSize (-1, -1);
1844 int const border = 8;
1847 e->AddPage (new GeneralPage (ps, border));
1848 e->AddPage (new DefaultsPage (ps, border));
1849 e->AddPage (new EncodingServersPage (ps, border));
1850 e->AddPage (new KeysPage (ps, border));
1851 e->AddPage (new TMSPage (ps, border));
1852 e->AddPage (new KDMEmailPage (ps, border));
1853 e->AddPage (new CoverSheetPage (ps, border));
1854 e->AddPage (new AdvancedPage (ps, border));