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