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