2 Copyright (C) 2012-2016 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 "lib/config.h"
37 #include "lib/ratio.h"
38 #include "lib/filter.h"
39 #include "lib/dcp_content_type.h"
42 #include "lib/cross.h"
43 #include "lib/exceptions.h"
44 #include <dcp/locale_convert.h>
45 #include <dcp/exceptions.h>
46 #include <dcp/certificate_chain.h>
47 #include <wx/stdpaths.h>
48 #include <wx/preferences.h>
49 #include <wx/spinctrl.h>
50 #include <wx/filepicker.h>
52 #include <boost/filesystem.hpp>
53 #include <boost/foreach.hpp>
64 using boost::shared_ptr;
65 using boost::function;
66 using boost::optional;
67 using dcp::locale_convert;
72 Page (wxSize panel_size, int border)
75 , _panel_size (panel_size)
76 , _window_exists (false)
78 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
84 wxWindow* create_window (wxWindow* parent)
86 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
87 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
91 _window_exists = true;
94 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
103 virtual void config_changed () = 0;
104 virtual void setup () = 0;
106 void config_changed_wrapper ()
108 if (_window_exists) {
113 void window_destroyed ()
115 _window_exists = false;
119 boost::signals2::scoped_connection _config_connection;
123 class StockPage : public wxStockPreferencesPage, public Page
126 StockPage (Kind kind, wxSize panel_size, int border)
127 : wxStockPreferencesPage (kind)
128 , Page (panel_size, border)
131 wxWindow* CreateWindow (wxWindow* parent)
133 return create_window (parent);
137 class StandardPage : public wxPreferencesPage, public Page
140 StandardPage (wxSize panel_size, int border)
141 : Page (panel_size, border)
144 wxWindow* CreateWindow (wxWindow* parent)
146 return create_window (parent);
150 class GeneralPage : public StockPage
153 GeneralPage (wxSize panel_size, int border)
154 : StockPage (Kind_General, panel_size, border)
160 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
161 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
164 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
165 table->Add (_set_language, wxGBPosition (r, 0));
166 _language = new wxChoice (_panel, wxID_ANY);
167 vector<pair<string, string> > languages;
168 languages.push_back (make_pair ("Čeština", "cs_CZ"));
169 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
170 languages.push_back (make_pair ("Dansk", "da_DK"));
171 languages.push_back (make_pair ("Deutsch", "de_DE"));
172 languages.push_back (make_pair ("English", "en_GB"));
173 languages.push_back (make_pair ("Español", "es_ES"));
174 languages.push_back (make_pair ("Français", "fr_FR"));
175 languages.push_back (make_pair ("Italiano", "it_IT"));
176 languages.push_back (make_pair ("Nederlands", "nl_NL"));
177 languages.push_back (make_pair ("Русский", "ru_RU"));
178 languages.push_back (make_pair ("Polski", "pl_PL"));
179 languages.push_back (make_pair ("Português europeu", "pt_PT"));
180 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
181 languages.push_back (make_pair ("Svenska", "sv_SE"));
182 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
183 languages.push_back (make_pair ("українська мова", "uk_UA"));
184 checked_set (_language, languages);
185 table->Add (_language, wxGBPosition (r, 1));
188 wxStaticText* restart = add_label_to_sizer (
189 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
191 wxFont font = restart->GetFont();
192 font.SetStyle (wxFONTSTYLE_ITALIC);
193 font.SetPointSize (font.GetPointSize() - 1);
194 restart->SetFont (font);
197 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
198 _master_encoding_threads = new wxSpinCtrl (_panel);
199 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
202 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
203 _server_encoding_threads = new wxSpinCtrl (_panel);
204 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
207 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
208 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml");
209 table->Add (_cinemas_file, wxGBPosition (r, 1));
212 _preview_sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound in the preview via"));
213 table->Add (_preview_sound, wxGBPosition (r, 0));
214 _preview_sound_output = new wxChoice (_panel, wxID_ANY);
215 table->Add (_preview_sound_output, wxGBPosition (r, 1));
218 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
219 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
220 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
224 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
225 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
228 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
229 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
232 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
233 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
236 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
237 bottom_table->AddGrowableCol (1, 1);
239 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
240 _issuer = new wxTextCtrl (_panel, wxID_ANY);
241 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
243 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
244 _creator = new wxTextCtrl (_panel, wxID_ANY);
245 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
247 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
250 RtAudio audio (DCPOMATIC_RTAUDIO_API);
251 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
252 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
253 if (dev.probed && dev.outputChannels > 0) {
254 _preview_sound_output->Append (std_to_wx (dev.name));
258 _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
259 _language->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::language_changed, this));
260 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&GeneralPage::cinemas_file_changed, this));
261 _preview_sound->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::preview_sound_changed, this));
262 _preview_sound_output->Bind (wxEVT_CHOICE, boost::bind (&GeneralPage::preview_sound_output_changed, this));
264 _master_encoding_threads->SetRange (1, 128);
265 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::master_encoding_threads_changed, this));
266 _server_encoding_threads->SetRange (1, 128);
267 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&GeneralPage::server_encoding_threads_changed, this));
269 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
270 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::analyse_ebur128_changed, this));
272 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
273 _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
274 _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
276 _issuer->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::issuer_changed, this));
277 _creator->Bind (wxEVT_TEXT, boost::bind (&GeneralPage::creator_changed, this));
280 void config_changed ()
282 Config* config = Config::instance ();
284 checked_set (_set_language, static_cast<bool>(config->language()));
286 /* Backwards compatibility of config file */
288 map<string, string> compat_map;
289 compat_map["fr"] = "fr_FR";
290 compat_map["it"] = "it_IT";
291 compat_map["es"] = "es_ES";
292 compat_map["sv"] = "sv_SE";
293 compat_map["de"] = "de_DE";
294 compat_map["nl"] = "nl_NL";
295 compat_map["ru"] = "ru_RU";
296 compat_map["pl"] = "pl_PL";
297 compat_map["da"] = "da_DK";
298 compat_map["pt"] = "pt_PT";
299 compat_map["sk"] = "sk_SK";
300 compat_map["cs"] = "cs_CZ";
301 compat_map["uk"] = "uk_UA";
303 string lang = config->language().get_value_or ("en_GB");
304 if (compat_map.find (lang) != compat_map.end ()) {
305 lang = compat_map[lang];
308 checked_set (_language, lang);
310 checked_set (_master_encoding_threads, config->master_encoding_threads ());
311 checked_set (_server_encoding_threads, config->server_encoding_threads ());
312 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
313 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
315 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
316 checked_set (_check_for_updates, config->check_for_updates ());
317 checked_set (_check_for_test_updates, config->check_for_test_updates ());
318 checked_set (_issuer, config->dcp_issuer ());
319 checked_set (_creator, config->dcp_creator ());
320 checked_set (_cinemas_file, config->cinemas_file());
321 checked_set (_preview_sound, config->preview_sound());
323 optional<string> const current_so = get_preview_sound_output ();
324 string configured_so;
326 if (config->preview_sound_output()) {
327 configured_so = config->preview_sound_output().get();
329 /* No configured output means we should use the default */
330 RtAudio audio (DCPOMATIC_RTAUDIO_API);
331 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
334 if (!current_so || *current_so != configured_so) {
335 /* Update _preview_sound_output with the configured value */
337 while (i < _preview_sound_output->GetCount()) {
338 if (_preview_sound_output->GetString(i) == std_to_wx(configured_so)) {
339 _preview_sound_output->SetSelection (i);
346 setup_sensitivity ();
349 /** @return Currently-selected preview sound output in the dialogue */
350 optional<string> get_preview_sound_output ()
352 int const sel = _preview_sound_output->GetSelection ();
353 if (sel == wxNOT_FOUND) {
354 return optional<string> ();
357 return wx_to_std (_preview_sound_output->GetString (sel));
360 void setup_sensitivity ()
362 _language->Enable (_set_language->GetValue ());
363 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
364 _preview_sound_output->Enable (_preview_sound->GetValue ());
367 void set_language_changed ()
369 setup_sensitivity ();
370 if (_set_language->GetValue ()) {
373 Config::instance()->unset_language ();
377 void language_changed ()
379 int const sel = _language->GetSelection ();
381 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
383 Config::instance()->unset_language ();
387 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
388 void analyse_ebur128_changed ()
390 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
394 void automatic_audio_analysis_changed ()
396 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
399 void check_for_updates_changed ()
401 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
404 void check_for_test_updates_changed ()
406 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
409 void master_encoding_threads_changed ()
411 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
414 void server_encoding_threads_changed ()
416 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
419 void issuer_changed ()
421 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
424 void creator_changed ()
426 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
429 void cinemas_file_changed ()
431 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
434 void preview_sound_changed ()
436 Config::instance()->set_preview_sound (_preview_sound->GetValue ());
439 void preview_sound_output_changed ()
441 RtAudio audio (DCPOMATIC_RTAUDIO_API);
442 optional<string> const so = get_preview_sound_output();
443 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
444 Config::instance()->unset_preview_sound_output ();
446 Config::instance()->set_preview_sound_output (*so);
450 wxCheckBox* _set_language;
452 wxSpinCtrl* _master_encoding_threads;
453 wxSpinCtrl* _server_encoding_threads;
454 FilePickerCtrl* _cinemas_file;
455 wxCheckBox* _preview_sound;
456 wxChoice* _preview_sound_output;
457 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
458 wxCheckBox* _analyse_ebur128;
460 wxCheckBox* _automatic_audio_analysis;
461 wxCheckBox* _check_for_updates;
462 wxCheckBox* _check_for_test_updates;
464 wxTextCtrl* _creator;
467 class DefaultsPage : public StandardPage
470 DefaultsPage (wxSize panel_size, int border)
471 : StandardPage (panel_size, border)
474 wxString GetName () const
476 return _("Defaults");
480 wxBitmap GetLargeIcon () const
482 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
489 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
490 table->AddGrowableCol (1, 1);
491 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
494 add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
495 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
496 _still_length = new wxSpinCtrl (_panel);
497 s->Add (_still_length);
498 add_label_to_sizer (s, _panel, _("s"), false);
502 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
503 #ifdef DCPOMATIC_USE_OWN_PICKER
504 _directory = new DirPickerCtrl (_panel);
506 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
508 table->Add (_directory, 1, wxEXPAND);
510 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
511 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
512 table->Add (_isdcf_metadata_button);
514 add_label_to_sizer (table, _panel, _("Default container"), true);
515 _container = new wxChoice (_panel, wxID_ANY);
516 table->Add (_container);
518 add_label_to_sizer (table, _panel, _("Default content type"), true);
519 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
520 table->Add (_dcp_content_type);
522 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
523 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
524 table->Add (_dcp_audio_channels);
527 add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
528 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
529 _j2k_bandwidth = new wxSpinCtrl (_panel);
530 s->Add (_j2k_bandwidth);
531 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
536 add_label_to_sizer (table, _panel, _("Default audio delay"), true);
537 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
538 _audio_delay = new wxSpinCtrl (_panel);
539 s->Add (_audio_delay);
540 add_label_to_sizer (s, _panel, _("ms"), false);
544 add_label_to_sizer (table, _panel, _("Default standard"), true);
545 _standard = new wxChoice (_panel, wxID_ANY);
546 table->Add (_standard);
548 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
549 #ifdef DCPOMATIC_USE_OWN_PICKER
550 _kdm_directory = new DirPickerCtrl (_panel);
552 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
554 table->Add (_kdm_directory, 1, wxEXPAND);
556 _still_length->SetRange (1, 3600);
557 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
559 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
560 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
562 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
564 vector<Ratio const *> ratios = Ratio::all ();
565 for (size_t i = 0; i < ratios.size(); ++i) {
566 _container->Append (std_to_wx (ratios[i]->nickname ()));
569 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
571 vector<DCPContentType const *> const ct = DCPContentType::all ();
572 for (size_t i = 0; i < ct.size(); ++i) {
573 _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
576 setup_audio_channels_choice (_dcp_audio_channels, 2);
578 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
579 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
581 _j2k_bandwidth->SetRange (50, 250);
582 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
584 _audio_delay->SetRange (-1000, 1000);
585 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
587 _standard->Append (_("SMPTE"));
588 _standard->Append (_("Interop"));
589 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
592 void config_changed ()
594 Config* config = Config::instance ();
596 vector<Ratio const *> ratios = Ratio::all ();
597 for (size_t i = 0; i < ratios.size(); ++i) {
598 if (ratios[i] == config->default_container ()) {
599 _container->SetSelection (i);
603 vector<DCPContentType const *> const ct = DCPContentType::all ();
604 for (size_t i = 0; i < ct.size(); ++i) {
605 if (ct[i] == config->default_dcp_content_type ()) {
606 _dcp_content_type->SetSelection (i);
610 checked_set (_still_length, config->default_still_length ());
611 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
612 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
613 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
614 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
615 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
616 checked_set (_audio_delay, config->default_audio_delay ());
617 checked_set (_standard, config->default_interop() ? 1 : 0);
620 void j2k_bandwidth_changed ()
622 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
625 void audio_delay_changed ()
627 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
630 void dcp_audio_channels_changed ()
632 int const s = _dcp_audio_channels->GetSelection ();
633 if (s != wxNOT_FOUND) {
634 Config::instance()->set_default_dcp_audio_channels (
635 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
640 void directory_changed ()
642 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
645 void kdm_directory_changed ()
647 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
650 void edit_isdcf_metadata_clicked ()
652 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
654 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
658 void still_length_changed ()
660 Config::instance()->set_default_still_length (_still_length->GetValue ());
663 void container_changed ()
665 vector<Ratio const *> ratio = Ratio::all ();
666 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
669 void dcp_content_type_changed ()
671 vector<DCPContentType const *> ct = DCPContentType::all ();
672 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
675 void standard_changed ()
677 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
680 wxSpinCtrl* _j2k_bandwidth;
681 wxSpinCtrl* _audio_delay;
682 wxButton* _isdcf_metadata_button;
683 wxSpinCtrl* _still_length;
684 #ifdef DCPOMATIC_USE_OWN_PICKER
685 DirPickerCtrl* _directory;
686 DirPickerCtrl* _kdm_directory;
688 wxDirPickerCtrl* _directory;
689 wxDirPickerCtrl* _kdm_directory;
691 wxChoice* _container;
692 wxChoice* _dcp_content_type;
693 wxChoice* _dcp_audio_channels;
697 class EncodingServersPage : public StandardPage
700 EncodingServersPage (wxSize panel_size, int border)
701 : StandardPage (panel_size, border)
704 wxString GetName () const
710 wxBitmap GetLargeIcon () const
712 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
719 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
720 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
722 vector<string> columns;
723 columns.push_back (wx_to_std (_("IP address / host name")));
724 _servers_list = new EditableList<string, ServerDialog> (
727 boost::bind (&Config::servers, Config::instance()),
728 boost::bind (&Config::set_servers, Config::instance(), _1),
729 boost::bind (&EncodingServersPage::server_column, this, _1)
732 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
734 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
737 void config_changed ()
739 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
740 _servers_list->refresh ();
743 void use_any_servers_changed ()
745 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
748 string server_column (string s)
753 wxCheckBox* _use_any_servers;
754 EditableList<string, ServerDialog>* _servers_list;
757 class CertificateChainEditor : public wxPanel
760 CertificateChainEditor (
764 function<void (shared_ptr<dcp::CertificateChain>)> set,
765 function<shared_ptr<const dcp::CertificateChain> (void)> get
771 wxFont subheading_font (*wxNORMAL_FONT);
772 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
774 _sizer = new wxBoxSizer (wxVERTICAL);
777 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
778 m->SetFont (subheading_font);
779 _sizer->Add (m, 0, wxALL, border);
782 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
783 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
785 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
790 ip.SetText (_("Type"));
792 _certificates->InsertColumn (0, ip);
798 ip.SetText (_("Thumbprint"));
801 wxFont font = ip.GetFont ();
802 font.SetFamily (wxFONTFAMILY_TELETYPE);
805 _certificates->InsertColumn (1, ip);
808 certificates_sizer->Add (_certificates, 1, wxEXPAND);
811 wxSizer* s = new wxBoxSizer (wxVERTICAL);
812 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
813 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
814 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
815 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
816 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
817 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
818 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
821 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
822 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
825 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
826 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
827 wxFont font = _private_key->GetFont ();
828 font.SetFamily (wxFONTFAMILY_TELETYPE);
829 _private_key->SetFont (font);
830 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
831 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
832 table->Add (_load_private_key, wxGBPosition (r, 2));
833 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
834 table->Add (_export_private_key, wxGBPosition (r, 3));
837 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
838 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
839 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
840 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
843 _add_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::add_certificate, this));
844 _remove_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remove_certificate, this));
845 _export_certificate->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_certificate, this));
846 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
847 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
848 _remake_certificates->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::remake_certificates, this));
849 _load_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::load_private_key, this));
850 _export_private_key->Bind (wxEVT_BUTTON, boost::bind (&CertificateChainEditor::export_private_key, this));
852 SetSizerAndFit (_sizer);
855 void config_changed ()
857 _chain.reset (new dcp::CertificateChain (*_get().get ()));
859 update_certificate_list ();
860 update_private_key ();
861 update_sensitivity ();
864 void add_button (wxWindow* button)
866 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
871 void add_certificate ()
873 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
875 if (d->ShowModal() == wxID_OK) {
878 string const extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
879 if (!extra.empty ()) {
882 _("This file contains other certificates (or other data) after its first certificate. "
883 "Only the first certificate will be used.")
888 update_certificate_list ();
889 } catch (dcp::MiscError& e) {
890 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
896 update_sensitivity ();
899 void remove_certificate ()
901 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
906 _certificates->DeleteItem (i);
910 update_sensitivity ();
913 void export_certificate ()
915 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
920 wxFileDialog* d = new wxFileDialog (
921 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
922 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
925 dcp::CertificateChain::List all = _chain->root_to_leaf ();
926 dcp::CertificateChain::List::iterator j = all.begin ();
927 for (int k = 0; k < i; ++k) {
931 if (d->ShowModal () == wxID_OK) {
932 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
934 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
937 string const s = j->certificate (true);
938 fwrite (s.c_str(), 1, s.length(), f);
944 void update_certificate_list ()
946 _certificates->DeleteAllItems ();
948 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
949 BOOST_FOREACH (dcp::Certificate const & i, certs) {
952 _certificates->InsertItem (item);
953 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
956 _certificates->SetItem (n, 0, _("Root"));
957 } else if (n == (certs.size() - 1)) {
958 _certificates->SetItem (n, 0, _("Leaf"));
960 _certificates->SetItem (n, 0, _("Intermediate"));
967 void remake_certificates ()
969 shared_ptr<const dcp::CertificateChain> chain = _get ();
971 string subject_organization_name;
972 string subject_organizational_unit_name;
973 string root_common_name;
974 string intermediate_common_name;
975 string leaf_common_name;
977 dcp::CertificateChain::List all = chain->root_to_leaf ();
979 if (all.size() >= 1) {
981 subject_organization_name = chain->root().subject_organization_name ();
982 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
983 root_common_name = chain->root().subject_common_name ();
986 if (all.size() >= 2) {
988 leaf_common_name = chain->leaf().subject_common_name ();
991 if (all.size() >= 3) {
992 /* Have an intermediate */
993 dcp::CertificateChain::List::iterator i = all.begin ();
995 intermediate_common_name = i->subject_common_name ();
998 MakeChainDialog* d = new MakeChainDialog (
1000 subject_organization_name,
1001 subject_organizational_unit_name,
1003 intermediate_common_name,
1007 if (d->ShowModal () == wxID_OK) {
1009 new dcp::CertificateChain (
1012 d->organisational_unit (),
1013 d->root_common_name (),
1014 d->intermediate_common_name (),
1015 d->leaf_common_name ()
1020 update_certificate_list ();
1021 update_private_key ();
1027 void update_sensitivity ()
1029 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1030 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
1033 void update_private_key ()
1035 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
1039 void load_private_key ()
1041 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
1043 if (d->ShowModal() == wxID_OK) {
1045 boost::filesystem::path p (wx_to_std (d->GetPath ()));
1046 if (boost::filesystem::file_size (p) > 8192) {
1049 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
1054 _chain->set_key (dcp::file_to_string (p));
1056 update_private_key ();
1057 } catch (dcp::MiscError& e) {
1058 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
1064 update_sensitivity ();
1067 void export_private_key ()
1069 optional<string> key = _chain->key ();
1074 wxFileDialog* d = new wxFileDialog (
1075 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1076 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1079 if (d->ShowModal () == wxID_OK) {
1080 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1082 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1085 string const s = _chain->key().get ();
1086 fwrite (s.c_str(), 1, s.length(), f);
1092 wxListCtrl* _certificates;
1093 wxButton* _add_certificate;
1094 wxButton* _export_certificate;
1095 wxButton* _remove_certificate;
1096 wxButton* _remake_certificates;
1097 wxStaticText* _private_key;
1098 wxButton* _load_private_key;
1099 wxButton* _export_private_key;
1101 wxBoxSizer* _button_sizer;
1102 shared_ptr<dcp::CertificateChain> _chain;
1103 boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
1104 boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
1107 class KeysPage : public StandardPage
1110 KeysPage (wxSize panel_size, int border)
1111 : StandardPage (panel_size, border)
1114 wxString GetName () const
1119 #ifdef DCPOMATIC_OSX
1120 wxBitmap GetLargeIcon () const
1122 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
1130 _signer = new CertificateChainEditor (
1131 _panel, _("Signing DCPs and KDMs"), _border,
1132 boost::bind (&Config::set_signer_chain, Config::instance (), _1),
1133 boost::bind (&Config::signer_chain, Config::instance ())
1136 _panel->GetSizer()->Add (_signer);
1138 _decryption = new CertificateChainEditor (
1139 _panel, _("Decrypting DCPs"), _border,
1140 boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
1141 boost::bind (&Config::decryption_chain, Config::instance ())
1144 _panel->GetSizer()->Add (_decryption);
1146 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\ncertificate..."));
1147 _decryption->add_button (_export_decryption_certificate);
1148 _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption\nchain..."));
1149 _decryption->add_button (_export_decryption_chain);
1151 _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
1152 _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
1155 void export_decryption_certificate ()
1157 wxFileDialog* d = new wxFileDialog (
1158 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1159 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1162 if (d->ShowModal () == wxID_OK) {
1163 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1165 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1168 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
1169 fwrite (s.c_str(), 1, s.length(), f);
1175 void export_decryption_chain ()
1177 wxFileDialog* d = new wxFileDialog (
1178 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
1179 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
1182 if (d->ShowModal () == wxID_OK) {
1183 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
1185 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
1188 string const s = Config::instance()->decryption_chain()->chain();
1189 fwrite (s.c_str(), 1, s.length(), f);
1195 void config_changed ()
1197 _signer->config_changed ();
1198 _decryption->config_changed ();
1201 CertificateChainEditor* _signer;
1202 CertificateChainEditor* _decryption;
1203 wxButton* _export_decryption_certificate;
1204 wxButton* _export_decryption_chain;
1207 class TMSPage : public StandardPage
1210 TMSPage (wxSize panel_size, int border)
1211 : StandardPage (panel_size, border)
1214 wxString GetName () const
1219 #ifdef DCPOMATIC_OSX
1220 wxBitmap GetLargeIcon () const
1222 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1229 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1230 table->AddGrowableCol (1, 1);
1231 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1233 add_label_to_sizer (table, _panel, _("Protocol"), true);
1234 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1235 table->Add (_tms_protocol, 1, wxEXPAND);
1237 add_label_to_sizer (table, _panel, _("IP address"), true);
1238 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1239 table->Add (_tms_ip, 1, wxEXPAND);
1241 add_label_to_sizer (table, _panel, _("Target path"), true);
1242 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1243 table->Add (_tms_path, 1, wxEXPAND);
1245 add_label_to_sizer (table, _panel, _("User name"), true);
1246 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1247 table->Add (_tms_user, 1, wxEXPAND);
1249 add_label_to_sizer (table, _panel, _("Password"), true);
1250 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1251 table->Add (_tms_password, 1, wxEXPAND);
1253 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1254 _tms_protocol->Append (_("FTP (for Dolby)"));
1256 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
1257 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
1258 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
1259 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
1260 _tms_password->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_password_changed, this));
1263 void config_changed ()
1265 Config* config = Config::instance ();
1267 checked_set (_tms_protocol, config->tms_protocol ());
1268 checked_set (_tms_ip, config->tms_ip ());
1269 checked_set (_tms_path, config->tms_path ());
1270 checked_set (_tms_user, config->tms_user ());
1271 checked_set (_tms_password, config->tms_password ());
1274 void tms_protocol_changed ()
1276 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1279 void tms_ip_changed ()
1281 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1284 void tms_path_changed ()
1286 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1289 void tms_user_changed ()
1291 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1294 void tms_password_changed ()
1296 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1299 wxChoice* _tms_protocol;
1300 wxTextCtrl* _tms_ip;
1301 wxTextCtrl* _tms_path;
1302 wxTextCtrl* _tms_user;
1303 wxTextCtrl* _tms_password;
1312 class KDMEmailPage : public StandardPage
1316 KDMEmailPage (wxSize panel_size, int border)
1317 #ifdef DCPOMATIC_OSX
1318 /* We have to force both width and height of this one */
1319 : StandardPage (wxSize (480, 128), border)
1321 : StandardPage (panel_size, border)
1325 wxString GetName () const
1327 return _("KDM Email");
1330 #ifdef DCPOMATIC_OSX
1331 wxBitmap GetLargeIcon () const
1333 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1340 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1341 table->AddGrowableCol (1, 1);
1342 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1344 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1346 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1347 _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1348 s->Add (_mail_server, 1, wxEXPAND | wxALL);
1349 add_label_to_sizer (s, _panel, _("port"), false);
1350 _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1351 _mail_port->SetRange (0, 65535);
1352 s->Add (_mail_port);
1353 table->Add (s, 1, wxEXPAND | wxALL);
1356 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1357 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1358 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1360 add_label_to_sizer (table, _panel, _("Mail password"), true);
1361 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1362 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1364 add_label_to_sizer (table, _panel, _("Subject"), true);
1365 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1366 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1368 add_label_to_sizer (table, _panel, _("From address"), true);
1369 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1370 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1372 vector<string> columns;
1373 columns.push_back (wx_to_std (_("Address")));
1374 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1375 _kdm_cc = new EditableList<string, EmailDialog> (
1378 bind (&Config::kdm_cc, Config::instance()),
1379 bind (&Config::set_kdm_cc, Config::instance(), _1),
1382 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1384 add_label_to_sizer (table, _panel, _("BCC address"), true);
1385 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1386 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1388 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1389 _panel->GetSizer()->Add (_kdm_email, 0, wxEXPAND | wxALL, _border);
1391 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1392 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1396 _mail_server->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_server_changed, this));
1397 _mail_port->Bind (wxEVT_SPINCTRL, boost::bind (&KDMEmailPage::mail_port_changed, this));
1398 _mail_user->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_user_changed, this));
1399 _mail_password->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::mail_password_changed, this));
1400 _kdm_subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1401 _kdm_from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1402 _kdm_bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1403 _kdm_email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1404 _reset_kdm_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1407 void config_changed ()
1409 Config* config = Config::instance ();
1411 checked_set (_mail_server, config->mail_server ());
1412 checked_set (_mail_port, config->mail_port ());
1413 checked_set (_mail_user, config->mail_user ());
1414 checked_set (_mail_password, config->mail_password ());
1415 checked_set (_kdm_subject, config->kdm_subject ());
1416 checked_set (_kdm_from, config->kdm_from ());
1417 checked_set (_kdm_bcc, config->kdm_bcc ());
1418 checked_set (_kdm_email, Config::instance()->kdm_email ());
1421 void mail_server_changed ()
1423 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1426 void mail_port_changed ()
1428 Config::instance()->set_mail_port (_mail_port->GetValue ());
1431 void mail_user_changed ()
1433 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1436 void mail_password_changed ()
1438 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1441 void kdm_subject_changed ()
1443 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1446 void kdm_from_changed ()
1448 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1451 void kdm_bcc_changed ()
1453 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1456 void kdm_email_changed ()
1458 if (_kdm_email->GetValue().IsEmpty ()) {
1459 /* Sometimes we get sent an erroneous notification that the email
1460 is empty; I don't know why.
1464 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1467 void reset_kdm_email ()
1469 Config::instance()->reset_kdm_email ();
1470 checked_set (_kdm_email, Config::instance()->kdm_email ());
1473 wxTextCtrl* _mail_server;
1474 wxSpinCtrl* _mail_port;
1475 wxTextCtrl* _mail_user;
1476 wxTextCtrl* _mail_password;
1477 wxTextCtrl* _kdm_subject;
1478 wxTextCtrl* _kdm_from;
1479 EditableList<string, EmailDialog>* _kdm_cc;
1480 wxTextCtrl* _kdm_bcc;
1481 wxTextCtrl* _kdm_email;
1482 wxButton* _reset_kdm_email;
1485 class CoverSheetPage : public StandardPage
1489 CoverSheetPage (wxSize panel_size, int border)
1490 #ifdef DCPOMATIC_OSX
1491 /* We have to force both width and height of this one */
1492 : StandardPage (wxSize (480, 128), border)
1494 : StandardPage (panel_size, border)
1498 wxString GetName () const
1500 return _("Cover Sheet");
1503 #ifdef DCPOMATIC_OSX
1504 wxBitmap GetLargeIcon () const
1506 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1513 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1514 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1516 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1517 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1519 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1520 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1523 void config_changed ()
1525 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1528 void cover_sheet_changed ()
1530 if (_cover_sheet->GetValue().IsEmpty ()) {
1531 /* Sometimes we get sent an erroneous notification that the cover sheet
1532 is empty; I don't know why.
1536 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1539 void reset_cover_sheet ()
1541 Config::instance()->reset_cover_sheet ();
1542 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1545 wxTextCtrl* _cover_sheet;
1546 wxButton* _reset_cover_sheet;
1550 /** @class AdvancedPage
1551 * @brief Advanced page of the preferences dialog.
1553 class AdvancedPage : public StockPage
1556 AdvancedPage (wxSize panel_size, int border)
1557 : StockPage (Kind_Advanced, panel_size, border)
1558 , _maximum_j2k_bandwidth (0)
1559 , _allow_any_dcp_frame_rate (0)
1560 , _only_servers_encode (0)
1565 , _log_debug_decode (0)
1566 , _log_debug_encode (0)
1567 , _log_debug_email (0)
1571 void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1573 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1575 flags |= wxALIGN_RIGHT;
1578 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1579 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1584 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1585 table->AddGrowableCol (1, 1);
1586 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1589 add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1590 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1591 _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1592 s->Add (_maximum_j2k_bandwidth, 1);
1593 add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1597 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1598 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1599 table->AddSpacer (0);
1601 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1602 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1603 table->AddSpacer (0);
1606 add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1607 dcp::NameFormat::Map titles;
1608 titles['t'] = "type (cpl/pkl)";
1609 dcp::NameFormat::Map examples;
1610 examples['t'] = "cpl";
1611 _dcp_metadata_filename_format = new NameFormatEditor (
1612 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1614 table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1618 add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1619 dcp::NameFormat::Map titles;
1620 titles['t'] = "type (j2c/pcm/sub)";
1621 titles['r'] = "reel number";
1622 titles['n'] = "number of reels";
1623 titles['c'] = "content filename";
1624 dcp::NameFormat::Map examples;
1625 examples['t'] = "j2c";
1626 examples['r'] = "1";
1627 examples['n'] = "4";
1628 examples['c'] = "myfile.mp4";
1629 _dcp_asset_filename_format = new NameFormatEditor (
1630 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1632 table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1636 add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1637 wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1638 _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1639 t->Add (_log_general, 1, wxEXPAND | wxALL);
1640 _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1641 t->Add (_log_warning, 1, wxEXPAND | wxALL);
1642 _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1643 t->Add (_log_error, 1, wxEXPAND | wxALL);
1644 /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1645 _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1646 t->Add (_log_timing, 1, wxEXPAND | wxALL);
1647 _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1648 t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1649 _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1650 t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1651 _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1652 t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1653 table->Add (t, 0, wxALL, 6);
1656 #ifdef DCPOMATIC_WINDOWS
1657 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1658 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1659 table->AddSpacer (0);
1662 _maximum_j2k_bandwidth->SetRange (1, 1000);
1663 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1664 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1665 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1666 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1667 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1668 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1669 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1670 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1671 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1672 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1673 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1674 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1675 #ifdef DCPOMATIC_WINDOWS
1676 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1680 void config_changed ()
1682 Config* config = Config::instance ();
1684 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1685 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1686 checked_set (_only_servers_encode, config->only_servers_encode ());
1687 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1688 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1689 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1690 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1691 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1692 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1693 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1694 #ifdef DCPOMATIC_WINDOWS
1695 checked_set (_win32_console, config->win32_console());
1699 void maximum_j2k_bandwidth_changed ()
1701 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1704 void allow_any_dcp_frame_rate_changed ()
1706 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1709 void only_servers_encode_changed ()
1711 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1714 void dcp_metadata_filename_format_changed ()
1716 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1719 void dcp_asset_filename_format_changed ()
1721 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1727 if (_log_general->GetValue ()) {
1728 types |= LogEntry::TYPE_GENERAL;
1730 if (_log_warning->GetValue ()) {
1731 types |= LogEntry::TYPE_WARNING;
1733 if (_log_error->GetValue ()) {
1734 types |= LogEntry::TYPE_ERROR;
1736 if (_log_timing->GetValue ()) {
1737 types |= LogEntry::TYPE_TIMING;
1739 if (_log_debug_decode->GetValue ()) {
1740 types |= LogEntry::TYPE_DEBUG_DECODE;
1742 if (_log_debug_encode->GetValue ()) {
1743 types |= LogEntry::TYPE_DEBUG_ENCODE;
1745 if (_log_debug_email->GetValue ()) {
1746 types |= LogEntry::TYPE_DEBUG_EMAIL;
1748 Config::instance()->set_log_types (types);
1751 #ifdef DCPOMATIC_WINDOWS
1752 void win32_console_changed ()
1754 Config::instance()->set_win32_console (_win32_console->GetValue ());
1758 wxSpinCtrl* _maximum_j2k_bandwidth;
1759 wxCheckBox* _allow_any_dcp_frame_rate;
1760 wxCheckBox* _only_servers_encode;
1761 NameFormatEditor* _dcp_metadata_filename_format;
1762 NameFormatEditor* _dcp_asset_filename_format;
1763 wxCheckBox* _log_general;
1764 wxCheckBox* _log_warning;
1765 wxCheckBox* _log_error;
1766 wxCheckBox* _log_timing;
1767 wxCheckBox* _log_debug_decode;
1768 wxCheckBox* _log_debug_encode;
1769 wxCheckBox* _log_debug_email;
1770 #ifdef DCPOMATIC_WINDOWS
1771 wxCheckBox* _win32_console;
1775 wxPreferencesEditor*
1776 create_config_dialog ()
1778 wxPreferencesEditor* e = new wxPreferencesEditor ();
1780 #ifdef DCPOMATIC_OSX
1781 /* Width that we force some of the config panels to be on OSX so that
1782 the containing window doesn't shrink too much when we select those panels.
1783 This is obviously an unpleasant hack.
1785 wxSize ps = wxSize (520, -1);
1786 int const border = 16;
1788 wxSize ps = wxSize (-1, -1);
1789 int const border = 8;
1792 e->AddPage (new GeneralPage (ps, border));
1793 e->AddPage (new DefaultsPage (ps, border));
1794 e->AddPage (new EncodingServersPage (ps, border));
1795 e->AddPage (new KeysPage (ps, border));
1796 e->AddPage (new TMSPage (ps, border));
1797 e->AddPage (new KDMEmailPage (ps, border));
1798 e->AddPage (new CoverSheetPage (ps, border));
1799 e->AddPage (new AdvancedPage (ps, border));