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