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 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
885 font = *wxSMALL_FONT;
886 font.SetWeight (wxFONTWEIGHT_BOLD);
887 _private_key_bad->SetFont (font);
888 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
891 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
892 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
893 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
894 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
895 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
896 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
897 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
898 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
900 SetSizerAndFit (_sizer);
903 void config_changed ()
905 _chain.reset (new dcp::CertificateChain (*_get().get ()));
907 update_certificate_list ();
908 update_private_key ();
909 update_sensitivity ();
912 void add_button (wxWindow* button)
914 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
919 void add_certificate ()
921 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
923 if (d->ShowModal() == wxID_OK) {
928 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
929 } catch (boost::filesystem::filesystem_error& e) {
930 error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
935 if (!extra.empty ()) {
938 _("This file contains other certificates (or other data) after its first certificate. "
939 "Only the first certificate will be used.")
943 if (!_chain->chain_valid ()) {
946 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
947 "Add certificates in order from root to intermediate to leaf.")
952 update_certificate_list ();
954 } catch (dcp::MiscError& e) {
955 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
961 update_sensitivity ();
964 void remove_certificate ()
966 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
971 _certificates->DeleteItem (i);
975 update_sensitivity ();
978 void export_certificate ()
980 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
985 wxFileDialog* d = new wxFileDialog (
986 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
987 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
990 dcp::CertificateChain::List all = _chain->root_to_leaf ();
991 dcp::CertificateChain::List::iterator j = all.begin ();
992 for (int k = 0; k < i; ++k) {
996 if (d->ShowModal () == wxID_OK) {
997 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
999 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1002 string const s = j->certificate (true);
1003 fwrite (s.c_str(), 1, s.length(), f);
1009 void update_certificate_list ()
1011 _certificates->DeleteAllItems ();
1013 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
1014 BOOST_FOREACH (dcp::Certificate const & i, certs) {
1017 _certificates->InsertItem (item);
1018 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
1021 _certificates->SetItem (n, 0, _("Root"));
1022 } else if (n == (certs.size() - 1)) {
1023 _certificates->SetItem (n, 0, _("Leaf"));
1025 _certificates->SetItem (n, 0, _("Intermediate"));
1031 static wxColour normal = _private_key_bad->GetForegroundColour ();
1033 if (_chain->private_key_valid ()) {
1034 _private_key_bad->Hide ();
1035 _private_key_bad->SetForegroundColour (normal);
1037 _private_key_bad->Show ();
1038 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
1042 void remake_certificates ()
1044 shared_ptr<const dcp::CertificateChain> chain = _get ();
1046 string subject_organization_name;
1047 string subject_organizational_unit_name;
1048 string root_common_name;
1049 string intermediate_common_name;
1050 string leaf_common_name;
1052 dcp::CertificateChain::List all = chain->root_to_leaf ();
1054 if (all.size() >= 1) {
1056 subject_organization_name = chain->root().subject_organization_name ();
1057 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1058 root_common_name = chain->root().subject_common_name ();
1061 if (all.size() >= 2) {
1063 leaf_common_name = chain->leaf().subject_common_name ();
1066 if (all.size() >= 3) {
1067 /* Have an intermediate */
1068 dcp::CertificateChain::List::iterator i = all.begin ();
1070 intermediate_common_name = i->subject_common_name ();
1075 MakeChainDialog* d = new MakeChainDialog (
1077 subject_organization_name,
1078 subject_organizational_unit_name,
1080 intermediate_common_name,
1084 if (d->ShowModal () == wxID_OK) {
1086 new dcp::CertificateChain (
1089 d->organisational_unit (),
1090 d->root_common_name (),
1091 d->intermediate_common_name (),
1092 d->leaf_common_name ()
1097 update_certificate_list ();
1098 update_private_key ();
1104 void update_sensitivity ()
1106 /* We can only remove the leaf certificate */
1107 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
1108 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1111 void update_private_key ()
1113 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1117 void load_private_key ()
1119 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1121 if (d->ShowModal() == wxID_OK) {
1123 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1124 if (boost::filesystem::file_size (p) > 8192) {
1127 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1132 _chain->set_key (dcp::file_to_string (p));
1134 update_private_key ();
1135 } catch (dcp::MiscError& e) {
1136 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1142 update_sensitivity ();
1145 void export_private_key ()
1147 optional<string> key = _chain->key ();
1152 wxFileDialog* d = new wxFileDialog (
1153 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1154 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1157 if (d->ShowModal () == wxID_OK) {
1158 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1160 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1163 string const s = _chain->key().get ();
1164 fwrite (s.c_str(), 1, s.length(), f);
1170 wxListCtrl* _certificates;
1171 wxButton* _add_certificate;
1172 wxButton* _export_certificate;
1173 wxButton* _remove_certificate;
1174 wxButton* _remake_certificates;
1175 wxStaticText* _private_key;
1176 wxButton* _load_private_key;
1177 wxButton* _export_private_key;
1178 wxStaticText* _private_key_bad;
1180 wxBoxSizer* _button_sizer;
1181 shared_ptr<dcp::CertificateChain> _chain;
1182 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1183 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1184 boost::function<void (void)> _nag_remake;
1187 class KeysPage : public StandardPage
1190 KeysPage (wxSize panel_size, int border)
1191 : StandardPage (panel_size, border)
1194 wxString GetName () const
1199 #ifdef DCPOMATIC_OSX
1200 wxBitmap GetLargeIcon () const
1202 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1210 _signer = new CertificateChainEditor (
1211 _panel, _("Signing DCPs and KDMs"), _border,
1212 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1213 boost::bind (&Config::signer_chain, Config::instance ()),
1214 boost::bind (&do_nothing)
1217 _panel->GetSizer()->Add (_signer);
1219 _decryption = new CertificateChainEditor (
1220 _panel, _("Decrypting KDMs"), _border,
1221 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1222 boost::bind (&Config::decryption_chain, Config::instance ()),
1223 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1226 _panel->GetSizer()->Add (_decryption);
1228 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1229 _decryption->add_button (_export_decryption_certificate);
1230 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1231 _decryption->add_button (_export_decryption_chain);
1233 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1234 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1237 void export_decryption_certificate ()
1239 wxFileDialog* d = new wxFileDialog (
1240 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1241 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1244 if (d->ShowModal () == wxID_OK) {
1245 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1247 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1250 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1251 fwrite (s.c_str(), 1, s.length(), f);
1257 void export_decryption_chain ()
1259 wxFileDialog* d = new wxFileDialog (
1260 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1261 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1264 if (d->ShowModal () == wxID_OK) {
1265 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1267 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1270 string const s = Config::instance()->decryption_chain()->chain();
1271 fwrite (s.c_str(), 1, s.length(), f);
1277 void config_changed ()
1279 _signer->config_changed ();
1280 _decryption->config_changed ();
1283 void nag_remake_decryption_chain ()
1285 NagDialog::maybe_nag (
1287 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1288 _("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!")
1292 CertificateChainEditor* _signer;
1293 CertificateChainEditor* _decryption;
1294 wxButton* _export_decryption_certificate;
1295 wxButton* _export_decryption_chain;
1298 class TMSPage : public StandardPage
1301 TMSPage (wxSize panel_size, int border)
1302 : StandardPage (panel_size, border)
1305 wxString GetName () const
1310 #ifdef DCPOMATIC_OSX
1311 wxBitmap GetLargeIcon () const
1313 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1320 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1321 table->AddGrowableCol (1, 1);
1322 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1324 add_label_to_sizer (table, _panel, _("Protocol"), true);
1325 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1326 table->Add (_tms_protocol, 1, wxEXPAND);
1328 add_label_to_sizer (table, _panel, _("IP address"), true);
1329 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1330 table->Add (_tms_ip, 1, wxEXPAND);
1332 add_label_to_sizer (table, _panel, _("Target path"), true);
1333 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1334 table->Add (_tms_path, 1, wxEXPAND);
1336 add_label_to_sizer (table, _panel, _("User name"), true);
1337 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1338 table->Add (_tms_user, 1, wxEXPAND);
1340 add_label_to_sizer (table, _panel, _("Password"), true);
1341 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1342 table->Add (_tms_password, 1, wxEXPAND);
1344 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1345 _tms_protocol->Append (_("FTP (for Dolby)"));
1347 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1348 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1349 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1350 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1351 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1354 void config_changed ()
1356 Config* config = Config::instance ();
1358 checked_set (_tms_protocol, config->tms_protocol ());
1359 checked_set (_tms_ip, config->tms_ip ());
1360 checked_set (_tms_path, config->tms_path ());
1361 checked_set (_tms_user, config->tms_user ());
1362 checked_set (_tms_password, config->tms_password ());
1365 void tms_protocol_changed ()
1367 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1370 void tms_ip_changed ()
1372 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1375 void tms_path_changed ()
1377 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1380 void tms_user_changed ()
1382 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1385 void tms_password_changed ()
1387 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1390 wxChoice* _tms_protocol;
1391 wxTextCtrl* _tms_ip;
1392 wxTextCtrl* _tms_path;
1393 wxTextCtrl* _tms_user;
1394 wxTextCtrl* _tms_password;
1403 class KDMEmailPage : public StandardPage
1407 KDMEmailPage (wxSize panel_size, int border)
1408 #ifdef DCPOMATIC_OSX
1409 /* We have to force both width and height of this one */
1410 : StandardPage (wxSize (480, 128), border)
1412 : StandardPage (panel_size, border)
1416 wxString GetName () const
1418 return _("KDM Email");
1421 #ifdef DCPOMATIC_OSX
1422 wxBitmap GetLargeIcon () const
1424 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1431 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1432 table->AddGrowableCol (1, 1);
1433 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1435 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1437 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1438 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1439 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1440 add_label_to_sizer (s, _panel, _("port"), false);
1441 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1442 _mail_port->SetRange (0, 65535);
1443 s->Add (_mail_port);
1444 table->Add (s, 1, wxEXPAND | wxALL);
1447 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1448 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1449 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1451 add_label_to_sizer (table, _panel, _("Mail password"), true);
1452 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1453 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1455 add_label_to_sizer (table, _panel, _("Subject"), true);
1456 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1457 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1459 add_label_to_sizer (table, _panel, _("From address"), true);
1460 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1461 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1463 vector<string> columns;
1464 columns.push_back (wx_to_std (_("Address")));
1465 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1466 _kdm_cc = new EditableList<string, EmailDialog> (
1469 bind (&Config::kdm_cc, Config::instance()),
1470 bind (&Config::set_kdm_cc, Config::instance(), _1),
1473 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1475 add_label_to_sizer (table, _panel, _("BCC address"), true);
1476 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1477 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1479 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1480 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1482 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1483 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1487 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1488 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1489 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1490 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1491 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1492 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1493 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1494 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1495 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1498 void config_changed ()
1500 Config* config = Config::instance ();
1502 checked_set (_mail_server, config->mail_server ());
1503 checked_set (_mail_port, config->mail_port ());
1504 checked_set (_mail_user, config->mail_user ());
1505 checked_set (_mail_password, config->mail_password ());
1506 checked_set (_kdm_subject, config->kdm_subject ());
1507 checked_set (_kdm_from, config->kdm_from ());
1508 checked_set (_kdm_bcc, config->kdm_bcc ());
1509 checked_set (_kdm_email, Config::instance()->kdm_email ());
1512 void mail_server_changed ()
1514 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1517 void mail_port_changed ()
1519 Config::instance()->set_mail_port (_mail_port->GetValue ());
1522 void mail_user_changed ()
1524 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1527 void mail_password_changed ()
1529 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1532 void kdm_subject_changed ()
1534 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1537 void kdm_from_changed ()
1539 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1542 void kdm_bcc_changed ()
1544 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1547 void kdm_email_changed ()
1549 if (_kdm_email->GetValue().IsEmpty ()) {
1550 /* Sometimes we get sent an erroneous notification that the email
1551 is empty; I don't know why.
1555 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1558 void reset_kdm_email ()
1560 Config::instance()->reset_kdm_email ();
1561 checked_set (_kdm_email, Config::instance()->kdm_email ());
1564 wxTextCtrl* _mail_server;
1565 wxSpinCtrl* _mail_port;
1566 wxTextCtrl* _mail_user;
1567 wxTextCtrl* _mail_password;
1568 wxTextCtrl* _kdm_subject;
1569 wxTextCtrl* _kdm_from;
1570 EditableList<string, EmailDialog>* _kdm_cc;
1571 wxTextCtrl* _kdm_bcc;
1572 wxTextCtrl* _kdm_email;
1573 wxButton* _reset_kdm_email;
1576 class CoverSheetPage : public StandardPage
1580 CoverSheetPage (wxSize panel_size, int border)
1581 #ifdef DCPOMATIC_OSX
1582 /* We have to force both width and height of this one */
1583 : StandardPage (wxSize (480, 128), border)
1585 : StandardPage (panel_size, border)
1589 wxString GetName () const
1591 return _("Cover Sheet");
1594 #ifdef DCPOMATIC_OSX
1595 wxBitmap GetLargeIcon () const
1597 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1604 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1605 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1607 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1608 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1610 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1611 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1614 void config_changed ()
1616 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1619 void cover_sheet_changed ()
1621 if (_cover_sheet->GetValue().IsEmpty ()) {
1622 /* Sometimes we get sent an erroneous notification that the cover sheet
1623 is empty; I don't know why.
1627 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1630 void reset_cover_sheet ()
1632 Config::instance()->reset_cover_sheet ();
1633 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1636 wxTextCtrl* _cover_sheet;
1637 wxButton* _reset_cover_sheet;
1641 /** @class AdvancedPage
1642 * @brief Advanced page of the preferences dialog.
1644 class AdvancedPage : public StockPage
1647 AdvancedPage (wxSize panel_size, int border)
1648 : StockPage (Kind_Advanced, panel_size, border)
1649 , _maximum_j2k_bandwidth (0)
1650 , _allow_any_dcp_frame_rate (0)
1651 , _only_servers_encode (0)
1656 , _log_debug_decode (0)
1657 , _log_debug_encode (0)
1658 , _log_debug_email (0)
1662 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1664 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1666 flags |= wxALIGN_RIGHT;
1669 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1670 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1675 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1676 table->AddGrowableCol (1, 1);
1677 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1680 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1681 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1682 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1683 s->Add (_maximum_j2k_bandwidth, 1);
1684 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1688 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1689 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1690 table->AddSpacer (0);
1692 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1693 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1694 table->AddSpacer (0);
1697 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1698 dcp::NameFormat::Map titles;
1699 titles['t'] = "type (cpl/pkl)";
1700 dcp::NameFormat::Map examples;
1701 examples['t'] = "cpl";
1702 _dcp_metadata_filename_format = new NameFormatEditor (
1703 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1705 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1709 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1710 dcp::NameFormat::Map titles;
1711 titles['t'] = "type (j2c/pcm/sub)";
1712 titles['r'] = "reel number";
1713 titles['n'] = "number of reels";
1714 titles['c'] = "content filename";
1715 dcp::NameFormat::Map examples;
1716 examples['t'] = "j2c";
1717 examples['r'] = "1";
1718 examples['n'] = "4";
1719 examples['c'] = "myfile.mp4";
1720 _dcp_asset_filename_format = new NameFormatEditor (
1721 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1723 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1727 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1728 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1729 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1730 t->Add (_log_general, 1, wxEXPAND | wxALL);
1731 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1732 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1733 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1734 t->Add (_log_error, 1, wxEXPAND | wxALL);
1735 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1736 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1737 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1738 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1739 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1740 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1741 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1742 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1743 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1744 table->Add (t, 0, wxALL, 6);
1747 #ifdef DCPOMATIC_WINDOWS
1748 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1749 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1750 table->AddSpacer (0);
1753 _maximum_j2k_bandwidth->SetRange (1, 1000);
1754 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1755 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1756 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1757 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1758 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1759 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1760 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1761 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1762 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1763 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1764 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1765 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1766 #ifdef DCPOMATIC_WINDOWS
1767 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1771 void config_changed ()
1773 Config* config = Config::instance ();
1775 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1776 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1777 checked_set (_only_servers_encode, config->only_servers_encode ());
1778 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1779 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1780 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1781 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1782 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1783 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1784 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1785 #ifdef DCPOMATIC_WINDOWS
1786 checked_set (_win32_console, config->win32_console());
1790 void maximum_j2k_bandwidth_changed ()
1792 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1795 void allow_any_dcp_frame_rate_changed ()
1797 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1800 void only_servers_encode_changed ()
1802 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1805 void dcp_metadata_filename_format_changed ()
1807 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1810 void dcp_asset_filename_format_changed ()
1812 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1818 if (_log_general->GetValue ()) {
1819 types |= LogEntry::TYPE_GENERAL;
1821 if (_log_warning->GetValue ()) {
1822 types |= LogEntry::TYPE_WARNING;
1824 if (_log_error->GetValue ()) {
1825 types |= LogEntry::TYPE_ERROR;
1827 if (_log_timing->GetValue ()) {
1828 types |= LogEntry::TYPE_TIMING;
1830 if (_log_debug_decode->GetValue ()) {
1831 types |= LogEntry::TYPE_DEBUG_DECODE;
1833 if (_log_debug_encode->GetValue ()) {
1834 types |= LogEntry::TYPE_DEBUG_ENCODE;
1836 if (_log_debug_email->GetValue ()) {
1837 types |= LogEntry::TYPE_DEBUG_EMAIL;
1839 Config::instance()->set_log_types (types);
1842 #ifdef DCPOMATIC_WINDOWS
1843 void win32_console_changed ()
1845 Config::instance()->set_win32_console (_win32_console->GetValue ());
1849 wxSpinCtrl* _maximum_j2k_bandwidth;
1850 wxCheckBox* _allow_any_dcp_frame_rate;
1851 wxCheckBox* _only_servers_encode;
1852 NameFormatEditor* _dcp_metadata_filename_format;
1853 NameFormatEditor* _dcp_asset_filename_format;
1854 wxCheckBox* _log_general;
1855 wxCheckBox* _log_warning;
1856 wxCheckBox* _log_error;
1857 wxCheckBox* _log_timing;
1858 wxCheckBox* _log_debug_decode;
1859 wxCheckBox* _log_debug_encode;
1860 wxCheckBox* _log_debug_email;
1861 #ifdef DCPOMATIC_WINDOWS
1862 wxCheckBox* _win32_console;
1866 wxPreferencesEditor*
1867 create_config_dialog ()
1869 wxPreferencesEditor* e = new wxPreferencesEditor ();
1871 #ifdef DCPOMATIC_OSX
1872 /* Width that we force some of the config panels to be on OSX so that
1873 the containing window doesn't shrink too much when we select those panels.
1874 This is obviously an unpleasant hack.
1876 wxSize ps = wxSize (520, -1);
1877 int const border = 16;
1879 wxSize ps = wxSize (-1, -1);
1880 int const border = 8;
1883 e->AddPage (new GeneralPage (ps, border));
1884 e->AddPage (new DefaultsPage (ps, border));
1885 e->AddPage (new EncodingServersPage (ps, border));
1886 e->AddPage (new KeysPage (ps, border));
1887 e->AddPage (new TMSPage (ps, border));
1888 e->AddPage (new KDMEmailPage (ps, border));
1889 e->AddPage (new CoverSheetPage (ps, border));
1890 e->AddPage (new AdvancedPage (ps, border));