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 "config_move_dialog.h"
38 #include "lib/config.h"
39 #include "lib/ratio.h"
40 #include "lib/filter.h"
41 #include "lib/dcp_content_type.h"
44 #include "lib/cross.h"
45 #include "lib/exceptions.h"
46 #include <dcp/locale_convert.h>
47 #include <dcp/exceptions.h>
48 #include <dcp/certificate_chain.h>
49 #include <wx/stdpaths.h>
50 #include <wx/preferences.h>
51 #include <wx/spinctrl.h>
52 #include <wx/filepicker.h>
54 #include <boost/filesystem.hpp>
55 #include <boost/foreach.hpp>
66 using boost::shared_ptr;
67 using boost::function;
68 using boost::optional;
69 using dcp::locale_convert;
81 Page (wxSize panel_size, int border)
84 , _panel_size (panel_size)
85 , _window_exists (false)
87 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
93 wxWindow* create_window (wxWindow* parent)
95 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
96 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
100 _window_exists = true;
103 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
112 virtual void config_changed () = 0;
113 virtual void setup () = 0;
115 void config_changed_wrapper ()
117 if (_window_exists) {
122 void window_destroyed ()
124 _window_exists = false;
128 boost::signals2::scoped_connection _config_connection;
132 class StockPage : public wxStockPreferencesPage, public Page
135 StockPage (Kind kind, wxSize panel_size, int border)
136 : wxStockPreferencesPage (kind)
137 , Page (panel_size, border)
140 wxWindow* CreateWindow (wxWindow* parent)
142 return create_window (parent);
146 class StandardPage : public wxPreferencesPage, public Page
149 StandardPage (wxSize panel_size, int border)
150 : Page (panel_size, border)
153 wxWindow* CreateWindow (wxWindow* parent)
155 return create_window (parent);
159 class GeneralPage : public StockPage
162 GeneralPage (wxSize panel_size, int border)
163 : StockPage (Kind_General, panel_size, border)
169 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
170 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
173 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
174 table->Add (_set_language, wxGBPosition (r, 0));
175 _language = new wxChoice (_panel, wxID_ANY);
176 vector<pair<string, string> > languages;
177 languages.push_back (make_pair ("Čeština", "cs_CZ"));
178 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
179 languages.push_back (make_pair ("Dansk", "da_DK"));
180 languages.push_back (make_pair ("Deutsch", "de_DE"));
181 languages.push_back (make_pair ("English", "en_GB"));
182 languages.push_back (make_pair ("Español", "es_ES"));
183 languages.push_back (make_pair ("Français", "fr_FR"));
184 languages.push_back (make_pair ("Italiano", "it_IT"));
185 languages.push_back (make_pair ("Nederlands", "nl_NL"));
186 languages.push_back (make_pair ("Русский", "ru_RU"));
187 languages.push_back (make_pair ("Polski", "pl_PL"));
188 languages.push_back (make_pair ("Português europeu", "pt_PT"));
189 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
190 languages.push_back (make_pair ("Svenska", "sv_SE"));
191 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
192 languages.push_back (make_pair ("українська мова", "uk_UA"));
193 checked_set (_language, languages);
194 table->Add (_language, wxGBPosition (r, 1));
197 wxStaticText* restart = add_label_to_sizer (
198 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
200 wxFont font = restart->GetFont();
201 font.SetStyle (wxFONTSTYLE_ITALIC);
202 font.SetPointSize (font.GetPointSize() - 1);
203 restart->SetFont (font);
206 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
207 _master_encoding_threads = new wxSpinCtrl (_panel);
208 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
211 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
212 _server_encoding_threads = new wxSpinCtrl (_panel);
213 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
216 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
217 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true);
218 table->Add (_config_file, wxGBPosition (r, 1));
221 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
222 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
223 table->Add (_cinemas_file, wxGBPosition (r, 1));
226 _preview_sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound in the preview via"));
227 table->Add (_preview_sound, wxGBPosition (r, 0));
228 _preview_sound_output = new wxChoice (_panel, wxID_ANY);
229 table->Add (_preview_sound_output, wxGBPosition (r, 1));
232 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
233 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
234 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
238 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
239 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
242 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
243 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
246 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
247 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
250 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
251 bottom_table->AddGrowableCol (1, 1);
253 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
254 _issuer = new wxTextCtrl (_panel, wxID_ANY);
255 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
257 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
258 _creator = new wxTextCtrl (_panel, wxID_ANY);
259 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
261 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
264 RtAudio audio (DCPOMATIC_RTAUDIO_API);
265 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
266 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
267 if (dev.probed && dev.outputChannels > 0) {
268 _preview_sound_output->Append (std_to_wx (dev.name));
272 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
273 _language->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::language_changed, this));
274 _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::config_file_changed, this));
275 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
276 _preview_sound->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::preview_sound_changed, this));
277 _preview_sound_output->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::preview_sound_output_changed, this));
279 _master_encoding_threads->SetRange (1, 128);
280 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
281 _server_encoding_threads->SetRange (1, 128);
282 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::server_encoding_threads_changed, this));
284 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
285 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
287 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
288 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
289 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
291 _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
292 _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
295 void config_changed ()
297 Config* config = Config::instance ();
299 checked_set (_set_language, static_cast<bool>(config->language()));
301 /* Backwards compatibility of config file */
303 map<string, string> compat_map;
304 compat_map["fr"] = "fr_FR";
305 compat_map["it"] = "it_IT";
306 compat_map["es"] = "es_ES";
307 compat_map["sv"] = "sv_SE";
308 compat_map["de"] = "de_DE";
309 compat_map["nl"] = "nl_NL";
310 compat_map["ru"] = "ru_RU";
311 compat_map["pl"] = "pl_PL";
312 compat_map["da"] = "da_DK";
313 compat_map["pt"] = "pt_PT";
314 compat_map["sk"] = "sk_SK";
315 compat_map["cs"] = "cs_CZ";
316 compat_map["uk"] = "uk_UA";
318 string lang = config->language().get_value_or ("en_GB");
319 if (compat_map.find (lang) != compat_map.end ()) {
320 lang = compat_map[lang];
323 checked_set (_language, lang);
325 checked_set (_master_encoding_threads, config->master_encoding_threads ());
326 checked_set (_server_encoding_threads, config->server_encoding_threads ());
327 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
328 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
330 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
331 checked_set (_check_for_updates, config->check_for_updates ());
332 checked_set (_check_for_test_updates, config->check_for_test_updates ());
333 checked_set (_issuer, config->dcp_issuer ());
334 checked_set (_creator, config->dcp_creator ());
335 checked_set (_config_file, config->config_file());
336 checked_set (_cinemas_file, config->cinemas_file());
337 checked_set (_preview_sound, config->preview_sound());
339 optional<string> const current_so = get_preview_sound_output ();
340 optional<string> configured_so;
342 if (config->preview_sound_output()) {
343 configured_so = config->preview_sound_output().get();
345 /* No configured output means we should use the default */
346 RtAudio audio (DCPOMATIC_RTAUDIO_API);
348 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
349 } catch (RtAudioError& e) {
350 /* Probably no audio devices at all */
354 if (configured_so && current_so != configured_so) {
355 /* Update _preview_sound_output with the configured value */
357 while (i < _preview_sound_output->GetCount()) {
358 if (_preview_sound_output->GetString(i) == std_to_wx(*configured_so)) {
359 _preview_sound_output->SetSelection (i);
366 setup_sensitivity ();
369 /** @return Currently-selected preview sound output in the dialogue */
370 optional<string> get_preview_sound_output ()
372 int const sel = _preview_sound_output->GetSelection ();
373 if (sel == wxNOT_FOUND) {
374 return optional<string> ();
377 return wx_to_std (_preview_sound_output->GetString (sel));
380 void setup_sensitivity ()
382 _language->Enable (_set_language->GetValue ());
383 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
384 _preview_sound_output->Enable (_preview_sound->GetValue ());
387 void set_language_changed ()
389 setup_sensitivity ();
390 if (_set_language->GetValue ()) {
393 Config::instance()->unset_language ();
397 void language_changed ()
399 int const sel = _language->GetSelection ();
401 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
403 Config::instance()->unset_language ();
407 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
408 void analyse_ebur128_changed ()
410 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
414 void automatic_audio_analysis_changed ()
416 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
419 void check_for_updates_changed ()
421 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
424 void check_for_test_updates_changed ()
426 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
429 void master_encoding_threads_changed ()
431 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
434 void server_encoding_threads_changed ()
436 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
439 void issuer_changed ()
441 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
444 void creator_changed ()
446 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
449 void config_file_changed ()
451 Config* config = Config::instance();
452 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
453 if (new_file == config->config_file()) {
456 bool copy_and_link = true;
457 if (boost::filesystem::exists(new_file)) {
458 ConfigMoveDialog* d = new ConfigMoveDialog (_panel, new_file);
459 if (d->ShowModal() == wxID_OK) {
460 copy_and_link = false;
467 if (new_file != config->config_file()) {
468 config->copy_and_link (new_file);
471 config->link (new_file);
475 void cinemas_file_changed ()
477 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
480 void preview_sound_changed ()
482 Config::instance()->set_preview_sound (_preview_sound->GetValue ());
485 void preview_sound_output_changed ()
487 RtAudio audio (DCPOMATIC_RTAUDIO_API);
488 optional<string> const so = get_preview_sound_output();
489 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
490 Config::instance()->unset_preview_sound_output ();
492 Config::instance()->set_preview_sound_output (*so);
496 wxCheckBox* _set_language;
498 wxSpinCtrl* _master_encoding_threads;
499 wxSpinCtrl* _server_encoding_threads;
500 FilePickerCtrl* _config_file;
501 FilePickerCtrl* _cinemas_file;
502 wxCheckBox* _preview_sound;
503 wxChoice* _preview_sound_output;
504 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
505 wxCheckBox* _analyse_ebur128;
507 wxCheckBox* _automatic_audio_analysis;
508 wxCheckBox* _check_for_updates;
509 wxCheckBox* _check_for_test_updates;
511 wxTextCtrl* _creator;
514 class DefaultsPage : public StandardPage
517 DefaultsPage (wxSize panel_size, int border)
518 : StandardPage (panel_size, border)
521 wxString GetName () const
523 return _("Defaults");
527 wxBitmap GetLargeIcon () const
529 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
536 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
537 table->AddGrowableCol (1, 1);
538 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
541 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
542 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
543 _still_length = new wxSpinCtrl (_panel);
544 s->Add (_still_length);
545 add_label_to_sizer (s, _panel, _("s"), false);
549 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
550 #ifdef DCPOMATIC_USE_OWN_PICKER
551 _directory = new DirPickerCtrl (_panel);
553 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
555 table->Add (_directory, 1, wxEXPAND);
557 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
558 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
559 table->Add (_isdcf_metadata_button);
561 add_label_to_sizer (table, _panel, _("Default container"), true);
562 _container = new wxChoice (_panel, wxID_ANY);
563 table->Add (_container);
565 add_label_to_sizer (table, _panel, _("Default scale-to"), true);
566 _scale_to = new wxChoice (_panel, wxID_ANY);
567 table->Add (_scale_to);
569 add_label_to_sizer (table, _panel, _("Default content type"), true);
570 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
571 table->Add (_dcp_content_type);
573 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
574 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
575 table->Add (_dcp_audio_channels);
578 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
579 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
580 _j2k_bandwidth = new wxSpinCtrl (_panel);
581 s->Add (_j2k_bandwidth);
582 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
587 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
588 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
589 _audio_delay = new wxSpinCtrl (_panel);
590 s->Add (_audio_delay);
591 add_label_to_sizer (s, _panel, _("ms"), false);
595 add_label_to_sizer (table, _panel, _("Default standard"), true);
596 _standard = new wxChoice (_panel, wxID_ANY);
597 table->Add (_standard);
599 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
600 #ifdef DCPOMATIC_USE_OWN_PICKER
601 _kdm_directory = new DirPickerCtrl (_panel);
603 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
605 table->Add (_kdm_directory, 1, wxEXPAND);
607 _still_length->SetRange (1, 3600);
608 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
610 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
611 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
613 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
615 BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
616 _container->Append (std_to_wx(i->container_nickname()));
619 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
621 _scale_to->Append (_("Guess from content"));
623 BOOST_FOREACH (Ratio const * i, Ratio::all()) {
624 _scale_to->Append (std_to_wx(i->image_nickname()));
627 _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
629 BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
630 _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
633 setup_audio_channels_choice (_dcp_audio_channels, 2);
635 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
636 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
638 _j2k_bandwidth->SetRange (50, 250);
639 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
641 _audio_delay->SetRange (-1000, 1000);
642 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
644 _standard->Append (_("SMPTE"));
645 _standard->Append (_("Interop"));
646 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
649 void config_changed ()
651 Config* config = Config::instance ();
653 vector<Ratio const *> containers = Ratio::containers ();
654 for (size_t i = 0; i < containers.size(); ++i) {
655 if (containers[i] == config->default_container ()) {
656 _container->SetSelection (i);
660 vector<Ratio const *> ratios = Ratio::all ();
661 for (size_t i = 0; i < ratios.size(); ++i) {
662 if (ratios[i] == config->default_scale_to ()) {
663 _scale_to->SetSelection (i + 1);
667 if (!config->default_scale_to()) {
668 _scale_to->SetSelection (0);
671 vector<DCPContentType const *> const ct = DCPContentType::all ();
672 for (size_t i = 0; i < ct.size(); ++i) {
673 if (ct[i] == config->default_dcp_content_type ()) {
674 _dcp_content_type->SetSelection (i);
678 checked_set (_still_length, config->default_still_length ());
679 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
680 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
681 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
682 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
683 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
684 checked_set (_audio_delay, config->default_audio_delay ());
685 checked_set (_standard, config->default_interop() ? 1 : 0);
688 void j2k_bandwidth_changed ()
690 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
693 void audio_delay_changed ()
695 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
698 void dcp_audio_channels_changed ()
700 int const s = _dcp_audio_channels->GetSelection ();
701 if (s != wxNOT_FOUND) {
702 Config::instance()->set_default_dcp_audio_channels (
703 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
708 void directory_changed ()
710 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
713 void kdm_directory_changed ()
715 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
718 void edit_isdcf_metadata_clicked ()
720 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
722 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
726 void still_length_changed ()
728 Config::instance()->set_default_still_length (_still_length->GetValue ());
731 void container_changed ()
733 vector<Ratio const *> ratio = Ratio::containers ();
734 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
737 void scale_to_changed ()
739 int const s = _scale_to->GetSelection ();
741 Config::instance()->set_default_scale_to (0);
743 vector<Ratio const *> ratio = Ratio::all ();
744 Config::instance()->set_default_scale_to (ratio[s - 1]);
748 void dcp_content_type_changed ()
750 vector<DCPContentType const *> ct = DCPContentType::all ();
751 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
754 void standard_changed ()
756 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
759 wxSpinCtrl* _j2k_bandwidth;
760 wxSpinCtrl* _audio_delay;
761 wxButton* _isdcf_metadata_button;
762 wxSpinCtrl* _still_length;
763 #ifdef DCPOMATIC_USE_OWN_PICKER
764 DirPickerCtrl* _directory;
765 DirPickerCtrl* _kdm_directory;
767 wxDirPickerCtrl* _directory;
768 wxDirPickerCtrl* _kdm_directory;
770 wxChoice* _container;
772 wxChoice* _dcp_content_type;
773 wxChoice* _dcp_audio_channels;
777 class EncodingServersPage : public StandardPage
780 EncodingServersPage (wxSize panel_size, int border)
781 : StandardPage (panel_size, border)
784 wxString GetName () const
790 wxBitmap GetLargeIcon () const
792 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
799 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
800 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
802 vector<string> columns;
803 columns.push_back (wx_to_std (_("IP address / host name")));
804 _servers_list = new EditableList<string, ServerDialog> (
807 boost::bind (&Config::servers, Config::instance()),
808 boost::bind (&Config::set_servers, Config::instance(), _1),
809 boost::bind (&EncodingServersPage::server_column, this, _1)
812 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
814 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
817 void config_changed ()
819 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
820 _servers_list->refresh ();
823 void use_any_servers_changed ()
825 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
828 string server_column (string s)
833 wxCheckBox* _use_any_servers;
834 EditableList<string, ServerDialog>* _servers_list;
837 class CertificateChainEditor : public wxPanel
840 CertificateChainEditor (
844 function<void (shared_ptr<dcp::CertificateChain>)> set,
845 function<shared_ptr<const dcp::CertificateChain> (void)> get,
846 function<void (void)> nag_remake
851 , _nag_remake (nag_remake)
853 wxFont subheading_font (*wxNORMAL_FONT);
854 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
856 _sizer = new wxBoxSizer (wxVERTICAL);
859 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
860 m->SetFont (subheading_font);
861 _sizer->Add (m, 0, wxALL, border);
864 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
865 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
867 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
872 ip.SetText (_("Type"));
874 _certificates->InsertColumn (0, ip);
880 ip.SetText (_("Thumbprint"));
883 wxFont font = ip.GetFont ();
884 font.SetFamily (wxFONTFAMILY_TELETYPE);
887 _certificates->InsertColumn (1, ip);
890 certificates_sizer->Add (_certificates, 1, wxEXPAND);
893 wxSizer* s = new wxBoxSizer (wxVERTICAL);
894 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
895 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
896 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
897 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
898 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
899 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
900 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
903 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
904 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
907 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
908 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
909 wxFont font = _private_key->GetFont ();
910 font.SetFamily (wxFONTFAMILY_TELETYPE);
911 _private_key->SetFont (font);
912 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
913 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
914 table->Add (_load_private_key, wxGBPosition (r, 2));
915 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
916 table->Add (_export_private_key, wxGBPosition (r, 3));
919 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
920 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
921 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
922 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
925 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
926 font = *wxSMALL_FONT;
927 font.SetWeight (wxFONTWEIGHT_BOLD);
928 _private_key_bad->SetFont (font);
929 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
932 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
933 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
934 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
935 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
936 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
937 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
938 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
939 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
941 SetSizerAndFit (_sizer);
944 void config_changed ()
946 _chain.reset (new dcp::CertificateChain (*_get().get ()));
948 update_certificate_list ();
949 update_private_key ();
950 update_sensitivity ();
953 void add_button (wxWindow* button)
955 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
960 void add_certificate ()
962 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
964 if (d->ShowModal() == wxID_OK) {
969 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
970 } catch (boost::filesystem::filesystem_error& e) {
971 error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
976 if (!extra.empty ()) {
979 _("This file contains other certificates (or other data) after its first certificate. "
980 "Only the first certificate will be used.")
984 if (!_chain->chain_valid ()) {
987 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
988 "Add certificates in order from root to intermediate to leaf.")
993 update_certificate_list ();
995 } catch (dcp::MiscError& e) {
996 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1002 update_sensitivity ();
1005 void remove_certificate ()
1007 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1012 _certificates->DeleteItem (i);
1016 update_sensitivity ();
1019 void export_certificate ()
1021 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1026 wxFileDialog* d = new wxFileDialog (
1027 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1028 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1031 dcp::CertificateChain::List all = _chain->root_to_leaf ();
1032 dcp::CertificateChain::List::iterator j = all.begin ();
1033 for (int k = 0; k < i; ++k) {
1037 if (d->ShowModal () == wxID_OK) {
1038 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1040 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1043 string const s = j->certificate (true);
1044 fwrite (s.c_str(), 1, s.length(), f);
1050 void update_certificate_list ()
1052 _certificates->DeleteAllItems ();
1054 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
1055 BOOST_FOREACH (dcp::Certificate const & i, certs) {
1058 _certificates->InsertItem (item);
1059 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
1062 _certificates->SetItem (n, 0, _("Root"));
1063 } else if (n == (certs.size() - 1)) {
1064 _certificates->SetItem (n, 0, _("Leaf"));
1066 _certificates->SetItem (n, 0, _("Intermediate"));
1072 static wxColour normal = _private_key_bad->GetForegroundColour ();
1074 if (_chain->private_key_valid ()) {
1075 _private_key_bad->Hide ();
1076 _private_key_bad->SetForegroundColour (normal);
1078 _private_key_bad->Show ();
1079 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
1083 void remake_certificates ()
1085 shared_ptr<const dcp::CertificateChain> chain = _get ();
1087 string subject_organization_name;
1088 string subject_organizational_unit_name;
1089 string root_common_name;
1090 string intermediate_common_name;
1091 string leaf_common_name;
1093 dcp::CertificateChain::List all = chain->root_to_leaf ();
1095 if (all.size() >= 1) {
1097 subject_organization_name = chain->root().subject_organization_name ();
1098 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
1099 root_common_name = chain->root().subject_common_name ();
1102 if (all.size() >= 2) {
1104 leaf_common_name = chain->leaf().subject_common_name ();
1107 if (all.size() >= 3) {
1108 /* Have an intermediate */
1109 dcp::CertificateChain::List::iterator i = all.begin ();
1111 intermediate_common_name = i->subject_common_name ();
1116 MakeChainDialog* d = new MakeChainDialog (
1118 subject_organization_name,
1119 subject_organizational_unit_name,
1121 intermediate_common_name,
1125 if (d->ShowModal () == wxID_OK) {
1127 new dcp::CertificateChain (
1130 d->organisational_unit (),
1131 d->root_common_name (),
1132 d->intermediate_common_name (),
1133 d->leaf_common_name ()
1138 update_certificate_list ();
1139 update_private_key ();
1145 void update_sensitivity ()
1147 /* We can only remove the leaf certificate */
1148 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
1149 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1152 void update_private_key ()
1154 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1158 void load_private_key ()
1160 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1162 if (d->ShowModal() == wxID_OK) {
1164 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1165 if (boost::filesystem::file_size (p) > 8192) {
1168 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1173 _chain->set_key (dcp::file_to_string (p));
1175 update_private_key ();
1176 } catch (dcp::MiscError& e) {
1177 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1183 update_sensitivity ();
1186 void export_private_key ()
1188 optional<string> key = _chain->key ();
1193 wxFileDialog* d = new wxFileDialog (
1194 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1195 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1198 if (d->ShowModal () == wxID_OK) {
1199 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1201 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1204 string const s = _chain->key().get ();
1205 fwrite (s.c_str(), 1, s.length(), f);
1211 wxListCtrl* _certificates;
1212 wxButton* _add_certificate;
1213 wxButton* _export_certificate;
1214 wxButton* _remove_certificate;
1215 wxButton* _remake_certificates;
1216 wxStaticText* _private_key;
1217 wxButton* _load_private_key;
1218 wxButton* _export_private_key;
1219 wxStaticText* _private_key_bad;
1221 wxBoxSizer* _button_sizer;
1222 shared_ptr<dcp::CertificateChain> _chain;
1223 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1224 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1225 boost::function<void (void)> _nag_remake;
1228 class KeysPage : public StandardPage
1231 KeysPage (wxSize panel_size, int border)
1232 : StandardPage (panel_size, border)
1235 wxString GetName () const
1240 #ifdef DCPOMATIC_OSX
1241 wxBitmap GetLargeIcon () const
1243 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1251 _signer = new CertificateChainEditor (
1252 _panel, _("Signing DCPs and KDMs"), _border,
1253 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1254 boost::bind (&Config::signer_chain, Config::instance ()),
1255 boost::bind (&do_nothing)
1258 _panel->GetSizer()->Add (_signer);
1260 _decryption = new CertificateChainEditor (
1261 _panel, _("Decrypting KDMs"), _border,
1262 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1263 boost::bind (&Config::decryption_chain, Config::instance ()),
1264 boost::bind (&KeysPage::nag_remake_decryption_chain, this)
1267 _panel->GetSizer()->Add (_decryption);
1269 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
1270 _decryption->add_button (_export_decryption_certificate);
1271 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
1272 _decryption->add_button (_export_decryption_chain);
1274 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1275 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1278 void export_decryption_certificate ()
1280 wxFileDialog* d = new wxFileDialog (
1281 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1282 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1285 if (d->ShowModal () == wxID_OK) {
1286 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1288 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1291 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1292 fwrite (s.c_str(), 1, s.length(), f);
1298 void export_decryption_chain ()
1300 wxFileDialog* d = new wxFileDialog (
1301 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1302 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1305 if (d->ShowModal () == wxID_OK) {
1306 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1308 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1311 string const s = Config::instance()->decryption_chain()->chain();
1312 fwrite (s.c_str(), 1, s.length(), f);
1318 void config_changed ()
1320 _signer->config_changed ();
1321 _decryption->config_changed ();
1324 void nag_remake_decryption_chain ()
1326 NagDialog::maybe_nag (
1328 Config::NAG_REMAKE_DECRYPTION_CHAIN,
1329 _("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!")
1333 CertificateChainEditor* _signer;
1334 CertificateChainEditor* _decryption;
1335 wxButton* _export_decryption_certificate;
1336 wxButton* _export_decryption_chain;
1339 class TMSPage : public StandardPage
1342 TMSPage (wxSize panel_size, int border)
1343 : StandardPage (panel_size, border)
1346 wxString GetName () const
1351 #ifdef DCPOMATIC_OSX
1352 wxBitmap GetLargeIcon () const
1354 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1361 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1362 table->AddGrowableCol (1, 1);
1363 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1365 add_label_to_sizer (table, _panel, _("Protocol"), true);
1366 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1367 table->Add (_tms_protocol, 1, wxEXPAND);
1369 add_label_to_sizer (table, _panel, _("IP address"), true);
1370 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1371 table->Add (_tms_ip, 1, wxEXPAND);
1373 add_label_to_sizer (table, _panel, _("Target path"), true);
1374 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1375 table->Add (_tms_path, 1, wxEXPAND);
1377 add_label_to_sizer (table, _panel, _("User name"), true);
1378 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1379 table->Add (_tms_user, 1, wxEXPAND);
1381 add_label_to_sizer (table, _panel, _("Password"), true);
1382 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1383 table->Add (_tms_password, 1, wxEXPAND);
1385 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1386 _tms_protocol->Append (_("FTP (for Dolby)"));
1388 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1389 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1390 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1391 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1392 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1395 void config_changed ()
1397 Config* config = Config::instance ();
1399 checked_set (_tms_protocol, config->tms_protocol ());
1400 checked_set (_tms_ip, config->tms_ip ());
1401 checked_set (_tms_path, config->tms_path ());
1402 checked_set (_tms_user, config->tms_user ());
1403 checked_set (_tms_password, config->tms_password ());
1406 void tms_protocol_changed ()
1408 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1411 void tms_ip_changed ()
1413 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1416 void tms_path_changed ()
1418 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1421 void tms_user_changed ()
1423 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1426 void tms_password_changed ()
1428 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1431 wxChoice* _tms_protocol;
1432 wxTextCtrl* _tms_ip;
1433 wxTextCtrl* _tms_path;
1434 wxTextCtrl* _tms_user;
1435 wxTextCtrl* _tms_password;
1444 class KDMEmailPage : public StandardPage
1448 KDMEmailPage (wxSize panel_size, int border)
1449 #ifdef DCPOMATIC_OSX
1450 /* We have to force both width and height of this one */
1451 : StandardPage (wxSize (480, 128), border)
1453 : StandardPage (panel_size, border)
1457 wxString GetName () const
1459 return _("KDM Email");
1462 #ifdef DCPOMATIC_OSX
1463 wxBitmap GetLargeIcon () const
1465 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1472 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1473 table->AddGrowableCol (1, 1);
1474 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1476 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1478 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1479 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1480 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1481 add_label_to_sizer (s, _panel, _("port"), false);
1482 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1483 _mail_port->SetRange (0, 65535);
1484 s->Add (_mail_port);
1485 table->Add (s, 1, wxEXPAND | wxALL);
1488 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1489 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1490 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1492 add_label_to_sizer (table, _panel, _("Mail password"), true);
1493 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1494 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1496 add_label_to_sizer (table, _panel, _("Subject"), true);
1497 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1498 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1500 add_label_to_sizer (table, _panel, _("From address"), true);
1501 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1502 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1504 vector<string> columns;
1505 columns.push_back (wx_to_std (_("Address")));
1506 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1507 _kdm_cc = new EditableList<string, EmailDialog> (
1510 bind (&Config::kdm_cc, Config::instance()),
1511 bind (&Config::set_kdm_cc, Config::instance(), _1),
1514 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1516 add_label_to_sizer (table, _panel, _("BCC address"), true);
1517 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1518 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1520 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1521 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1523 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1524 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1528 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1529 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1530 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1531 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1532 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1533 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1534 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1535 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1536 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1539 void config_changed ()
1541 Config* config = Config::instance ();
1543 checked_set (_mail_server, config->mail_server ());
1544 checked_set (_mail_port, config->mail_port ());
1545 checked_set (_mail_user, config->mail_user ());
1546 checked_set (_mail_password, config->mail_password ());
1547 checked_set (_kdm_subject, config->kdm_subject ());
1548 checked_set (_kdm_from, config->kdm_from ());
1549 checked_set (_kdm_bcc, config->kdm_bcc ());
1550 checked_set (_kdm_email, Config::instance()->kdm_email ());
1553 void mail_server_changed ()
1555 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1558 void mail_port_changed ()
1560 Config::instance()->set_mail_port (_mail_port->GetValue ());
1563 void mail_user_changed ()
1565 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1568 void mail_password_changed ()
1570 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1573 void kdm_subject_changed ()
1575 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1578 void kdm_from_changed ()
1580 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1583 void kdm_bcc_changed ()
1585 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1588 void kdm_email_changed ()
1590 if (_kdm_email->GetValue().IsEmpty ()) {
1591 /* Sometimes we get sent an erroneous notification that the email
1592 is empty; I don't know why.
1596 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1599 void reset_kdm_email ()
1601 Config::instance()->reset_kdm_email ();
1602 checked_set (_kdm_email, Config::instance()->kdm_email ());
1605 wxTextCtrl* _mail_server;
1606 wxSpinCtrl* _mail_port;
1607 wxTextCtrl* _mail_user;
1608 wxTextCtrl* _mail_password;
1609 wxTextCtrl* _kdm_subject;
1610 wxTextCtrl* _kdm_from;
1611 EditableList<string, EmailDialog>* _kdm_cc;
1612 wxTextCtrl* _kdm_bcc;
1613 wxTextCtrl* _kdm_email;
1614 wxButton* _reset_kdm_email;
1617 class CoverSheetPage : public StandardPage
1621 CoverSheetPage (wxSize panel_size, int border)
1622 #ifdef DCPOMATIC_OSX
1623 /* We have to force both width and height of this one */
1624 : StandardPage (wxSize (480, 128), border)
1626 : StandardPage (panel_size, border)
1630 wxString GetName () const
1632 return _("Cover Sheet");
1635 #ifdef DCPOMATIC_OSX
1636 wxBitmap GetLargeIcon () const
1638 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1645 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1646 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1648 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1649 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1651 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1652 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1655 void config_changed ()
1657 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1660 void cover_sheet_changed ()
1662 if (_cover_sheet->GetValue().IsEmpty ()) {
1663 /* Sometimes we get sent an erroneous notification that the cover sheet
1664 is empty; I don't know why.
1668 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1671 void reset_cover_sheet ()
1673 Config::instance()->reset_cover_sheet ();
1674 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1677 wxTextCtrl* _cover_sheet;
1678 wxButton* _reset_cover_sheet;
1682 /** @class AdvancedPage
1683 * @brief Advanced page of the preferences dialog.
1685 class AdvancedPage : public StockPage
1688 AdvancedPage (wxSize panel_size, int border)
1689 : StockPage (Kind_Advanced, panel_size, border)
1690 , _maximum_j2k_bandwidth (0)
1691 , _allow_any_dcp_frame_rate (0)
1692 , _only_servers_encode (0)
1697 , _log_debug_decode (0)
1698 , _log_debug_encode (0)
1699 , _log_debug_email (0)
1703 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1705 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1707 flags |= wxALIGN_RIGHT;
1710 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1711 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1716 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1717 table->AddGrowableCol (1, 1);
1718 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1721 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1722 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1723 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1724 s->Add (_maximum_j2k_bandwidth, 1);
1725 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1729 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1730 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1731 table->AddSpacer (0);
1733 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1734 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1735 table->AddSpacer (0);
1738 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1739 dcp::NameFormat::Map titles;
1740 titles['t'] = "type (cpl/pkl)";
1741 dcp::NameFormat::Map examples;
1742 examples['t'] = "cpl";
1743 _dcp_metadata_filename_format = new NameFormatEditor (
1744 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1746 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1750 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1751 dcp::NameFormat::Map titles;
1752 titles['t'] = "type (j2c/pcm/sub)";
1753 titles['r'] = "reel number";
1754 titles['n'] = "number of reels";
1755 titles['c'] = "content filename";
1756 dcp::NameFormat::Map examples;
1757 examples['t'] = "j2c";
1758 examples['r'] = "1";
1759 examples['n'] = "4";
1760 examples['c'] = "myfile.mp4";
1761 _dcp_asset_filename_format = new NameFormatEditor (
1762 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1764 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1768 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1769 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1770 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1771 t->Add (_log_general, 1, wxEXPAND | wxALL);
1772 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1773 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1774 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1775 t->Add (_log_error, 1, wxEXPAND | wxALL);
1776 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1777 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1778 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1779 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1780 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1781 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1782 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1783 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1784 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1785 table->Add (t, 0, wxALL, 6);
1788 #ifdef DCPOMATIC_WINDOWS
1789 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1790 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1791 table->AddSpacer (0);
1794 _maximum_j2k_bandwidth->SetRange (1, 1000);
1795 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1796 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1797 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1798 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1799 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1800 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1801 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1802 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1803 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1804 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1805 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1806 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1807 #ifdef DCPOMATIC_WINDOWS
1808 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1812 void config_changed ()
1814 Config* config = Config::instance ();
1816 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1817 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1818 checked_set (_only_servers_encode, config->only_servers_encode ());
1819 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1820 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1821 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1822 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1823 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1824 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1825 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1826 #ifdef DCPOMATIC_WINDOWS
1827 checked_set (_win32_console, config->win32_console());
1831 void maximum_j2k_bandwidth_changed ()
1833 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1836 void allow_any_dcp_frame_rate_changed ()
1838 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1841 void only_servers_encode_changed ()
1843 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1846 void dcp_metadata_filename_format_changed ()
1848 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1851 void dcp_asset_filename_format_changed ()
1853 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1859 if (_log_general->GetValue ()) {
1860 types |= LogEntry::TYPE_GENERAL;
1862 if (_log_warning->GetValue ()) {
1863 types |= LogEntry::TYPE_WARNING;
1865 if (_log_error->GetValue ()) {
1866 types |= LogEntry::TYPE_ERROR;
1868 if (_log_timing->GetValue ()) {
1869 types |= LogEntry::TYPE_TIMING;
1871 if (_log_debug_decode->GetValue ()) {
1872 types |= LogEntry::TYPE_DEBUG_DECODE;
1874 if (_log_debug_encode->GetValue ()) {
1875 types |= LogEntry::TYPE_DEBUG_ENCODE;
1877 if (_log_debug_email->GetValue ()) {
1878 types |= LogEntry::TYPE_DEBUG_EMAIL;
1880 Config::instance()->set_log_types (types);
1883 #ifdef DCPOMATIC_WINDOWS
1884 void win32_console_changed ()
1886 Config::instance()->set_win32_console (_win32_console->GetValue ());
1890 wxSpinCtrl* _maximum_j2k_bandwidth;
1891 wxCheckBox* _allow_any_dcp_frame_rate;
1892 wxCheckBox* _only_servers_encode;
1893 NameFormatEditor* _dcp_metadata_filename_format;
1894 NameFormatEditor* _dcp_asset_filename_format;
1895 wxCheckBox* _log_general;
1896 wxCheckBox* _log_warning;
1897 wxCheckBox* _log_error;
1898 wxCheckBox* _log_timing;
1899 wxCheckBox* _log_debug_decode;
1900 wxCheckBox* _log_debug_encode;
1901 wxCheckBox* _log_debug_email;
1902 #ifdef DCPOMATIC_WINDOWS
1903 wxCheckBox* _win32_console;
1907 wxPreferencesEditor*
1908 create_config_dialog ()
1910 wxPreferencesEditor* e = new wxPreferencesEditor ();
1912 #ifdef DCPOMATIC_OSX
1913 /* Width that we force some of the config panels to be on OSX so that
1914 the containing window doesn't shrink too much when we select those panels.
1915 This is obviously an unpleasant hack.
1917 wxSize ps = wxSize (520, -1);
1918 int const border = 16;
1920 wxSize ps = wxSize (-1, -1);
1921 int const border = 8;
1924 e->AddPage (new GeneralPage (ps, border));
1925 e->AddPage (new DefaultsPage (ps, border));
1926 e->AddPage (new EncodingServersPage (ps, border));
1927 e->AddPage (new KeysPage (ps, border));
1928 e->AddPage (new TMSPage (ps, border));
1929 e->AddPage (new KDMEmailPage (ps, border));
1930 e->AddPage (new CoverSheetPage (ps, border));
1931 e->AddPage (new AdvancedPage (ps, border));