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