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