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