Allow more complete control over the libdcp/DCP-o-matic metadata written to various...
[dcpomatic.git] / src / wx / full_config_dialog.cc
1 /*
2     Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 /** @file src/full_config_dialog.cc
22  *  @brief A dialogue to edit all DCP-o-matic configuration.
23  */
24
25 #include "full_config_dialog.h"
26 #include "wx_util.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 "config_dialog.h"
39 #include "static_text.h"
40 #include "check_box.h"
41 #include "dcpomatic_button.h"
42 #include "password_entry.h"
43 #include "lib/config.h"
44 #include "lib/ratio.h"
45 #include "lib/filter.h"
46 #include "lib/dcp_content_type.h"
47 #include "lib/log.h"
48 #include "lib/util.h"
49 #include "lib/cross.h"
50 #include "lib/exceptions.h"
51 #include <dcp/locale_convert.h>
52 #include <dcp/exceptions.h>
53 #include <dcp/certificate_chain.h>
54 #include <wx/stdpaths.h>
55 #include <wx/preferences.h>
56 #include <wx/spinctrl.h>
57 #include <wx/filepicker.h>
58 #include <RtAudio.h>
59 #include <boost/filesystem.hpp>
60 #include <boost/foreach.hpp>
61 #include <iostream>
62
63 using std::vector;
64 using std::string;
65 using std::list;
66 using std::cout;
67 using std::pair;
68 using std::make_pair;
69 using std::map;
70 using boost::bind;
71 using boost::shared_ptr;
72 using boost::function;
73 using boost::optional;
74 using dcp::locale_convert;
75
76 class FullGeneralPage : public GeneralPage
77 {
78 public:
79         FullGeneralPage (wxSize panel_size, int border)
80                 : GeneralPage (panel_size, border)
81         {}
82
83 private:
84         void setup ()
85         {
86                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
87                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
88
89                 int r = 0;
90                 add_language_controls (table, r);
91
92                 add_label_to_sizer (table, _panel, _("Interface complexity"), true, wxGBPosition(r, 0));
93                 _interface_complexity = new wxChoice (_panel, wxID_ANY);
94                 table->Add (_interface_complexity, wxGBPosition (r, 1));
95                 ++r;
96
97                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
98                 _master_encoding_threads = new wxSpinCtrl (_panel);
99                 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
100                 ++r;
101
102                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
103                 _server_encoding_threads = new wxSpinCtrl (_panel);
104                 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
105                 ++r;
106
107                 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
108                 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true, true);
109                 table->Add (_config_file, wxGBPosition (r, 1));
110                 ++r;
111
112                 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
113                 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true, true);
114                 table->Add (_cinemas_file, wxGBPosition (r, 1));
115                 Button* export_cinemas = new Button (_panel, _("Export..."));
116                 table->Add (export_cinemas, wxGBPosition (r, 2));
117                 ++r;
118
119 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
120                 _analyse_ebur128 = new CheckBox (_panel, _("Find integrated loudness, true peak and loudness range when analysing audio"));
121                 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
122                 ++r;
123 #endif
124
125                 _automatic_audio_analysis = new CheckBox (_panel, _("Automatically analyse content audio"));
126                 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
127                 ++r;
128
129                 add_update_controls (table, r);
130
131                 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
132                 bottom_table->AddGrowableCol (1, 1);
133
134                 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
135                 _issuer = new wxTextCtrl (_panel, wxID_ANY);
136                 _issuer->SetToolTip (_("This will be written to the DCP's XML files as the <Issuer>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
137                 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
138
139                 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
140                 _creator = new wxTextCtrl (_panel, wxID_ANY);
141                 _creator->SetToolTip (_("This will be written to the DCP's XML files as the <Creator>.  If it is blank, a default value mentioning DCP-o-matic will be used."));
142                 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
143
144                 add_label_to_sizer (bottom_table, _panel, _("Company name"), true);
145                 _company_name = new wxTextCtrl (_panel, wxID_ANY);
146                 _company_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'company name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
147                 bottom_table->Add (_company_name, 1, wxALL | wxEXPAND);
148
149                 add_label_to_sizer (bottom_table, _panel, _("Product name"), true);
150                 _product_name = new wxTextCtrl (_panel, wxID_ANY);
151                 _product_name->SetToolTip (_("This will be written to the DCP's MXF files as the 'product name'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
152                 bottom_table->Add (_product_name, 1, wxALL | wxEXPAND);
153
154                 add_label_to_sizer (bottom_table, _panel, _("Product version"), true);
155                 _product_version = new wxTextCtrl (_panel, wxID_ANY);
156                 _product_version->SetToolTip (_("This will be written to the DCP's MXF files as the 'product version'.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
157                 bottom_table->Add (_product_version, 1, wxALL | wxEXPAND);
158
159                 add_label_to_sizer (bottom_table, _panel, _("JPEG2000 comment"), true);
160                 _j2k_comment = new wxTextCtrl (_panel, wxID_ANY);
161                 _j2k_comment->SetToolTip (_("This will be written to the DCP's JPEG2000 data as a comment.  If it is blank, a default value mentioning libdcp (an internal DCP-o-matic library) will be used."));
162                 bottom_table->Add (_j2k_comment, 1, wxALL | wxEXPAND);
163
164                 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
165                 ++r;
166
167                 _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::config_file_changed,   this));
168                 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::cinemas_file_changed,  this));
169
170                 _interface_complexity->Append (_("Simple"));
171                 _interface_complexity->Append (_("Full"));
172                 _interface_complexity->Bind (wxEVT_CHOICE, boost::bind(&FullGeneralPage::interface_complexity_changed, this));
173
174                 _master_encoding_threads->SetRange (1, 128);
175                 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
176                 _server_encoding_threads->SetRange (1, 128);
177                 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
178                 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
179
180 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
181                 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
182 #endif
183                 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
184
185                 _issuer->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::issuer_changed, this));
186                 _creator->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::creator_changed, this));
187                 _company_name->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::company_name_changed, this));
188                 _product_name->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::product_name_changed, this));
189                 _product_version->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::product_version_changed, this));
190                 _j2k_comment->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::j2k_comment_changed, this));
191         }
192
193         void config_changed ()
194         {
195                 Config* config = Config::instance ();
196
197                 switch (config->interface_complexity()) {
198                 case Config::INTERFACE_SIMPLE:
199                         checked_set (_interface_complexity, 0);
200                         break;
201                 case Config::INTERFACE_FULL:
202                         checked_set (_interface_complexity, 1);
203                         break;
204                 }
205                 checked_set (_master_encoding_threads, config->master_encoding_threads ());
206                 checked_set (_server_encoding_threads, config->server_encoding_threads ());
207 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
208                 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
209 #endif
210                 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
211                 checked_set (_issuer, config->dcp_issuer ());
212                 checked_set (_creator, config->dcp_creator ());
213                 checked_set (_company_name, config->dcp_company_name ());
214                 checked_set (_product_name, config->dcp_product_name ());
215                 checked_set (_product_version, config->dcp_product_version ());
216                 checked_set (_j2k_comment, config->dcp_j2k_comment ());
217                 checked_set (_config_file, config->config_file());
218                 checked_set (_cinemas_file, config->cinemas_file());
219
220                 GeneralPage::config_changed ();
221         }
222
223         void export_cinemas_file ()
224         {
225                 wxFileDialog* d = new wxFileDialog (
226                         _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT ("XML files (*.xml)|*.xml"),
227                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
228                 );
229
230                 if (d->ShowModal () == wxID_OK) {
231                         boost::filesystem::copy_file (Config::instance()->cinemas_file(), wx_to_std(d->GetPath()));
232                 }
233                 d->Destroy ();
234         }
235
236         void interface_complexity_changed ()
237         {
238                 if (_interface_complexity->GetSelection() == 0) {
239                         Config::instance()->set_interface_complexity (Config::INTERFACE_SIMPLE);
240                 } else {
241                         Config::instance()->set_interface_complexity (Config::INTERFACE_FULL);
242                 }
243         }
244
245
246 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
247         void analyse_ebur128_changed ()
248         {
249                 Config::instance()->set_analyse_ebur128 (_analyse_ebur128->GetValue ());
250         }
251 #endif
252
253         void automatic_audio_analysis_changed ()
254         {
255                 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
256         }
257
258         void master_encoding_threads_changed ()
259         {
260                 Config::instance()->set_master_encoding_threads (_master_encoding_threads->GetValue ());
261         }
262
263         void server_encoding_threads_changed ()
264         {
265                 Config::instance()->set_server_encoding_threads (_server_encoding_threads->GetValue ());
266         }
267
268         void issuer_changed ()
269         {
270                 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
271         }
272
273         void creator_changed ()
274         {
275                 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
276         }
277
278         void company_name_changed ()
279         {
280                 Config::instance()->set_dcp_company_name (wx_to_std(_company_name->GetValue()));
281         }
282
283         void product_name_changed ()
284         {
285                 Config::instance()->set_dcp_product_name (wx_to_std(_product_name->GetValue()));
286         }
287
288         void product_version_changed ()
289         {
290                 Config::instance()->set_dcp_product_version (wx_to_std(_product_version->GetValue()));
291         }
292
293         void j2k_comment_changed ()
294         {
295                 Config::instance()->set_dcp_j2k_comment (wx_to_std(_j2k_comment->GetValue()));
296         }
297
298         void config_file_changed ()
299         {
300                 Config* config = Config::instance();
301                 boost::filesystem::path new_file = wx_to_std(_config_file->GetPath());
302                 if (new_file == config->config_file()) {
303                         return;
304                 }
305                 bool copy_and_link = true;
306                 if (boost::filesystem::exists(new_file)) {
307                         ConfigMoveDialog* d = new ConfigMoveDialog (_panel, new_file);
308                         if (d->ShowModal() == wxID_OK) {
309                                 copy_and_link = false;
310                         }
311                         d->Destroy ();
312                 }
313
314                 if (copy_and_link) {
315                         config->write ();
316                         if (new_file != config->config_file()) {
317                                 config->copy_and_link (new_file);
318                         }
319                 } else {
320                         config->link (new_file);
321                 }
322         }
323
324         void cinemas_file_changed ()
325         {
326                 Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ()));
327         }
328
329         wxChoice* _interface_complexity;
330         wxSpinCtrl* _master_encoding_threads;
331         wxSpinCtrl* _server_encoding_threads;
332         FilePickerCtrl* _config_file;
333         FilePickerCtrl* _cinemas_file;
334 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
335         wxCheckBox* _analyse_ebur128;
336 #endif
337         wxCheckBox* _automatic_audio_analysis;
338         wxTextCtrl* _issuer;
339         wxTextCtrl* _creator;
340         wxTextCtrl* _company_name;
341         wxTextCtrl* _product_name;
342         wxTextCtrl* _product_version;
343         wxTextCtrl* _j2k_comment;
344 };
345
346 class DefaultsPage : public StandardPage
347 {
348 public:
349         DefaultsPage (wxSize panel_size, int border)
350                 : StandardPage (panel_size, border)
351         {}
352
353         wxString GetName () const
354         {
355                 return _("Defaults");
356         }
357
358 #ifdef DCPOMATIC_OSX
359         wxBitmap GetLargeIcon () const
360         {
361                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
362         }
363 #endif
364
365 private:
366         void setup ()
367         {
368                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
369                 table->AddGrowableCol (1, 1);
370                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
371
372                 {
373                         add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
374                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
375                         _still_length = new wxSpinCtrl (_panel);
376                         s->Add (_still_length);
377                         add_label_to_sizer (s, _panel, _("s"), false);
378                         table->Add (s, 1);
379                 }
380
381                 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
382 #ifdef DCPOMATIC_USE_OWN_PICKER
383                 _directory = new DirPickerCtrl (_panel);
384 #else
385                 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
386 #endif
387                 table->Add (_directory, 1, wxEXPAND);
388
389                 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
390                 _isdcf_metadata_button = new Button (_panel, _("Edit..."));
391                 table->Add (_isdcf_metadata_button);
392
393                 add_label_to_sizer (table, _panel, _("Default container"), true);
394                 _container = new wxChoice (_panel, wxID_ANY);
395                 table->Add (_container);
396
397                 add_label_to_sizer (table, _panel, _("Default content type"), true);
398                 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
399                 table->Add (_dcp_content_type);
400
401                 add_label_to_sizer (table, _panel, _("Default DCP audio channels"), true);
402                 _dcp_audio_channels = new wxChoice (_panel, wxID_ANY);
403                 table->Add (_dcp_audio_channels);
404
405                 {
406                         add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
407                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
408                         _j2k_bandwidth = new wxSpinCtrl (_panel);
409                         s->Add (_j2k_bandwidth);
410                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
411                         table->Add (s, 1);
412                 }
413
414                 {
415                         add_label_to_sizer (table, _panel, _("Default audio delay"), true);
416                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
417                         _audio_delay = new wxSpinCtrl (_panel);
418                         s->Add (_audio_delay);
419                         add_label_to_sizer (s, _panel, _("ms"), false);
420                         table->Add (s, 1);
421                 }
422
423                 add_label_to_sizer (table, _panel, _("Default standard"), true);
424                 _standard = new wxChoice (_panel, wxID_ANY);
425                 table->Add (_standard);
426
427                 add_label_to_sizer (table, _panel, _("Default KDM directory"), true);
428 #ifdef DCPOMATIC_USE_OWN_PICKER
429                 _kdm_directory = new DirPickerCtrl (_panel);
430 #else
431                 _kdm_directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
432 #endif
433
434                 table->Add (_kdm_directory, 1, wxEXPAND);
435
436                 _still_length->SetRange (1, 3600);
437                 _still_length->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::still_length_changed, this));
438
439                 _directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
440                 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::kdm_directory_changed, this));
441
442                 _isdcf_metadata_button->Bind (wxEVT_BUTTON, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
443
444                 BOOST_FOREACH (Ratio const * i, Ratio::containers()) {
445                         _container->Append (std_to_wx(i->container_nickname()));
446                 }
447
448                 _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
449
450                 BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
451                         _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
452                 }
453
454                 setup_audio_channels_choice (_dcp_audio_channels, 2);
455
456                 _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
457                 _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this));
458
459                 _j2k_bandwidth->SetRange (50, 250);
460                 _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
461
462                 _audio_delay->SetRange (-1000, 1000);
463                 _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this));
464
465                 _standard->Append (_("SMPTE"));
466                 _standard->Append (_("Interop"));
467                 _standard->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::standard_changed, this));
468         }
469
470         void config_changed ()
471         {
472                 Config* config = Config::instance ();
473
474                 vector<Ratio const *> containers = Ratio::containers ();
475                 for (size_t i = 0; i < containers.size(); ++i) {
476                         if (containers[i] == config->default_container ()) {
477                                 _container->SetSelection (i);
478                         }
479                 }
480
481                 vector<DCPContentType const *> const ct = DCPContentType::all ();
482                 for (size_t i = 0; i < ct.size(); ++i) {
483                         if (ct[i] == config->default_dcp_content_type ()) {
484                                 _dcp_content_type->SetSelection (i);
485                         }
486                 }
487
488                 checked_set (_still_length, config->default_still_length ());
489                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
490                 _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
491                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
492                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
493                 checked_set (_dcp_audio_channels, locale_convert<string> (config->default_dcp_audio_channels()));
494                 checked_set (_audio_delay, config->default_audio_delay ());
495                 checked_set (_standard, config->default_interop() ? 1 : 0);
496         }
497
498         void j2k_bandwidth_changed ()
499         {
500                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
501         }
502
503         void audio_delay_changed ()
504         {
505                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
506         }
507
508         void dcp_audio_channels_changed ()
509         {
510                 int const s = _dcp_audio_channels->GetSelection ();
511                 if (s != wxNOT_FOUND) {
512                         Config::instance()->set_default_dcp_audio_channels (
513                                 locale_convert<int> (string_client_data (_dcp_audio_channels->GetClientObject (s)))
514                                 );
515                 }
516         }
517
518         void directory_changed ()
519         {
520                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
521         }
522
523         void kdm_directory_changed ()
524         {
525                 Config::instance()->set_default_kdm_directory (wx_to_std (_kdm_directory->GetPath ()));
526         }
527
528         void edit_isdcf_metadata_clicked ()
529         {
530                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
531                 d->ShowModal ();
532                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
533                 d->Destroy ();
534         }
535
536         void still_length_changed ()
537         {
538                 Config::instance()->set_default_still_length (_still_length->GetValue ());
539         }
540
541         void container_changed ()
542         {
543                 vector<Ratio const *> ratio = Ratio::containers ();
544                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
545         }
546
547         void dcp_content_type_changed ()
548         {
549                 vector<DCPContentType const *> ct = DCPContentType::all ();
550                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
551         }
552
553         void standard_changed ()
554         {
555                 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
556         }
557
558         wxSpinCtrl* _j2k_bandwidth;
559         wxSpinCtrl* _audio_delay;
560         wxButton* _isdcf_metadata_button;
561         wxSpinCtrl* _still_length;
562 #ifdef DCPOMATIC_USE_OWN_PICKER
563         DirPickerCtrl* _directory;
564         DirPickerCtrl* _kdm_directory;
565 #else
566         wxDirPickerCtrl* _directory;
567         wxDirPickerCtrl* _kdm_directory;
568 #endif
569         wxChoice* _container;
570         wxChoice* _dcp_content_type;
571         wxChoice* _dcp_audio_channels;
572         wxChoice* _standard;
573 };
574
575 class EncodingServersPage : public StandardPage
576 {
577 public:
578         EncodingServersPage (wxSize panel_size, int border)
579                 : StandardPage (panel_size, border)
580         {}
581
582         wxString GetName () const
583         {
584                 return _("Servers");
585         }
586
587 #ifdef DCPOMATIC_OSX
588         wxBitmap GetLargeIcon () const
589         {
590                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
591         }
592 #endif
593
594 private:
595         void setup ()
596         {
597                 _use_any_servers = new CheckBox (_panel, _("Search network for servers"));
598                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
599
600                 vector<EditableListColumn> columns;
601                 columns.push_back (EditableListColumn(wx_to_std(_("IP address / host name"))));
602                 _servers_list = new EditableList<string, ServerDialog> (
603                         _panel,
604                         columns,
605                         boost::bind (&Config::servers, Config::instance()),
606                         boost::bind (&Config::set_servers, Config::instance(), _1),
607                         boost::bind (&EncodingServersPage::server_column, this, _1)
608                         );
609
610                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
611
612                 _use_any_servers->Bind (wxEVT_CHECKBOX, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
613         }
614
615         void config_changed ()
616         {
617                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
618                 _servers_list->refresh ();
619         }
620
621         void use_any_servers_changed ()
622         {
623                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
624         }
625
626         string server_column (string s)
627         {
628                 return s;
629         }
630
631         wxCheckBox* _use_any_servers;
632         EditableList<string, ServerDialog>* _servers_list;
633 };
634
635 class TMSPage : public StandardPage
636 {
637 public:
638         TMSPage (wxSize panel_size, int border)
639                 : StandardPage (panel_size, border)
640         {}
641
642         wxString GetName () const
643         {
644                 return _("TMS");
645         }
646
647 #ifdef DCPOMATIC_OSX
648         wxBitmap GetLargeIcon () const
649         {
650                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
651         }
652 #endif
653
654 private:
655         void setup ()
656         {
657                 _upload = new CheckBox (_panel, _("Upload DCP to TMS after creation"));
658                 _panel->GetSizer()->Add (_upload, 0, wxALL | wxEXPAND, _border);
659
660                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
661                 table->AddGrowableCol (1, 1);
662                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
663
664                 add_label_to_sizer (table, _panel, _("Protocol"), true);
665                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
666                 table->Add (_tms_protocol, 1, wxEXPAND);
667
668                 add_label_to_sizer (table, _panel, _("IP address"), true);
669                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
670                 table->Add (_tms_ip, 1, wxEXPAND);
671
672                 add_label_to_sizer (table, _panel, _("Target path"), true);
673                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
674                 table->Add (_tms_path, 1, wxEXPAND);
675
676                 add_label_to_sizer (table, _panel, _("User name"), true);
677                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
678                 table->Add (_tms_user, 1, wxEXPAND);
679
680                 add_label_to_sizer (table, _panel, _("Password"), true);
681                 _tms_password = new PasswordEntry (_panel);
682                 table->Add (_tms_password->get_panel(), 1, wxEXPAND);
683
684                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
685                 _tms_protocol->Append (_("FTP (for Dolby)"));
686
687                 _upload->Bind (wxEVT_CHECKBOX, boost::bind(&TMSPage::upload_changed, this));
688                 _tms_protocol->Bind (wxEVT_CHOICE, boost::bind (&TMSPage::tms_protocol_changed, this));
689                 _tms_ip->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_ip_changed, this));
690                 _tms_path->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_path_changed, this));
691                 _tms_user->Bind (wxEVT_TEXT, boost::bind (&TMSPage::tms_user_changed, this));
692                 _tms_password->Changed.connect (boost::bind (&TMSPage::tms_password_changed, this));
693         }
694
695         void config_changed ()
696         {
697                 Config* config = Config::instance ();
698
699                 checked_set (_upload, config->upload_after_make_dcp());
700                 checked_set (_tms_protocol, config->tms_protocol ());
701                 checked_set (_tms_ip, config->tms_ip ());
702                 checked_set (_tms_path, config->tms_path ());
703                 checked_set (_tms_user, config->tms_user ());
704                 checked_set (_tms_password, config->tms_password ());
705         }
706
707         void upload_changed ()
708         {
709                 Config::instance()->set_upload_after_make_dcp (_upload->GetValue());
710         }
711
712         void tms_protocol_changed ()
713         {
714                 Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
715         }
716
717         void tms_ip_changed ()
718         {
719                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
720         }
721
722         void tms_path_changed ()
723         {
724                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
725         }
726
727         void tms_user_changed ()
728         {
729                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
730         }
731
732         void tms_password_changed ()
733         {
734                 Config::instance()->set_tms_password (_tms_password->get());
735         }
736
737         CheckBox* _upload;
738         wxChoice* _tms_protocol;
739         wxTextCtrl* _tms_ip;
740         wxTextCtrl* _tms_path;
741         wxTextCtrl* _tms_user;
742         PasswordEntry* _tms_password;
743 };
744
745 static string
746 column (string s)
747 {
748         return s;
749 }
750
751 class EmailPage : public StandardPage
752 {
753 public:
754         EmailPage (wxSize panel_size, int border)
755                 : StandardPage (panel_size, border)
756         {}
757
758         wxString GetName () const
759         {
760                 return _("Email");
761         }
762
763 #ifdef DCPOMATIC_OSX
764         wxBitmap GetLargeIcon () const
765         {
766                 return wxBitmap ("email", wxBITMAP_TYPE_PNG_RESOURCE);
767         }
768 #endif
769
770 private:
771         void setup ()
772         {
773                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
774                 table->AddGrowableCol (1, 1);
775                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
776
777                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
778                 {
779                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
780                         _server = new wxTextCtrl (_panel, wxID_ANY);
781                         s->Add (_server, 1, wxEXPAND | wxALL);
782                         add_label_to_sizer (s, _panel, _("port"), false);
783                         _port = new wxSpinCtrl (_panel, wxID_ANY);
784                         _port->SetRange (0, 65535);
785                         s->Add (_port);
786                         add_label_to_sizer (s, _panel, _("protocol"), false);
787                         _protocol = new wxChoice (_panel, wxID_ANY);
788                         /* Make sure this matches the switches in config_changed and port_changed below */
789                         _protocol->Append (_("Auto"));
790                         _protocol->Append (_("Plain"));
791                         _protocol->Append (_("STARTTLS"));
792                         _protocol->Append (_("SSL"));
793                         s->Add (_protocol);
794                         table->Add (s, 1, wxEXPAND | wxALL);
795                 }
796
797                 add_label_to_sizer (table, _panel, _("User name"), true);
798                 _user = new wxTextCtrl (_panel, wxID_ANY);
799                 table->Add (_user, 1, wxEXPAND | wxALL);
800
801                 add_label_to_sizer (table, _panel, _("Password"), true);
802                 _password = new PasswordEntry (_panel);
803                 table->Add (_password->get_panel(), 1, wxEXPAND | wxALL);
804
805                 _server->Bind (wxEVT_TEXT, boost::bind (&EmailPage::server_changed, this));
806                 _port->Bind (wxEVT_SPINCTRL, boost::bind (&EmailPage::port_changed, this));
807                 _protocol->Bind (wxEVT_CHOICE, boost::bind (&EmailPage::protocol_changed, this));
808                 _user->Bind (wxEVT_TEXT, boost::bind (&EmailPage::user_changed, this));
809                 _password->Changed.connect (boost::bind (&EmailPage::password_changed, this));
810         }
811
812         void config_changed ()
813         {
814                 Config* config = Config::instance ();
815
816                 checked_set (_server, config->mail_server ());
817                 checked_set (_port, config->mail_port ());
818                 switch (config->mail_protocol()) {
819                 case EMAIL_PROTOCOL_AUTO:
820                         checked_set (_protocol, 0);
821                         break;
822                 case EMAIL_PROTOCOL_PLAIN:
823                         checked_set (_protocol, 1);
824                         break;
825                 case EMAIL_PROTOCOL_STARTTLS:
826                         checked_set (_protocol, 2);
827                         break;
828                 case EMAIL_PROTOCOL_SSL:
829                         checked_set (_protocol, 3);
830                         break;
831                 }
832                 checked_set (_user, config->mail_user ());
833                 checked_set (_password, config->mail_password());
834         }
835
836         void server_changed ()
837         {
838                 Config::instance()->set_mail_server (wx_to_std (_server->GetValue ()));
839         }
840
841         void port_changed ()
842         {
843                 Config::instance()->set_mail_port (_port->GetValue ());
844         }
845
846         void protocol_changed ()
847         {
848                 switch (_protocol->GetSelection()) {
849                 case 0:
850                         Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_AUTO);
851                         break;
852                 case 1:
853                         Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_PLAIN);
854                         break;
855                 case 2:
856                         Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_STARTTLS);
857                         break;
858                 case 3:
859                         Config::instance()->set_mail_protocol(EMAIL_PROTOCOL_SSL);
860                         break;
861                 }
862         }
863
864         void user_changed ()
865         {
866                 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
867         }
868
869         void password_changed ()
870         {
871                 Config::instance()->set_mail_password(_password->get());
872         }
873
874         wxTextCtrl* _server;
875         wxSpinCtrl* _port;
876         wxChoice* _protocol;
877         wxTextCtrl* _user;
878         PasswordEntry* _password;
879 };
880
881 class KDMEmailPage : public StandardPage
882 {
883 public:
884
885         KDMEmailPage (wxSize panel_size, int border)
886 #ifdef DCPOMATIC_OSX
887                 /* We have to force both width and height of this one */
888                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
889 #else
890                 : StandardPage (panel_size, border)
891 #endif
892         {}
893
894         wxString GetName () const
895         {
896                 return _("KDM Email");
897         }
898
899 #ifdef DCPOMATIC_OSX
900         wxBitmap GetLargeIcon () const
901         {
902                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
903         }
904 #endif
905
906 private:
907         void setup ()
908         {
909                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
910                 table->AddGrowableCol (1, 1);
911                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
912
913                 add_label_to_sizer (table, _panel, _("Subject"), true);
914                 _subject = new wxTextCtrl (_panel, wxID_ANY);
915                 table->Add (_subject, 1, wxEXPAND | wxALL);
916
917                 add_label_to_sizer (table, _panel, _("From address"), true);
918                 _from = new wxTextCtrl (_panel, wxID_ANY);
919                 table->Add (_from, 1, wxEXPAND | wxALL);
920
921                 vector<EditableListColumn> columns;
922                 columns.push_back (EditableListColumn(wx_to_std(_("Address"))));
923                 add_label_to_sizer (table, _panel, _("CC addresses"), true);
924                 _cc = new EditableList<string, EmailDialog> (
925                         _panel,
926                         columns,
927                         bind (&Config::kdm_cc, Config::instance()),
928                         bind (&Config::set_kdm_cc, Config::instance(), _1),
929                         bind (&column, _1)
930                         );
931                 table->Add (_cc, 1, wxEXPAND | wxALL);
932
933                 add_label_to_sizer (table, _panel, _("BCC address"), true);
934                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
935                 table->Add (_bcc, 1, wxEXPAND | wxALL);
936
937                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
938                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
939
940                 _reset_email = new Button (_panel, _("Reset to default subject and text"));
941                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
942
943                 _cc->layout ();
944
945                 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
946                 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
947                 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
948                 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
949                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
950         }
951
952         void config_changed ()
953         {
954                 Config* config = Config::instance ();
955
956                 checked_set (_subject, config->kdm_subject ());
957                 checked_set (_from, config->kdm_from ());
958                 checked_set (_bcc, config->kdm_bcc ());
959                 checked_set (_email, Config::instance()->kdm_email ());
960         }
961
962         void kdm_subject_changed ()
963         {
964                 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
965         }
966
967         void kdm_from_changed ()
968         {
969                 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
970         }
971
972         void kdm_bcc_changed ()
973         {
974                 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
975         }
976
977         void kdm_email_changed ()
978         {
979                 if (_email->GetValue().IsEmpty ()) {
980                         /* Sometimes we get sent an erroneous notification that the email
981                            is empty; I don't know why.
982                         */
983                         return;
984                 }
985                 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
986         }
987
988         void reset_email ()
989         {
990                 Config::instance()->reset_kdm_email ();
991                 checked_set (_email, Config::instance()->kdm_email ());
992         }
993
994         wxTextCtrl* _subject;
995         wxTextCtrl* _from;
996         EditableList<string, EmailDialog>* _cc;
997         wxTextCtrl* _bcc;
998         wxTextCtrl* _email;
999         wxButton* _reset_email;
1000 };
1001
1002 class NotificationsPage : public StandardPage
1003 {
1004 public:
1005         NotificationsPage (wxSize panel_size, int border)
1006 #ifdef DCPOMATIC_OSX
1007                 /* We have to force both width and height of this one */
1008                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
1009 #else
1010                 : StandardPage (panel_size, border)
1011 #endif
1012         {}
1013
1014         wxString GetName () const
1015         {
1016                 return _("Notifications");
1017         }
1018
1019 #ifdef DCPOMATIC_OSX
1020         wxBitmap GetLargeIcon () const
1021         {
1022                 return wxBitmap ("notifications", wxBITMAP_TYPE_PNG_RESOURCE);
1023         }
1024 #endif
1025
1026 private:
1027         void setup ()
1028         {
1029                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1030                 table->AddGrowableCol (1, 1);
1031                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1032
1033                 _enable_message_box = new CheckBox (_panel, _("Message box"));
1034                 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1035                 table->AddSpacer (0);
1036
1037                 _enable_email = new CheckBox (_panel, _("Email"));
1038                 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1039                 table->AddSpacer (0);
1040
1041                 add_label_to_sizer (table, _panel, _("Subject"), true);
1042                 _subject = new wxTextCtrl (_panel, wxID_ANY);
1043                 table->Add (_subject, 1, wxEXPAND | wxALL);
1044
1045                 add_label_to_sizer (table, _panel, _("From address"), true);
1046                 _from = new wxTextCtrl (_panel, wxID_ANY);
1047                 table->Add (_from, 1, wxEXPAND | wxALL);
1048
1049                 add_label_to_sizer (table, _panel, _("To address"), true);
1050                 _to = new wxTextCtrl (_panel, wxID_ANY);
1051                 table->Add (_to, 1, wxEXPAND | wxALL);
1052
1053                 vector<EditableListColumn> columns;
1054                 columns.push_back (EditableListColumn(wx_to_std(_("Address"))));
1055                 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1056                 _cc = new EditableList<string, EmailDialog> (
1057                         _panel,
1058                         columns,
1059                         bind (&Config::notification_cc, Config::instance()),
1060                         bind (&Config::set_notification_cc, Config::instance(), _1),
1061                         bind (&column, _1)
1062                         );
1063                 table->Add (_cc, 1, wxEXPAND | wxALL);
1064
1065                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1066                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1067                 table->Add (_bcc, 1, wxEXPAND | wxALL);
1068
1069                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1070                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1071
1072                 _reset_email = new Button (_panel, _("Reset to default subject and text"));
1073                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1074
1075                 _cc->layout ();
1076
1077                 _enable_message_box->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX));
1078                 _enable_email->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL));
1079
1080                 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1081                 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1082                 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1083                 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1084                 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1085                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1086
1087                 update_sensitivity ();
1088         }
1089
1090         void update_sensitivity ()
1091         {
1092                 bool const s = _enable_email->GetValue();
1093                 _subject->Enable(s);
1094                 _from->Enable(s);
1095                 _to->Enable(s);
1096                 _cc->Enable(s);
1097                 _bcc->Enable(s);
1098                 _email->Enable(s);
1099                 _reset_email->Enable(s);
1100         }
1101
1102         void config_changed ()
1103         {
1104                 Config* config = Config::instance ();
1105
1106                 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1107                 checked_set (_enable_email, config->notification(Config::EMAIL));
1108                 checked_set (_subject, config->notification_subject ());
1109                 checked_set (_from, config->notification_from ());
1110                 checked_set (_to, config->notification_to ());
1111                 checked_set (_bcc, config->notification_bcc ());
1112                 checked_set (_email, Config::instance()->notification_email ());
1113
1114                 update_sensitivity ();
1115         }
1116
1117         void notification_subject_changed ()
1118         {
1119                 Config::instance()->set_notification_subject (wx_to_std (_subject->GetValue ()));
1120         }
1121
1122         void notification_from_changed ()
1123         {
1124                 Config::instance()->set_notification_from (wx_to_std (_from->GetValue ()));
1125         }
1126
1127         void notification_to_changed ()
1128         {
1129                 Config::instance()->set_notification_to (wx_to_std (_to->GetValue ()));
1130         }
1131
1132         void notification_bcc_changed ()
1133         {
1134                 Config::instance()->set_notification_bcc (wx_to_std (_bcc->GetValue ()));
1135         }
1136
1137         void notification_email_changed ()
1138         {
1139                 if (_email->GetValue().IsEmpty ()) {
1140                         /* Sometimes we get sent an erroneous notification that the email
1141                            is empty; I don't know why.
1142                         */
1143                         return;
1144                 }
1145                 Config::instance()->set_notification_email (wx_to_std (_email->GetValue ()));
1146         }
1147
1148         void reset_email ()
1149         {
1150                 Config::instance()->reset_notification_email ();
1151                 checked_set (_email, Config::instance()->notification_email ());
1152         }
1153
1154         void type_changed (wxCheckBox* b, Config::Notification n)
1155         {
1156                 Config::instance()->set_notification(n, b->GetValue());
1157                 update_sensitivity ();
1158         }
1159
1160         wxCheckBox* _enable_message_box;
1161         wxCheckBox* _enable_email;
1162
1163         wxTextCtrl* _subject;
1164         wxTextCtrl* _from;
1165         wxTextCtrl* _to;
1166         EditableList<string, EmailDialog>* _cc;
1167         wxTextCtrl* _bcc;
1168         wxTextCtrl* _email;
1169         wxButton* _reset_email;
1170 };
1171
1172 class CoverSheetPage : public StandardPage
1173 {
1174 public:
1175
1176         CoverSheetPage (wxSize panel_size, int border)
1177 #ifdef DCPOMATIC_OSX
1178                 /* We have to force both width and height of this one */
1179                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
1180 #else
1181                 : StandardPage (panel_size, border)
1182 #endif
1183         {}
1184
1185         wxString GetName () const
1186         {
1187                 return _("Cover Sheet");
1188         }
1189
1190 #ifdef DCPOMATIC_OSX
1191         wxBitmap GetLargeIcon () const
1192         {
1193                 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1194         }
1195 #endif
1196
1197 private:
1198         void setup ()
1199         {
1200                 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1201                 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1202
1203                 _reset_cover_sheet = new Button (_panel, _("Reset to default text"));
1204                 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1205
1206                 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1207                 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1208         }
1209
1210         void config_changed ()
1211         {
1212                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1213         }
1214
1215         void cover_sheet_changed ()
1216         {
1217                 if (_cover_sheet->GetValue().IsEmpty ()) {
1218                         /* Sometimes we get sent an erroneous notification that the cover sheet
1219                            is empty; I don't know why.
1220                         */
1221                         return;
1222                 }
1223                 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1224         }
1225
1226         void reset_cover_sheet ()
1227         {
1228                 Config::instance()->reset_cover_sheet ();
1229                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1230         }
1231
1232         wxTextCtrl* _cover_sheet;
1233         wxButton* _reset_cover_sheet;
1234 };
1235
1236
1237 /** @class AdvancedPage
1238  *  @brief Advanced page of the preferences dialog.
1239  */
1240 class AdvancedPage : public StockPage
1241 {
1242 public:
1243         AdvancedPage (wxSize panel_size, int border)
1244                 : StockPage (Kind_Advanced, panel_size, border)
1245                 , _maximum_j2k_bandwidth (0)
1246                 , _allow_any_dcp_frame_rate (0)
1247                 , _allow_any_container (0)
1248                 , _show_experimental_audio_processors (0)
1249                 , _only_servers_encode (0)
1250                 , _log_general (0)
1251                 , _log_warning (0)
1252                 , _log_error (0)
1253                 , _log_timing (0)
1254                 , _log_debug_threed (0)
1255                 , _log_debug_encode (0)
1256                 , _log_debug_email (0)
1257         {}
1258
1259 private:
1260         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1261         {
1262                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1263 #ifdef __WXOSX__
1264                 flags |= wxALIGN_RIGHT;
1265                 text += wxT (":");
1266 #endif
1267                 wxStaticText* m = new StaticText (parent, text);
1268                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1269         }
1270
1271         void setup ()
1272         {
1273                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1274                 table->AddGrowableCol (1, 1);
1275                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1276
1277                 {
1278                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1279                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1280                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1281                         s->Add (_maximum_j2k_bandwidth, 1);
1282                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1283                         table->Add (s, 1);
1284                 }
1285
1286                 add_label_to_sizer (table, _panel, _("Video display mode"), true);
1287                 _video_display_mode = new wxChoice (_panel, wxID_ANY);
1288                 table->Add (_video_display_mode);
1289
1290                 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to change display mode)"), false);
1291                 wxFont font = restart->GetFont();
1292                 font.SetStyle (wxFONTSTYLE_ITALIC);
1293                 font.SetPointSize (font.GetPointSize() - 1);
1294                 restart->SetFont (font);
1295                 table->AddSpacer (0);
1296
1297                 _allow_any_dcp_frame_rate = new CheckBox (_panel, _("Allow any DCP frame rate"));
1298                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1299                 table->AddSpacer (0);
1300
1301                 _allow_any_container = new CheckBox (_panel, _("Allow full-frame and non-standard container ratios"));
1302                 table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
1303                 table->AddSpacer (0);
1304
1305                 restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
1306                 restart->SetFont (font);
1307                 table->AddSpacer (0);
1308
1309                 _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
1310                 table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
1311                 table->AddSpacer (0);
1312
1313                 _only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
1314                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1315                 table->AddSpacer (0);
1316
1317                 {
1318                         add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true);
1319                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1320                         _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1321                         s->Add (_frames_in_memory_multiplier, 1);
1322                         table->Add (s, 1);
1323                 }
1324
1325                 {
1326                         add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1327                         dcp::NameFormat::Map titles;
1328                         titles['t'] = wx_to_std (_("type (cpl/pkl)"));
1329                         dcp::NameFormat::Map examples;
1330                         examples['t'] = "cpl";
1331                         _dcp_metadata_filename_format = new NameFormatEditor (
1332                                 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1333                                 );
1334                         table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1335                 }
1336
1337                 {
1338                         add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1339                         dcp::NameFormat::Map titles;
1340                         titles['t'] = wx_to_std (_("type (j2c/pcm/sub)"));
1341                         titles['r'] = wx_to_std (_("reel number"));
1342                         titles['n'] = wx_to_std (_("number of reels"));
1343                         titles['c'] = wx_to_std (_("content filename"));
1344                         dcp::NameFormat::Map examples;
1345                         examples['t'] = "j2c";
1346                         examples['r'] = "1";
1347                         examples['n'] = "4";
1348                         examples['c'] = "myfile.mp4";
1349                         _dcp_asset_filename_format = new NameFormatEditor (
1350                                 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1351                                 );
1352                         table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1353                 }
1354
1355                 {
1356                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1357                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1358                         _log_general = new CheckBox (_panel, _("General"));
1359                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1360                         _log_warning = new CheckBox (_panel, _("Warnings"));
1361                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1362                         _log_error = new CheckBox (_panel, _("Errors"));
1363                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1364                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1365                         _log_timing = new CheckBox (_panel, S_("Config|Timing"));
1366                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1367                         _log_debug_threed = new CheckBox (_panel, _("Debug: 3D"));
1368                         t->Add (_log_debug_threed, 1, wxEXPAND | wxALL);
1369                         _log_debug_encode = new CheckBox (_panel, _("Debug: encode"));
1370                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1371                         _log_debug_email = new CheckBox (_panel, _("Debug: email sending"));
1372                         t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1373                         table->Add (t, 0, wxALL, 6);
1374                 }
1375
1376 #ifdef DCPOMATIC_WINDOWS
1377                 _win32_console = new CheckBox (_panel, _("Open console window"));
1378                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1379                 table->AddSpacer (0);
1380 #endif
1381
1382                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1383                 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1384                 _video_display_mode->Append (_("Simple (safer)"));
1385                 _video_display_mode->Append (_("OpenGL (faster)"));
1386                 _video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
1387                 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1388                 _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
1389                 _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
1390                 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1391                 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1392                 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1393                 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1394                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1395                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1396                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1397                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1398                 _log_debug_threed->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1399                 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1400                 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1401 #ifdef DCPOMATIC_WINDOWS
1402                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1403 #endif
1404         }
1405
1406         void config_changed ()
1407         {
1408                 Config* config = Config::instance ();
1409
1410                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1411                 switch (config->video_view_type()) {
1412                 case Config::VIDEO_VIEW_SIMPLE:
1413                         checked_set (_video_display_mode, 0);
1414                         break;
1415                 case Config::VIDEO_VIEW_OPENGL:
1416                         checked_set (_video_display_mode, 1);
1417                         break;
1418                 }
1419                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1420                 checked_set (_allow_any_container, config->allow_any_container ());
1421                 checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
1422                 checked_set (_only_servers_encode, config->only_servers_encode ());
1423                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1424                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1425                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1426                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1427                 checked_set (_log_debug_threed, config->log_types() & LogEntry::TYPE_DEBUG_THREED);
1428                 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1429                 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1430                 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1431 #ifdef DCPOMATIC_WINDOWS
1432                 checked_set (_win32_console, config->win32_console());
1433 #endif
1434         }
1435
1436         void maximum_j2k_bandwidth_changed ()
1437         {
1438                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1439         }
1440
1441         void video_display_mode_changed ()
1442         {
1443                 if (_video_display_mode->GetSelection() == 0) {
1444                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_SIMPLE);
1445                 } else {
1446                         Config::instance()->set_video_view_type (Config::VIDEO_VIEW_OPENGL);
1447                 }
1448         }
1449
1450         void frames_in_memory_multiplier_changed ()
1451         {
1452                 Config::instance()->set_frames_in_memory_multiplier (_frames_in_memory_multiplier->GetValue());
1453         }
1454
1455         void allow_any_dcp_frame_rate_changed ()
1456         {
1457                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1458         }
1459
1460         void allow_any_container_changed ()
1461         {
1462                 Config::instance()->set_allow_any_container (_allow_any_container->GetValue ());
1463         }
1464
1465         void show_experimental_audio_processors_changed ()
1466         {
1467                 Config::instance()->set_show_experimental_audio_processors (_show_experimental_audio_processors->GetValue ());
1468         }
1469
1470         void only_servers_encode_changed ()
1471         {
1472                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1473         }
1474
1475         void dcp_metadata_filename_format_changed ()
1476         {
1477                 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1478         }
1479
1480         void dcp_asset_filename_format_changed ()
1481         {
1482                 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1483         }
1484
1485         void log_changed ()
1486         {
1487                 int types = 0;
1488                 if (_log_general->GetValue ()) {
1489                         types |= LogEntry::TYPE_GENERAL;
1490                 }
1491                 if (_log_warning->GetValue ()) {
1492                         types |= LogEntry::TYPE_WARNING;
1493                 }
1494                 if (_log_error->GetValue ())  {
1495                         types |= LogEntry::TYPE_ERROR;
1496                 }
1497                 if (_log_timing->GetValue ()) {
1498                         types |= LogEntry::TYPE_TIMING;
1499                 }
1500                 if (_log_debug_threed->GetValue ()) {
1501                         types |= LogEntry::TYPE_DEBUG_THREED;
1502                 }
1503                 if (_log_debug_encode->GetValue ()) {
1504                         types |= LogEntry::TYPE_DEBUG_ENCODE;
1505                 }
1506                 if (_log_debug_email->GetValue ()) {
1507                         types |= LogEntry::TYPE_DEBUG_EMAIL;
1508                 }
1509                 Config::instance()->set_log_types (types);
1510         }
1511
1512 #ifdef DCPOMATIC_WINDOWS
1513         void win32_console_changed ()
1514         {
1515                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1516         }
1517 #endif
1518
1519         wxSpinCtrl* _maximum_j2k_bandwidth;
1520         wxChoice* _video_display_mode;
1521         wxSpinCtrl* _frames_in_memory_multiplier;
1522         wxCheckBox* _allow_any_dcp_frame_rate;
1523         wxCheckBox* _allow_any_container;
1524         wxCheckBox* _show_experimental_audio_processors;
1525         wxCheckBox* _only_servers_encode;
1526         NameFormatEditor* _dcp_metadata_filename_format;
1527         NameFormatEditor* _dcp_asset_filename_format;
1528         wxCheckBox* _log_general;
1529         wxCheckBox* _log_warning;
1530         wxCheckBox* _log_error;
1531         wxCheckBox* _log_timing;
1532         wxCheckBox* _log_debug_threed;
1533         wxCheckBox* _log_debug_encode;
1534         wxCheckBox* _log_debug_email;
1535 #ifdef DCPOMATIC_WINDOWS
1536         wxCheckBox* _win32_console;
1537 #endif
1538 };
1539
1540 wxPreferencesEditor*
1541 create_full_config_dialog ()
1542 {
1543         wxPreferencesEditor* e = new wxPreferencesEditor ();
1544
1545 #ifdef DCPOMATIC_OSX
1546         /* Width that we force some of the config panels to be on OSX so that
1547            the containing window doesn't shrink too much when we select those panels.
1548            This is obviously an unpleasant hack.
1549         */
1550         wxSize ps = wxSize (700, -1);
1551         int const border = 16;
1552 #else
1553         wxSize ps = wxSize (-1, -1);
1554         int const border = 8;
1555 #endif
1556
1557         e->AddPage (new FullGeneralPage (ps, border));
1558         e->AddPage (new SoundPage (ps, border));
1559         e->AddPage (new DefaultsPage (ps, border));
1560         e->AddPage (new EncodingServersPage (ps, border));
1561         e->AddPage (new KeysPage (ps, border));
1562         e->AddPage (new TMSPage (ps, border));
1563         e->AddPage (new EmailPage (ps, border));
1564         e->AddPage (new KDMEmailPage (ps, border));
1565         e->AddPage (new NotificationsPage (ps, border));
1566         e->AddPage (new CoverSheetPage (ps, border));
1567         e->AddPage (new AdvancedPage (ps, border));
1568         return e;
1569 }