Remove wxWidgets' file overwrite checks in favour of our own (because of #1383).
[dcpomatic.git] / src / wx / full_config_dialog.cc
1 /*
2     Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 /** @file src/full_config_dialog.cc
22  *  @brief A dialogue to edit all DCP-o-matic configuration.
23  */
24
25 #include "full_config_dialog.h"
26 #include "wx_util.h"
27 #include "editable_list.h"
28 #include "filter_dialog.h"
29 #include "dir_picker_ctrl.h"
30 #include "file_picker_ctrl.h"
31 #include "isdcf_metadata_dialog.h"
32 #include "server_dialog.h"
33 #include "make_chain_dialog.h"
34 #include "email_dialog.h"
35 #include "name_format_editor.h"
36 #include "nag_dialog.h"
37 #include "config_move_dialog.h"
38 #include "config_dialog.h"
39 #include "lib/config.h"
40 #include "lib/ratio.h"
41 #include "lib/filter.h"
42 #include "lib/dcp_content_type.h"
43 #include "lib/log.h"
44 #include "lib/util.h"
45 #include "lib/cross.h"
46 #include "lib/exceptions.h"
47 #include <dcp/locale_convert.h>
48 #include <dcp/exceptions.h>
49 #include <dcp/certificate_chain.h>
50 #include <wx/stdpaths.h>
51 #include <wx/preferences.h>
52 #include <wx/spinctrl.h>
53 #include <wx/filepicker.h>
54 #include <RtAudio.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/foreach.hpp>
57 #include <iostream>
58
59 using std::vector;
60 using std::string;
61 using std::list;
62 using std::cout;
63 using std::pair;
64 using std::make_pair;
65 using std::map;
66 using boost::bind;
67 using boost::shared_ptr;
68 using boost::function;
69 using boost::optional;
70 using dcp::locale_convert;
71
72 class FullGeneralPage : public GeneralPage
73 {
74 public:
75         FullGeneralPage (wxSize panel_size, int border)
76                 : GeneralPage (panel_size, border)
77         {}
78
79 private:
80         void setup ()
81         {
82                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
84
85                 int r = 0;
86                 add_language_controls (table, r);
87
88                 add_label_to_sizer (table, _panel, _("Interface complexity"), true, wxGBPosition(r, 0));
89                 _interface_complexity = new wxChoice (_panel, wxID_ANY);
90                 table->Add (_interface_complexity, wxGBPosition (r, 1));
91                 ++r;
92
93                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0));
94                 _master_encoding_threads = new wxSpinCtrl (_panel);
95                 table->Add (_master_encoding_threads, wxGBPosition (r, 1));
96                 ++r;
97
98                 add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic encode server should use"), true, wxGBPosition (r, 0));
99                 _server_encoding_threads = new wxSpinCtrl (_panel);
100                 table->Add (_server_encoding_threads, wxGBPosition (r, 1));
101                 ++r;
102
103                 add_label_to_sizer (table, _panel, _("Configuration file"), true, wxGBPosition (r, 0));
104                 _config_file = new FilePickerCtrl (_panel, _("Select configuration file"), "*.xml", true);
105                 table->Add (_config_file, wxGBPosition (r, 1));
106                 ++r;
107
108                 add_label_to_sizer (table, _panel, _("Cinema and screen database file"), true, wxGBPosition (r, 0));
109                 _cinemas_file = new FilePickerCtrl (_panel, _("Select cinema and screen database file"), "*.xml", true);
110                 table->Add (_cinemas_file, wxGBPosition (r, 1));
111                 wxButton* export_cinemas = new wxButton (_panel, wxID_ANY, _("Export..."));
112                 table->Add (export_cinemas, wxGBPosition (r, 2));
113                 ++r;
114
115                 add_play_sound_controls (table, r);
116
117 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
118                 _analyse_ebur128 = new wxCheckBox (_panel, wxID_ANY, _("Find integrated loudness, true peak and loudness range when analysing audio"));
119                 table->Add (_analyse_ebur128, wxGBPosition (r, 0), wxGBSpan (1, 2));
120                 ++r;
121 #endif
122
123                 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
124                 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
125                 ++r;
126
127                 add_update_controls (table, r);
128
129                 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
130                 bottom_table->AddGrowableCol (1, 1);
131
132                 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
133                 _issuer = new wxTextCtrl (_panel, wxID_ANY);
134                 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
135
136                 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
137                 _creator = new wxTextCtrl (_panel, wxID_ANY);
138                 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
139
140                 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
141                 ++r;
142
143                 _config_file->Bind  (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::config_file_changed,   this));
144                 _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind (&FullGeneralPage::cinemas_file_changed,  this));
145
146                 _interface_complexity->Append (_("Simple"));
147                 _interface_complexity->Append (_("Full"));
148                 _interface_complexity->Bind (wxEVT_CHOICE, boost::bind(&FullGeneralPage::interface_complexity_changed, this));
149
150                 _master_encoding_threads->SetRange (1, 128);
151                 _master_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::master_encoding_threads_changed, this));
152                 _server_encoding_threads->SetRange (1, 128);
153                 _server_encoding_threads->Bind (wxEVT_SPINCTRL, boost::bind (&FullGeneralPage::server_encoding_threads_changed, this));
154                 export_cinemas->Bind (wxEVT_BUTTON, boost::bind (&FullGeneralPage::export_cinemas_file, this));
155
156 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
157                 _analyse_ebur128->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::analyse_ebur128_changed, this));
158 #endif
159                 _automatic_audio_analysis->Bind (wxEVT_CHECKBOX, boost::bind (&FullGeneralPage::automatic_audio_analysis_changed, this));
160
161                 _issuer->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::issuer_changed, this));
162                 _creator->Bind (wxEVT_TEXT, boost::bind (&FullGeneralPage::creator_changed, this));
163         }
164
165         void config_changed ()
166         {
167                 Config* config = Config::instance ();
168
169                 switch (config->interface_complexity()) {
170                 case Config::INTERFACE_SIMPLE:
171                         checked_set (_interface_complexity, 0);
172                         break;
173                 case Config::INTERFACE_FULL:
174                         checked_set (_interface_complexity, 1);
175                         break;
176                 }
177                 checked_set (_master_encoding_threads, config->master_encoding_threads ());
178                 checked_set (_server_encoding_threads, config->server_encoding_threads ());
179 #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG
180                 checked_set (_analyse_ebur128, config->analyse_ebur128 ());
181 #endif
182                 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
183                 checked_set (_issuer, config->dcp_issuer ());
184                 checked_set (_creator, config->dcp_creator ());
185                 checked_set (_config_file, config->config_file());
186                 checked_set (_cinemas_file, config->cinemas_file());
187
188                 GeneralPage::config_changed ();
189         }
190
191         void export_cinemas_file ()
192         {
193                 wxFileDialog* d = new wxFileDialog (
194                         _panel, _("Select Cinemas File"), wxEmptyString, wxEmptyString, wxT ("XML files (*.xml)|*.xml"), wxFD_SAVE
195                 );
196
197                 if (d->ShowModal () == wxID_OK) {
198                         optional<boost::filesystem::path> path = path_from_file_dialog (d, "xml");
199                         if (path) {
200                                 boost::filesystem::copy_file (Config::instance()->cinemas_file(), *path);
201                         }
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 wxButton (_panel, wxID_ANY, _("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 wxCheckBox (_panel, wxID_ANY, _("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 wxCheckBox (_panel, wxID_ANY, _("Search network for servers"));
591                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
592
593                 vector<string> columns;
594                 columns.push_back (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 wxTextCtrl (_panel, wxID_ANY);
672                 table->Add (_tms_password, 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->Bind (wxEVT_TEXT, 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<Protocol> (_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 (wx_to_std (_tms_password->GetValue ()));
718         }
719
720         wxChoice* _tms_protocol;
721         wxTextCtrl* _tms_ip;
722         wxTextCtrl* _tms_path;
723         wxTextCtrl* _tms_user;
724         wxTextCtrl* _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                         table->Add (s, 1, wxEXPAND | wxALL);
769                 }
770
771                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
772                 _user = new wxTextCtrl (_panel, wxID_ANY);
773                 table->Add (_user, 1, wxEXPAND | wxALL);
774
775                 add_label_to_sizer (table, _panel, _("Mail password"), true);
776                 _password = new wxTextCtrl (_panel, wxID_ANY);
777                 table->Add (_password, 1, wxEXPAND | wxALL);
778
779                 _server->Bind (wxEVT_TEXT, boost::bind (&EmailPage::server_changed, this));
780                 _port->Bind (wxEVT_SPINCTRL, boost::bind (&EmailPage::port_changed, this));
781                 _user->Bind (wxEVT_TEXT, boost::bind (&EmailPage::user_changed, this));
782                 _password->Bind (wxEVT_TEXT, boost::bind (&EmailPage::password_changed, this));
783         }
784
785         void config_changed ()
786         {
787                 Config* config = Config::instance ();
788
789                 checked_set (_server, config->mail_server ());
790                 checked_set (_port, config->mail_port ());
791                 checked_set (_user, config->mail_user ());
792                 checked_set (_password, config->mail_password ());
793         }
794
795         void server_changed ()
796         {
797                 Config::instance()->set_mail_server (wx_to_std (_server->GetValue ()));
798         }
799
800         void port_changed ()
801         {
802                 Config::instance()->set_mail_port (_port->GetValue ());
803         }
804
805         void user_changed ()
806         {
807                 Config::instance()->set_mail_user (wx_to_std (_user->GetValue ()));
808         }
809
810         void password_changed ()
811         {
812                 Config::instance()->set_mail_password (wx_to_std (_password->GetValue ()));
813         }
814
815         wxTextCtrl* _server;
816         wxSpinCtrl* _port;
817         wxTextCtrl* _user;
818         wxTextCtrl* _password;
819 };
820
821 class KDMEmailPage : public StandardPage
822 {
823 public:
824
825         KDMEmailPage (wxSize panel_size, int border)
826 #ifdef DCPOMATIC_OSX
827                 /* We have to force both width and height of this one */
828                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
829 #else
830                 : StandardPage (panel_size, border)
831 #endif
832         {}
833
834         wxString GetName () const
835         {
836                 return _("KDM Email");
837         }
838
839 #ifdef DCPOMATIC_OSX
840         wxBitmap GetLargeIcon () const
841         {
842                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
843         }
844 #endif
845
846 private:
847         void setup ()
848         {
849                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
850                 table->AddGrowableCol (1, 1);
851                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
852
853                 add_label_to_sizer (table, _panel, _("Subject"), true);
854                 _subject = new wxTextCtrl (_panel, wxID_ANY);
855                 table->Add (_subject, 1, wxEXPAND | wxALL);
856
857                 add_label_to_sizer (table, _panel, _("From address"), true);
858                 _from = new wxTextCtrl (_panel, wxID_ANY);
859                 table->Add (_from, 1, wxEXPAND | wxALL);
860
861                 vector<string> columns;
862                 columns.push_back (wx_to_std (_("Address")));
863                 add_label_to_sizer (table, _panel, _("CC addresses"), true);
864                 _cc = new EditableList<string, EmailDialog> (
865                         _panel,
866                         columns,
867                         bind (&Config::kdm_cc, Config::instance()),
868                         bind (&Config::set_kdm_cc, Config::instance(), _1),
869                         bind (&column, _1)
870                         );
871                 table->Add (_cc, 1, wxEXPAND | wxALL);
872
873                 add_label_to_sizer (table, _panel, _("BCC address"), true);
874                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
875                 table->Add (_bcc, 1, wxEXPAND | wxALL);
876
877                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
878                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
879
880                 _reset_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
881                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
882
883                 _cc->layout ();
884
885                 _subject->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
886                 _from->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_from_changed, this));
887                 _bcc->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
888                 _email->Bind (wxEVT_TEXT, boost::bind (&KDMEmailPage::kdm_email_changed, this));
889                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&KDMEmailPage::reset_email, this));
890         }
891
892         void config_changed ()
893         {
894                 Config* config = Config::instance ();
895
896                 checked_set (_subject, config->kdm_subject ());
897                 checked_set (_from, config->kdm_from ());
898                 checked_set (_bcc, config->kdm_bcc ());
899                 checked_set (_email, Config::instance()->kdm_email ());
900         }
901
902         void kdm_subject_changed ()
903         {
904                 Config::instance()->set_kdm_subject (wx_to_std (_subject->GetValue ()));
905         }
906
907         void kdm_from_changed ()
908         {
909                 Config::instance()->set_kdm_from (wx_to_std (_from->GetValue ()));
910         }
911
912         void kdm_bcc_changed ()
913         {
914                 Config::instance()->set_kdm_bcc (wx_to_std (_bcc->GetValue ()));
915         }
916
917         void kdm_email_changed ()
918         {
919                 if (_email->GetValue().IsEmpty ()) {
920                         /* Sometimes we get sent an erroneous notification that the email
921                            is empty; I don't know why.
922                         */
923                         return;
924                 }
925                 Config::instance()->set_kdm_email (wx_to_std (_email->GetValue ()));
926         }
927
928         void reset_email ()
929         {
930                 Config::instance()->reset_kdm_email ();
931                 checked_set (_email, Config::instance()->kdm_email ());
932         }
933
934         wxTextCtrl* _subject;
935         wxTextCtrl* _from;
936         EditableList<string, EmailDialog>* _cc;
937         wxTextCtrl* _bcc;
938         wxTextCtrl* _email;
939         wxButton* _reset_email;
940 };
941
942 class AccountsPage : public StandardPage
943 {
944 public:
945         AccountsPage (wxSize panel_size, int border)
946                 : StandardPage (panel_size, border)
947         {}
948
949         wxString GetName () const
950         {
951                 return _("Accounts");
952         }
953
954 #ifdef DCPOMATIC_OSX
955         wxBitmap GetLargeIcon () const
956         {
957                 return wxBitmap ("accounts", wxBITMAP_TYPE_PNG_RESOURCE);
958         }
959 #endif
960
961         void setup ()
962         {
963                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
964                 table->AddGrowableCol (1, 1);
965                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
966
967                 add_label_to_sizer (table, _panel, _("certificates.barco.com username"), true);
968                 _barco_username = new wxTextCtrl (_panel, wxID_ANY);
969                 table->Add (_barco_username, 1, wxEXPAND | wxALL);
970
971                 add_label_to_sizer (table, _panel, _("certificates.barco.com password"), true);
972                 _barco_password = new wxTextCtrl (_panel, wxID_ANY);
973                 table->Add (_barco_password, 1, wxEXPAND | wxALL);
974
975                 add_label_to_sizer (table, _panel, _("certificates.christiedigital.com username"), true);
976                 _christie_username = new wxTextCtrl (_panel, wxID_ANY);
977                 table->Add (_christie_username, 1, wxEXPAND | wxALL);
978
979                 add_label_to_sizer (table, _panel, _("certificates.christiedigital.com password"), true);
980                 _christie_password = new wxTextCtrl (_panel, wxID_ANY);
981                 table->Add (_christie_password, 1, wxEXPAND | wxALL);
982
983                 _barco_username->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::barco_username_changed, this));
984                 _barco_password->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::barco_password_changed, this));
985                 _christie_username->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::christie_username_changed, this));
986                 _christie_password->Bind (wxEVT_TEXT, boost::bind(&AccountsPage::christie_password_changed, this));
987         }
988
989         void config_changed ()
990         {
991                 Config* config = Config::instance ();
992
993                 checked_set (_barco_username, config->barco_username().get_value_or(""));
994                 checked_set (_barco_password, config->barco_password().get_value_or(""));
995                 checked_set (_christie_username, config->christie_username().get_value_or(""));
996                 checked_set (_christie_password, config->christie_password().get_value_or(""));
997         }
998
999         void barco_username_changed ()
1000         {
1001                 wxString const s = _barco_username->GetValue();
1002                 if (!s.IsEmpty()) {
1003                         Config::instance()->set_barco_username (wx_to_std(s));
1004                 } else {
1005                         Config::instance()->unset_barco_username ();
1006                 }
1007         }
1008
1009         void barco_password_changed ()
1010         {
1011                 wxString const s = _barco_password->GetValue();
1012                 if (!s.IsEmpty()) {
1013                         Config::instance()->set_barco_password (wx_to_std(s));
1014                 } else {
1015                         Config::instance()->unset_barco_password ();
1016                 }
1017         }
1018
1019         void christie_username_changed ()
1020         {
1021                 wxString const s = _christie_username->GetValue();
1022                 if (!s.IsEmpty()) {
1023                         Config::instance()->set_christie_username (wx_to_std(s));
1024                 } else {
1025                         Config::instance()->unset_christie_username ();
1026                 }
1027         }
1028
1029         void christie_password_changed ()
1030         {
1031                 wxString const s = _christie_password->GetValue();
1032                 if (!s.IsEmpty()) {
1033                         Config::instance()->set_christie_password (wx_to_std(s));
1034                 } else {
1035                         Config::instance()->unset_christie_password ();
1036                 }
1037         }
1038
1039 private:
1040         wxTextCtrl* _barco_username;
1041         wxTextCtrl* _barco_password;
1042         wxTextCtrl* _christie_username;
1043         wxTextCtrl* _christie_password;
1044 };
1045
1046
1047 class NotificationsPage : public StandardPage
1048 {
1049 public:
1050         NotificationsPage (wxSize panel_size, int border)
1051 #ifdef DCPOMATIC_OSX
1052                 /* We have to force both width and height of this one */
1053                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
1054 #else
1055                 : StandardPage (panel_size, border)
1056 #endif
1057         {}
1058
1059         wxString GetName () const
1060         {
1061                 return _("Notifications");
1062         }
1063
1064 #ifdef DCPOMATIC_OSX
1065         wxBitmap GetLargeIcon () const
1066         {
1067                 return wxBitmap ("notifications", wxBITMAP_TYPE_PNG_RESOURCE);
1068         }
1069 #endif
1070
1071 private:
1072         void setup ()
1073         {
1074                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1075                 table->AddGrowableCol (1, 1);
1076                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1077
1078                 _enable_message_box = new wxCheckBox (_panel, wxID_ANY, _("Message box"));
1079                 table->Add (_enable_message_box, 1, wxEXPAND | wxALL);
1080                 table->AddSpacer (0);
1081
1082                 _enable_email = new wxCheckBox (_panel, wxID_ANY, _("Email"));
1083                 table->Add (_enable_email, 1, wxEXPAND | wxALL);
1084                 table->AddSpacer (0);
1085
1086                 add_label_to_sizer (table, _panel, _("Subject"), true);
1087                 _subject = new wxTextCtrl (_panel, wxID_ANY);
1088                 table->Add (_subject, 1, wxEXPAND | wxALL);
1089
1090                 add_label_to_sizer (table, _panel, _("From address"), true);
1091                 _from = new wxTextCtrl (_panel, wxID_ANY);
1092                 table->Add (_from, 1, wxEXPAND | wxALL);
1093
1094                 add_label_to_sizer (table, _panel, _("To address"), true);
1095                 _to = new wxTextCtrl (_panel, wxID_ANY);
1096                 table->Add (_to, 1, wxEXPAND | wxALL);
1097
1098                 vector<string> columns;
1099                 columns.push_back (wx_to_std (_("Address")));
1100                 add_label_to_sizer (table, _panel, _("CC addresses"), true);
1101                 _cc = new EditableList<string, EmailDialog> (
1102                         _panel,
1103                         columns,
1104                         bind (&Config::notification_cc, Config::instance()),
1105                         bind (&Config::set_notification_cc, Config::instance(), _1),
1106                         bind (&column, _1)
1107                         );
1108                 table->Add (_cc, 1, wxEXPAND | wxALL);
1109
1110                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1111                 _bcc = new wxTextCtrl (_panel, wxID_ANY);
1112                 table->Add (_bcc, 1, wxEXPAND | wxALL);
1113
1114                 _email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1115                 _panel->GetSizer()->Add (_email, 0, wxEXPAND | wxALL, _border);
1116
1117                 _reset_email = new wxButton (_panel, wxID_ANY, _("Reset to default subject and text"));
1118                 _panel->GetSizer()->Add (_reset_email, 0, wxEXPAND | wxALL, _border);
1119
1120                 _cc->layout ();
1121
1122                 _enable_message_box->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_message_box, Config::MESSAGE_BOX));
1123                 _enable_email->Bind (wxEVT_CHECKBOX, boost::bind (&NotificationsPage::type_changed, this, _enable_email, Config::EMAIL));
1124
1125                 _subject->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_subject_changed, this));
1126                 _from->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_from_changed, this));
1127                 _to->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_to_changed, this));
1128                 _bcc->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_bcc_changed, this));
1129                 _email->Bind (wxEVT_TEXT, boost::bind (&NotificationsPage::notification_email_changed, this));
1130                 _reset_email->Bind (wxEVT_BUTTON, boost::bind (&NotificationsPage::reset_email, this));
1131
1132                 update_sensitivity ();
1133         }
1134
1135         void update_sensitivity ()
1136         {
1137                 bool const s = _enable_email->GetValue();
1138                 _subject->Enable(s);
1139                 _from->Enable(s);
1140                 _to->Enable(s);
1141                 _cc->Enable(s);
1142                 _bcc->Enable(s);
1143                 _email->Enable(s);
1144                 _reset_email->Enable(s);
1145         }
1146
1147         void config_changed ()
1148         {
1149                 Config* config = Config::instance ();
1150
1151                 checked_set (_enable_message_box, config->notification(Config::MESSAGE_BOX));
1152                 checked_set (_enable_email, config->notification(Config::EMAIL));
1153                 checked_set (_subject, config->notification_subject ());
1154                 checked_set (_from, config->notification_from ());
1155                 checked_set (_to, config->notification_to ());
1156                 checked_set (_bcc, config->notification_bcc ());
1157                 checked_set (_email, Config::instance()->notification_email ());
1158
1159                 update_sensitivity ();
1160         }
1161
1162         void notification_subject_changed ()
1163         {
1164                 Config::instance()->set_notification_subject (wx_to_std (_subject->GetValue ()));
1165         }
1166
1167         void notification_from_changed ()
1168         {
1169                 Config::instance()->set_notification_from (wx_to_std (_from->GetValue ()));
1170         }
1171
1172         void notification_to_changed ()
1173         {
1174                 Config::instance()->set_notification_to (wx_to_std (_to->GetValue ()));
1175         }
1176
1177         void notification_bcc_changed ()
1178         {
1179                 Config::instance()->set_notification_bcc (wx_to_std (_bcc->GetValue ()));
1180         }
1181
1182         void notification_email_changed ()
1183         {
1184                 if (_email->GetValue().IsEmpty ()) {
1185                         /* Sometimes we get sent an erroneous notification that the email
1186                            is empty; I don't know why.
1187                         */
1188                         return;
1189                 }
1190                 Config::instance()->set_notification_email (wx_to_std (_email->GetValue ()));
1191         }
1192
1193         void reset_email ()
1194         {
1195                 Config::instance()->reset_notification_email ();
1196                 checked_set (_email, Config::instance()->notification_email ());
1197         }
1198
1199         void type_changed (wxCheckBox* b, Config::Notification n)
1200         {
1201                 Config::instance()->set_notification(n, b->GetValue());
1202                 update_sensitivity ();
1203         }
1204
1205         wxCheckBox* _enable_message_box;
1206         wxCheckBox* _enable_email;
1207
1208         wxTextCtrl* _subject;
1209         wxTextCtrl* _from;
1210         wxTextCtrl* _to;
1211         EditableList<string, EmailDialog>* _cc;
1212         wxTextCtrl* _bcc;
1213         wxTextCtrl* _email;
1214         wxButton* _reset_email;
1215 };
1216
1217 class CoverSheetPage : public StandardPage
1218 {
1219 public:
1220
1221         CoverSheetPage (wxSize panel_size, int border)
1222 #ifdef DCPOMATIC_OSX
1223                 /* We have to force both width and height of this one */
1224                 : StandardPage (wxSize (panel_size.GetWidth(), 128), border)
1225 #else
1226                 : StandardPage (panel_size, border)
1227 #endif
1228         {}
1229
1230         wxString GetName () const
1231         {
1232                 return _("Cover Sheet");
1233         }
1234
1235 #ifdef DCPOMATIC_OSX
1236         wxBitmap GetLargeIcon () const
1237         {
1238                 return wxBitmap ("cover_sheet", wxBITMAP_TYPE_PNG_RESOURCE);
1239         }
1240 #endif
1241
1242 private:
1243         void setup ()
1244         {
1245                 _cover_sheet = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (-1, 200), wxTE_MULTILINE);
1246                 _panel->GetSizer()->Add (_cover_sheet, 0, wxEXPAND | wxALL, _border);
1247
1248                 _reset_cover_sheet = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1249                 _panel->GetSizer()->Add (_reset_cover_sheet, 0, wxEXPAND | wxALL, _border);
1250
1251                 _cover_sheet->Bind (wxEVT_TEXT, boost::bind (&CoverSheetPage::cover_sheet_changed, this));
1252                 _reset_cover_sheet->Bind (wxEVT_BUTTON, boost::bind (&CoverSheetPage::reset_cover_sheet, this));
1253         }
1254
1255         void config_changed ()
1256         {
1257                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1258         }
1259
1260         void cover_sheet_changed ()
1261         {
1262                 if (_cover_sheet->GetValue().IsEmpty ()) {
1263                         /* Sometimes we get sent an erroneous notification that the cover sheet
1264                            is empty; I don't know why.
1265                         */
1266                         return;
1267                 }
1268                 Config::instance()->set_cover_sheet (wx_to_std (_cover_sheet->GetValue ()));
1269         }
1270
1271         void reset_cover_sheet ()
1272         {
1273                 Config::instance()->reset_cover_sheet ();
1274                 checked_set (_cover_sheet, Config::instance()->cover_sheet ());
1275         }
1276
1277         wxTextCtrl* _cover_sheet;
1278         wxButton* _reset_cover_sheet;
1279 };
1280
1281
1282 /** @class AdvancedPage
1283  *  @brief Advanced page of the preferences dialog.
1284  */
1285 class AdvancedPage : public StockPage
1286 {
1287 public:
1288         AdvancedPage (wxSize panel_size, int border)
1289                 : StockPage (Kind_Advanced, panel_size, border)
1290                 , _maximum_j2k_bandwidth (0)
1291                 , _allow_any_dcp_frame_rate (0)
1292                 , _allow_any_container (0)
1293                 , _only_servers_encode (0)
1294                 , _log_general (0)
1295                 , _log_warning (0)
1296                 , _log_error (0)
1297                 , _log_timing (0)
1298                 , _log_debug_decode (0)
1299                 , _log_debug_encode (0)
1300                 , _log_debug_email (0)
1301         {}
1302
1303 private:
1304         void add_top_aligned_label_to_sizer (wxSizer* table, wxWindow* parent, wxString text)
1305         {
1306                 int flags = wxALIGN_TOP | wxTOP | wxLEFT;
1307 #ifdef __WXOSX__
1308                 flags |= wxALIGN_RIGHT;
1309                 text += wxT (":");
1310 #endif
1311                 wxStaticText* m = new wxStaticText (parent, wxID_ANY, text);
1312                 table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
1313         }
1314
1315         void setup ()
1316         {
1317                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1318                 table->AddGrowableCol (1, 1);
1319                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1320
1321                 {
1322                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1323                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1324                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1325                         s->Add (_maximum_j2k_bandwidth, 1);
1326                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1327                         table->Add (s, 1);
1328                 }
1329
1330                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1331                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1332                 table->AddSpacer (0);
1333
1334                 _allow_any_container = new wxCheckBox (_panel, wxID_ANY, _("Allow non-standard container ratios"));
1335                 table->Add (_allow_any_container, 1, wxEXPAND | wxALL);
1336                 table->AddSpacer (0);
1337
1338                 wxStaticText* restart = add_label_to_sizer (table, _panel, _("(restart DCP-o-matic to see all ratios)"), false);
1339                 wxFont font = restart->GetFont();
1340                 font.SetStyle (wxFONTSTYLE_ITALIC);
1341                 font.SetPointSize (font.GetPointSize() - 1);
1342                 restart->SetFont (font);
1343                 table->AddSpacer (0);
1344
1345                 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1346                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1347                 table->AddSpacer (0);
1348
1349                 {
1350                         add_label_to_sizer (table, _panel, _("Maximum number of frames to store per thread"), true);
1351                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1352                         _frames_in_memory_multiplier = new wxSpinCtrl (_panel);
1353                         s->Add (_frames_in_memory_multiplier, 1);
1354                         table->Add (s, 1);
1355                 }
1356
1357                 {
1358                         add_top_aligned_label_to_sizer (table, _panel, _("DCP metadata filename format"));
1359                         dcp::NameFormat::Map titles;
1360                         titles['t'] = "type (cpl/pkl)";
1361                         dcp::NameFormat::Map examples;
1362                         examples['t'] = "cpl";
1363                         _dcp_metadata_filename_format = new NameFormatEditor (
1364                                 _panel, Config::instance()->dcp_metadata_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.xml"
1365                                 );
1366                         table->Add (_dcp_metadata_filename_format->panel(), 1, wxEXPAND | wxALL);
1367                 }
1368
1369                 {
1370                         add_top_aligned_label_to_sizer (table, _panel, _("DCP asset filename format"));
1371                         dcp::NameFormat::Map titles;
1372                         titles['t'] = "type (j2c/pcm/sub)";
1373                         titles['r'] = "reel number";
1374                         titles['n'] = "number of reels";
1375                         titles['c'] = "content filename";
1376                         dcp::NameFormat::Map examples;
1377                         examples['t'] = "j2c";
1378                         examples['r'] = "1";
1379                         examples['n'] = "4";
1380                         examples['c'] = "myfile.mp4";
1381                         _dcp_asset_filename_format = new NameFormatEditor (
1382                                 _panel, Config::instance()->dcp_asset_filename_format(), titles, examples, "_eb1c112c-ca3c-4ae6-9263-c6714ff05d64.mxf"
1383                                 );
1384                         table->Add (_dcp_asset_filename_format->panel(), 1, wxEXPAND | wxALL);
1385                 }
1386
1387                 {
1388                         add_top_aligned_label_to_sizer (table, _panel, _("Log"));
1389                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1390                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1391                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1392                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1393                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1394                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1395                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1396                         /// TRANSLATORS: translate the word "Timing" here; do not include the "Config|" prefix
1397                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1398                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1399                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1400                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1401                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1402                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1403                         _log_debug_email = new wxCheckBox (_panel, wxID_ANY, _("Debug: email sending"));
1404                         t->Add (_log_debug_email, 1, wxEXPAND | wxALL);
1405                         table->Add (t, 0, wxALL, 6);
1406                 }
1407
1408 #ifdef DCPOMATIC_WINDOWS
1409                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1410                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1411                 table->AddSpacer (0);
1412 #endif
1413
1414                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1415                 _maximum_j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1416                 _allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1417                 _allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
1418                 _only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1419                 _frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
1420                 _dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
1421                 _dcp_asset_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_asset_filename_format_changed, this));
1422                 _log_general->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1423                 _log_warning->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1424                 _log_error->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1425                 _log_timing->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1426                 _log_debug_decode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1427                 _log_debug_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1428                 _log_debug_email->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::log_changed, this));
1429 #ifdef DCPOMATIC_WINDOWS
1430                 _win32_console->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::win32_console_changed, this));
1431 #endif
1432         }
1433
1434         void config_changed ()
1435         {
1436                 Config* config = Config::instance ();
1437
1438                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1439                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1440                 checked_set (_allow_any_container, config->allow_any_container ());
1441                 checked_set (_only_servers_encode, config->only_servers_encode ());
1442                 checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
1443                 checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
1444                 checked_set (_log_error, config->log_types() & LogEntry::TYPE_ERROR);
1445                 checked_set (_log_timing, config->log_types() & LogEntry::TYPE_TIMING);
1446                 checked_set (_log_debug_decode, config->log_types() & LogEntry::TYPE_DEBUG_DECODE);
1447                 checked_set (_log_debug_encode, config->log_types() & LogEntry::TYPE_DEBUG_ENCODE);
1448                 checked_set (_log_debug_email, config->log_types() & LogEntry::TYPE_DEBUG_EMAIL);
1449                 checked_set (_frames_in_memory_multiplier, config->frames_in_memory_multiplier());
1450 #ifdef DCPOMATIC_WINDOWS
1451                 checked_set (_win32_console, config->win32_console());
1452 #endif
1453         }
1454
1455         void maximum_j2k_bandwidth_changed ()
1456         {
1457                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1458         }
1459
1460         void frames_in_memory_multiplier_changed ()
1461         {
1462                 Config::instance()->set_frames_in_memory_multiplier (_frames_in_memory_multiplier->GetValue());
1463         }
1464
1465         void allow_any_dcp_frame_rate_changed ()
1466         {
1467                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1468         }
1469
1470         void allow_any_container_changed ()
1471         {
1472                 Config::instance()->set_allow_any_container (_allow_any_container->GetValue ());
1473         }
1474
1475         void only_servers_encode_changed ()
1476         {
1477                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1478         }
1479
1480         void dcp_metadata_filename_format_changed ()
1481         {
1482                 Config::instance()->set_dcp_metadata_filename_format (_dcp_metadata_filename_format->get ());
1483         }
1484
1485         void dcp_asset_filename_format_changed ()
1486         {
1487                 Config::instance()->set_dcp_asset_filename_format (_dcp_asset_filename_format->get ());
1488         }
1489
1490         void log_changed ()
1491         {
1492                 int types = 0;
1493                 if (_log_general->GetValue ()) {
1494                         types |= LogEntry::TYPE_GENERAL;
1495                 }
1496                 if (_log_warning->GetValue ()) {
1497                         types |= LogEntry::TYPE_WARNING;
1498                 }
1499                 if (_log_error->GetValue ())  {
1500                         types |= LogEntry::TYPE_ERROR;
1501                 }
1502                 if (_log_timing->GetValue ()) {
1503                         types |= LogEntry::TYPE_TIMING;
1504                 }
1505                 if (_log_debug_decode->GetValue ()) {
1506                         types |= LogEntry::TYPE_DEBUG_DECODE;
1507                 }
1508                 if (_log_debug_encode->GetValue ()) {
1509                         types |= LogEntry::TYPE_DEBUG_ENCODE;
1510                 }
1511                 if (_log_debug_email->GetValue ()) {
1512                         types |= LogEntry::TYPE_DEBUG_EMAIL;
1513                 }
1514                 Config::instance()->set_log_types (types);
1515         }
1516
1517 #ifdef DCPOMATIC_WINDOWS
1518         void win32_console_changed ()
1519         {
1520                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1521         }
1522 #endif
1523
1524         wxSpinCtrl* _maximum_j2k_bandwidth;
1525         wxSpinCtrl* _frames_in_memory_multiplier;
1526         wxCheckBox* _allow_any_dcp_frame_rate;
1527         wxCheckBox* _allow_any_container;
1528         wxCheckBox* _only_servers_encode;
1529         NameFormatEditor* _dcp_metadata_filename_format;
1530         NameFormatEditor* _dcp_asset_filename_format;
1531         wxCheckBox* _log_general;
1532         wxCheckBox* _log_warning;
1533         wxCheckBox* _log_error;
1534         wxCheckBox* _log_timing;
1535         wxCheckBox* _log_debug_decode;
1536         wxCheckBox* _log_debug_encode;
1537         wxCheckBox* _log_debug_email;
1538 #ifdef DCPOMATIC_WINDOWS
1539         wxCheckBox* _win32_console;
1540 #endif
1541 };
1542
1543 wxPreferencesEditor*
1544 create_full_config_dialog ()
1545 {
1546         wxPreferencesEditor* e = new wxPreferencesEditor ();
1547
1548 #ifdef DCPOMATIC_OSX
1549         /* Width that we force some of the config panels to be on OSX so that
1550            the containing window doesn't shrink too much when we select those panels.
1551            This is obviously an unpleasant hack.
1552         */
1553         wxSize ps = wxSize (600, -1);
1554         int const border = 16;
1555 #else
1556         wxSize ps = wxSize (-1, -1);
1557         int const border = 8;
1558 #endif
1559
1560         e->AddPage (new FullGeneralPage (ps, border));
1561         e->AddPage (new DefaultsPage (ps, border));
1562         e->AddPage (new EncodingServersPage (ps, border));
1563         e->AddPage (new KeysPage (ps, border));
1564         e->AddPage (new TMSPage (ps, border));
1565         e->AddPage (new EmailPage (ps, border));
1566         e->AddPage (new KDMEmailPage (ps, border));
1567         e->AddPage (new AccountsPage (ps, border));
1568         e->AddPage (new NotificationsPage (ps, border));
1569         e->AddPage (new CoverSheetPage (ps, border));
1570         e->AddPage (new AdvancedPage (ps, border));
1571         return e;
1572 }