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