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