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