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