Add option to analyse audio automatically when content is added (#673).
[dcpomatic.git] / src / wx / config_dialog.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file src/config_dialog.cc
21  *  @brief A dialogue to edit DCP-o-matic configuration.
22  */
23
24 #include "config_dialog.h"
25 #include "wx_util.h"
26 #include "editable_list.h"
27 #include "filter_dialog.h"
28 #include "dir_picker_ctrl.h"
29 #include "isdcf_metadata_dialog.h"
30 #include "server_dialog.h"
31 #include "make_chain_dialog.h"
32 #include "lib/config.h"
33 #include "lib/ratio.h"
34 #include "lib/filter.h"
35 #include "lib/dcp_content_type.h"
36 #include "lib/log.h"
37 #include "lib/util.h"
38 #include "lib/raw_convert.h"
39 #include "lib/cross.h"
40 #include "lib/exceptions.h"
41 #include <dcp/exceptions.h>
42 #include <dcp/certificate_chain.h>
43 #include <wx/stdpaths.h>
44 #include <wx/preferences.h>
45 #include <wx/filepicker.h>
46 #include <wx/spinctrl.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/filesystem.hpp>
49 #include <boost/foreach.hpp>
50 #include <iostream>
51
52 using std::vector;
53 using std::string;
54 using std::list;
55 using std::cout;
56 using boost::bind;
57 using boost::shared_ptr;
58 using boost::lexical_cast;
59 using boost::function;
60
61 class Page
62 {
63 public:
64         Page (wxSize panel_size, int border)
65                 : _border (border)
66                 , _panel (0)
67                 , _panel_size (panel_size)
68                 , _window_exists (false)
69         {
70                 _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
71         }
72
73         virtual ~Page () {}
74
75 protected:
76         wxWindow* create_window (wxWindow* parent)
77         {
78                 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
79                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
80                 _panel->SetSizer (s);
81
82                 setup ();
83                 _window_exists = true;
84                 config_changed ();
85
86                 _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
87
88                 return _panel;
89         }
90
91         int _border;
92         wxPanel* _panel;
93
94 private:
95         virtual void config_changed () = 0;
96         virtual void setup () = 0;
97
98         void config_changed_wrapper ()
99         {
100                 if (_window_exists) {
101                         config_changed ();
102                 }
103         }
104
105         void window_destroyed ()
106         {
107                 _window_exists = false;
108         }
109
110         wxSize _panel_size;
111         boost::signals2::scoped_connection _config_connection;
112         bool _window_exists;
113 };
114
115 class StockPage : public wxStockPreferencesPage, public Page
116 {
117 public:
118         StockPage (Kind kind, wxSize panel_size, int border)
119                 : wxStockPreferencesPage (kind)
120                 , Page (panel_size, border)
121         {}
122
123         wxWindow* CreateWindow (wxWindow* parent)
124         {
125                 return create_window (parent);
126         }
127 };
128
129 class StandardPage : public wxPreferencesPage, public Page
130 {
131 public:
132         StandardPage (wxSize panel_size, int border)
133                 : Page (panel_size, border)
134         {}
135
136         wxWindow* CreateWindow (wxWindow* parent)
137         {
138                 return create_window (parent);
139         }
140 };
141
142 class GeneralPage : public StockPage
143 {
144 public:
145         GeneralPage (wxSize panel_size, int border)
146                 : StockPage (Kind_General, panel_size, border)
147         {}
148
149 private:
150         void setup ()
151         {
152                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
153                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
154
155                 int r = 0;
156                 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
157                 table->Add (_set_language, wxGBPosition (r, 0));
158                 _language = new wxChoice (_panel, wxID_ANY);
159                 _language->Append (wxT ("Deutsch"));
160                 _language->Append (wxT ("English"));
161                 _language->Append (wxT ("Español"));
162                 _language->Append (wxT ("Français"));
163                 _language->Append (wxT ("Italiano"));
164                 _language->Append (wxT ("Nederlands"));
165                 _language->Append (wxT ("Svenska"));
166                 _language->Append (wxT ("Русский"));
167                 _language->Append (wxT ("Polski"));
168                 _language->Append (wxT ("Danske"));
169                 table->Add (_language, wxGBPosition (r, 1));
170                 ++r;
171
172                 wxStaticText* restart = add_label_to_grid_bag_sizer (
173                         table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
174                         );
175                 wxFont font = restart->GetFont();
176                 font.SetStyle (wxFONTSTYLE_ITALIC);
177                 font.SetPointSize (font.GetPointSize() - 1);
178                 restart->SetFont (font);
179                 ++r;
180
181                 add_label_to_grid_bag_sizer (table, _panel, _("Threads to use for encoding on this host"), true, wxGBPosition (r, 0));
182                 _num_local_encoding_threads = new wxSpinCtrl (_panel);
183                 table->Add (_num_local_encoding_threads, wxGBPosition (r, 1));
184                 ++r;
185
186                 _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio"));
187                 table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2));
188                 ++r;
189
190                 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
191                 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
192                 ++r;
193
194                 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
195                 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
196                 ++r;
197
198                 wxFlexGridSizer* bottom_table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
199                 bottom_table->AddGrowableCol (1, 1);
200
201                 add_label_to_sizer (bottom_table, _panel, _("Issuer"), true);
202                 _issuer = new wxTextCtrl (_panel, wxID_ANY);
203                 bottom_table->Add (_issuer, 1, wxALL | wxEXPAND);
204
205                 add_label_to_sizer (bottom_table, _panel, _("Creator"), true);
206                 _creator = new wxTextCtrl (_panel, wxID_ANY);
207                 bottom_table->Add (_creator, 1, wxALL | wxEXPAND);
208
209                 table->Add (bottom_table, wxGBPosition (r, 0), wxGBSpan (2, 2), wxEXPAND);
210                 ++r;
211
212                 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
213                 _language->Bind     (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&GeneralPage::language_changed,     this));
214
215                 _num_local_encoding_threads->SetRange (1, 128);
216                 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
217
218                 _automatic_audio_analysis->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this));
219                 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
220                 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
221
222                 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::issuer_changed, this));
223                 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::creator_changed, this));
224         }
225
226         void config_changed ()
227         {
228                 Config* config = Config::instance ();
229
230                 checked_set (_set_language, config->language ());
231
232                 if (config->language().get_value_or ("") == "fr") {
233                         checked_set (_language, 3);
234                 } else if (config->language().get_value_or ("") == "it") {
235                         checked_set (_language, 4);
236                 } else if (config->language().get_value_or ("") == "es") {
237                         checked_set (_language, 2);
238                 } else if (config->language().get_value_or ("") == "sv") {
239                         checked_set (_language, 6);
240                 } else if (config->language().get_value_or ("") == "de") {
241                         checked_set (_language, 0);
242                 } else if (config->language().get_value_or ("") == "nl") {
243                         checked_set (_language, 5);
244                 } else if (config->language().get_value_or ("") == "ru") {
245                         checked_set (_language, 7);
246                 } else if (config->language().get_value_or ("") == "pl") {
247                         checked_set (_language, 8);
248                 } else if (config->language().get_value_or ("") == "da") {
249                         checked_set (_language, 9);
250                 } else {
251                         _language->SetSelection (1);
252                 }
253
254                 setup_language_sensitivity ();
255
256                 checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ());
257                 checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ());
258                 checked_set (_check_for_updates, config->check_for_updates ());
259                 checked_set (_check_for_test_updates, config->check_for_test_updates ());
260                 checked_set (_issuer, config->dcp_issuer ());
261                 checked_set (_creator, config->dcp_creator ());
262         }
263
264         void setup_language_sensitivity ()
265         {
266                 _language->Enable (_set_language->GetValue ());
267         }
268
269         void set_language_changed ()
270         {
271                 setup_language_sensitivity ();
272                 if (_set_language->GetValue ()) {
273                         language_changed ();
274                 } else {
275                         Config::instance()->unset_language ();
276                 }
277         }
278
279         void language_changed ()
280         {
281                 switch (_language->GetSelection ()) {
282                 case 0:
283                         Config::instance()->set_language ("de");
284                         break;
285                 case 1:
286                         Config::instance()->set_language ("en");
287                         break;
288                 case 2:
289                         Config::instance()->set_language ("es");
290                         break;
291                 case 3:
292                         Config::instance()->set_language ("fr");
293                         break;
294                 case 4:
295                         Config::instance()->set_language ("it");
296                         break;
297                 case 5:
298                         Config::instance()->set_language ("nl");
299                         break;
300                 case 6:
301                         Config::instance()->set_language ("sv");
302                         break;
303                 case 7:
304                         Config::instance()->set_language ("ru");
305                         break;
306                 case 8:
307                         Config::instance()->set_language ("pl");
308                         break;
309                 case 9:
310                         Config::instance()->set_language ("da");
311                         break;
312                 }
313         }
314
315         void automatic_audio_analysis_changed ()
316         {
317                 Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ());
318         }
319
320         void check_for_updates_changed ()
321         {
322                 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
323         }
324
325         void check_for_test_updates_changed ()
326         {
327                 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
328         }
329
330         void num_local_encoding_threads_changed ()
331         {
332                 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
333         }
334
335         void issuer_changed ()
336         {
337                 Config::instance()->set_dcp_issuer (wx_to_std (_issuer->GetValue ()));
338         }
339
340         void creator_changed ()
341         {
342                 Config::instance()->set_dcp_creator (wx_to_std (_creator->GetValue ()));
343         }
344
345         wxCheckBox* _set_language;
346         wxChoice* _language;
347         wxSpinCtrl* _num_local_encoding_threads;
348         wxCheckBox* _automatic_audio_analysis;
349         wxCheckBox* _check_for_updates;
350         wxCheckBox* _check_for_test_updates;
351         wxTextCtrl* _issuer;
352         wxTextCtrl* _creator;
353 };
354
355 class DefaultsPage : public StandardPage
356 {
357 public:
358         DefaultsPage (wxSize panel_size, int border)
359                 : StandardPage (panel_size, border)
360         {}
361
362         wxString GetName () const
363         {
364                 return _("Defaults");
365         }
366
367 #ifdef DCPOMATIC_OSX
368         wxBitmap GetLargeIcon () const
369         {
370                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
371         }
372 #endif
373
374 private:
375         void setup ()
376         {
377                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
378                 table->AddGrowableCol (1, 1);
379                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
380
381                 {
382                         add_label_to_sizer (table, _panel, _("Default duration of still images"), true);
383                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
384                         _still_length = new wxSpinCtrl (_panel);
385                         s->Add (_still_length);
386                         add_label_to_sizer (s, _panel, _("s"), false);
387                         table->Add (s, 1);
388                 }
389
390                 add_label_to_sizer (table, _panel, _("Default directory for new films"), true);
391 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
392                 _directory = new DirPickerCtrl (_panel);
393 #else
394                 _directory = new wxDirPickerCtrl (_panel, wxDD_DIR_MUST_EXIST);
395 #endif
396                 table->Add (_directory, 1, wxEXPAND);
397
398                 add_label_to_sizer (table, _panel, _("Default ISDCF name details"), true);
399                 _isdcf_metadata_button = new wxButton (_panel, wxID_ANY, _("Edit..."));
400                 table->Add (_isdcf_metadata_button);
401
402                 add_label_to_sizer (table, _panel, _("Default container"), true);
403                 _container = new wxChoice (_panel, wxID_ANY);
404                 table->Add (_container);
405
406                 add_label_to_sizer (table, _panel, _("Default content type"), true);
407                 _dcp_content_type = new wxChoice (_panel, wxID_ANY);
408                 table->Add (_dcp_content_type);
409
410                 {
411                         add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true);
412                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
413                         _j2k_bandwidth = new wxSpinCtrl (_panel);
414                         s->Add (_j2k_bandwidth);
415                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
416                         table->Add (s, 1);
417                 }
418
419                 {
420                         add_label_to_sizer (table, _panel, _("Default audio delay"), true);
421                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
422                         _audio_delay = new wxSpinCtrl (_panel);
423                         s->Add (_audio_delay);
424                         add_label_to_sizer (s, _panel, _("ms"), false);
425                         table->Add (s, 1);
426                 }
427
428                 _still_length->SetRange (1, 3600);
429                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
430
431                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
432
433                 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
434
435                 vector<Ratio const *> ratios = Ratio::all ();
436                 for (size_t i = 0; i < ratios.size(); ++i) {
437                         _container->Append (std_to_wx (ratios[i]->nickname ()));
438                 }
439
440                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
441
442                 vector<DCPContentType const *> const ct = DCPContentType::all ();
443                 for (size_t i = 0; i < ct.size(); ++i) {
444                         _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
445                 }
446
447                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
448
449                 _j2k_bandwidth->SetRange (50, 250);
450                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
451
452                 _audio_delay->SetRange (-1000, 1000);
453                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
454         }
455
456         void config_changed ()
457         {
458                 Config* config = Config::instance ();
459
460                 vector<Ratio const *> ratios = Ratio::all ();
461                 for (size_t i = 0; i < ratios.size(); ++i) {
462                         if (ratios[i] == config->default_container ()) {
463                                 _container->SetSelection (i);
464                         }
465                 }
466
467                 vector<DCPContentType const *> const ct = DCPContentType::all ();
468                 for (size_t i = 0; i < ct.size(); ++i) {
469                         if (ct[i] == config->default_dcp_content_type ()) {
470                                 _dcp_content_type->SetSelection (i);
471                         }
472                 }
473
474                 checked_set (_still_length, config->default_still_length ());
475                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
476                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
477                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
478                 checked_set (_audio_delay, config->default_audio_delay ());
479         }
480
481         void j2k_bandwidth_changed ()
482         {
483                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
484         }
485
486         void audio_delay_changed ()
487         {
488                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
489         }
490
491         void directory_changed ()
492         {
493                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
494         }
495
496         void edit_isdcf_metadata_clicked ()
497         {
498                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
499                 d->ShowModal ();
500                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
501                 d->Destroy ();
502         }
503
504         void still_length_changed ()
505         {
506                 Config::instance()->set_default_still_length (_still_length->GetValue ());
507         }
508
509         void container_changed ()
510         {
511                 vector<Ratio const *> ratio = Ratio::all ();
512                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
513         }
514
515         void dcp_content_type_changed ()
516         {
517                 vector<DCPContentType const *> ct = DCPContentType::all ();
518                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
519         }
520
521         wxSpinCtrl* _j2k_bandwidth;
522         wxSpinCtrl* _audio_delay;
523         wxButton* _isdcf_metadata_button;
524         wxSpinCtrl* _still_length;
525 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
526         DirPickerCtrl* _directory;
527 #else
528         wxDirPickerCtrl* _directory;
529 #endif
530         wxChoice* _container;
531         wxChoice* _dcp_content_type;
532 };
533
534 class EncodingServersPage : public StandardPage
535 {
536 public:
537         EncodingServersPage (wxSize panel_size, int border)
538                 : StandardPage (panel_size, border)
539         {}
540
541         wxString GetName () const
542         {
543                 return _("Servers");
544         }
545
546 #ifdef DCPOMATIC_OSX
547         wxBitmap GetLargeIcon () const
548         {
549                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
550         }
551 #endif
552
553 private:
554         void setup ()
555         {
556                 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
557                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
558
559                 vector<string> columns;
560                 columns.push_back (wx_to_std (_("IP address / host name")));
561                 _servers_list = new EditableList<string, ServerDialog> (
562                         _panel,
563                         columns,
564                         boost::bind (&Config::servers, Config::instance()),
565                         boost::bind (&Config::set_servers, Config::instance(), _1),
566                         boost::bind (&EncodingServersPage::server_column, this, _1)
567                         );
568
569                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
570
571                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
572         }
573
574         void config_changed ()
575         {
576                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
577                 _servers_list->refresh ();
578         }
579
580         void use_any_servers_changed ()
581         {
582                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
583         }
584
585         string server_column (string s)
586         {
587                 return s;
588         }
589
590         wxCheckBox* _use_any_servers;
591         EditableList<string, ServerDialog>* _servers_list;
592 };
593
594 class CertificateChainEditor : public wxPanel
595 {
596 public:
597         CertificateChainEditor (
598                 wxWindow* parent,
599                 wxString title,
600                 int border,
601                 function<void (shared_ptr<dcp::CertificateChain>)> set,
602                 function<shared_ptr<const dcp::CertificateChain> (void)> get
603                 )
604                 : wxPanel (parent)
605                 , _set (set)
606                 , _get (get)
607         {
608                 wxFont subheading_font (*wxNORMAL_FONT);
609                 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
610
611                 _sizer = new wxBoxSizer (wxVERTICAL);
612
613                 {
614                         wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
615                         m->SetFont (subheading_font);
616                         _sizer->Add (m, 0, wxALL, border);
617                 }
618
619                 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
620                 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
621
622                 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
623
624                 {
625                         wxListItem ip;
626                         ip.SetId (0);
627                         ip.SetText (_("Type"));
628                         ip.SetWidth (100);
629                         _certificates->InsertColumn (0, ip);
630                 }
631
632                 {
633                         wxListItem ip;
634                         ip.SetId (1);
635                         ip.SetText (_("Thumbprint"));
636                         ip.SetWidth (300);
637
638                         wxFont font = ip.GetFont ();
639                         font.SetFamily (wxFONTFAMILY_TELETYPE);
640                         ip.SetFont (font);
641
642                         _certificates->InsertColumn (1, ip);
643                 }
644
645                 certificates_sizer->Add (_certificates, 1, wxEXPAND);
646
647                 {
648                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
649                         _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
650                         s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
651                         _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
652                         s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
653                         certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
654                 }
655
656                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
657                 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
658                 int r = 0;
659
660                 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
661                 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
662                 wxFont font = _private_key->GetFont ();
663                 font.SetFamily (wxFONTFAMILY_TELETYPE);
664                 _private_key->SetFont (font);
665                 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
666                 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
667                 table->Add (_load_private_key, wxGBPosition (r, 2));
668                 ++r;
669
670                 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
671                 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
672                 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
673                 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
674                 ++r;
675
676                 _add_certificate->Bind     (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::add_certificate, this));
677                 _remove_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remove_certificate, this));
678                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
679                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
680                 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remake_certificates, this));
681                 _load_private_key->Bind    (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::load_private_key, this));
682
683                 SetSizerAndFit (_sizer);
684         }
685
686         void config_changed ()
687         {
688                 _chain.reset (new dcp::CertificateChain (*_get().get ()));
689
690                 update_certificate_list ();
691                 update_private_key ();
692                 update_sensitivity ();
693         }
694
695         void add_button (wxWindow* button)
696         {
697                 _button_sizer->Add (button);
698                 _sizer->Layout ();
699         }
700
701 private:
702         void add_certificate ()
703         {
704                 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
705
706                 if (d->ShowModal() == wxID_OK) {
707                         try {
708                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
709                                 _chain->add (c);
710                                 _set (_chain);
711                                 update_certificate_list ();
712                         } catch (dcp::MiscError& e) {
713                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
714                         }
715                 }
716
717                 d->Destroy ();
718
719                 update_sensitivity ();
720         }
721
722         void remove_certificate ()
723         {
724                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
725                 if (i == -1) {
726                         return;
727                 }
728
729                 _certificates->DeleteItem (i);
730                 _chain->remove (i);
731                 _set (_chain);
732
733                 update_sensitivity ();
734         }
735
736         void update_certificate_list ()
737         {
738                 _certificates->DeleteAllItems ();
739                 size_t n = 0;
740                 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
741                 BOOST_FOREACH (dcp::Certificate const & i, certs) {
742                         wxListItem item;
743                         item.SetId (n);
744                         _certificates->InsertItem (item);
745                         _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
746
747                         if (n == 0) {
748                                 _certificates->SetItem (n, 0, _("Root"));
749                         } else if (n == (certs.size() - 1)) {
750                                 _certificates->SetItem (n, 0, _("Leaf"));
751                         } else {
752                                 _certificates->SetItem (n, 0, _("Intermediate"));
753                         }
754
755                         ++n;
756                 }
757         }
758
759         void remake_certificates ()
760         {
761                 shared_ptr<const dcp::CertificateChain> chain = _get ();
762
763                 string intermediate_common_name;
764                 if (chain->root_to_leaf().size() >= 3) {
765                         dcp::CertificateChain::List all = chain->root_to_leaf ();
766                         dcp::CertificateChain::List::iterator i = all.begin ();
767                         ++i;
768                         intermediate_common_name = i->subject_common_name ();
769                 }
770
771                 MakeChainDialog* d = new MakeChainDialog (
772                         this,
773                         chain->root().subject_organization_name (),
774                         chain->root().subject_organizational_unit_name (),
775                         chain->root().subject_common_name (),
776                         intermediate_common_name,
777                         chain->leaf().subject_common_name ()
778                         );
779
780                 if (d->ShowModal () == wxID_OK) {
781                         _chain.reset (
782                                 new dcp::CertificateChain (
783                                         openssl_path (),
784                                         d->organisation (),
785                                         d->organisational_unit (),
786                                         d->root_common_name (),
787                                         d->intermediate_common_name (),
788                                         d->leaf_common_name ()
789                                         )
790                                 );
791
792                         _set (_chain);
793                         update_certificate_list ();
794                         update_private_key ();
795                 }
796
797                 d->Destroy ();
798         }
799
800         void update_sensitivity ()
801         {
802                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
803         }
804
805         void update_private_key ()
806         {
807                 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
808                 _sizer->Layout ();
809         }
810
811         void load_private_key ()
812         {
813                 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
814
815                 if (d->ShowModal() == wxID_OK) {
816                         try {
817                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
818                                 if (boost::filesystem::file_size (p) > 1024) {
819                                         error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
820                                         return;
821                                 }
822
823                                 _chain->set_key (dcp::file_to_string (p));
824                                 _set (_chain);
825                                 update_private_key ();
826                         } catch (dcp::MiscError& e) {
827                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
828                         }
829                 }
830
831                 d->Destroy ();
832
833                 update_sensitivity ();
834         }
835
836         wxListCtrl* _certificates;
837         wxButton* _add_certificate;
838         wxButton* _remove_certificate;
839         wxButton* _remake_certificates;
840         wxStaticText* _private_key;
841         wxButton* _load_private_key;
842         wxSizer* _sizer;
843         wxBoxSizer* _button_sizer;
844         shared_ptr<dcp::CertificateChain> _chain;
845         boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
846         boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
847 };
848
849 class KeysPage : public StandardPage
850 {
851 public:
852         KeysPage (wxSize panel_size, int border)
853                 : StandardPage (panel_size, border)
854         {}
855
856         wxString GetName () const
857         {
858                 return _("Keys");
859         }
860
861 #ifdef DCPOMATIC_OSX
862         wxBitmap GetLargeIcon () const
863         {
864                 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
865         }
866 #endif
867
868 private:
869
870         void setup ()
871         {
872                 _signer = new CertificateChainEditor (
873                         _panel, _("Signing DCPs and KDMs"), _border,
874                         boost::bind (&Config::set_signer_chain, Config::instance (), _1),
875                         boost::bind (&Config::signer_chain, Config::instance ())
876                         );
877
878                 _panel->GetSizer()->Add (_signer);
879
880                 _decryption = new CertificateChainEditor (
881                         _panel, _("Decrypting DCPs"), _border,
882                         boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
883                         boost::bind (&Config::decryption_chain, Config::instance ())
884                         );
885
886                 _panel->GetSizer()->Add (_decryption);
887
888                 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
889                 _decryption->add_button (_export_decryption_certificate);
890
891                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
892         }
893
894         void export_decryption_certificate ()
895         {
896                 wxFileDialog* d = new wxFileDialog (
897                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
898                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
899                         );
900
901                 if (d->ShowModal () == wxID_OK) {
902                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
903                         if (!f) {
904                                 throw OpenFileError (wx_to_std (d->GetPath ()));
905                         }
906
907                         string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
908                         fwrite (s.c_str(), 1, s.length(), f);
909                         fclose (f);
910                 }
911                 d->Destroy ();
912         }
913
914         void config_changed ()
915         {
916                 _signer->config_changed ();
917                 _decryption->config_changed ();
918         }
919
920         CertificateChainEditor* _signer;
921         CertificateChainEditor* _decryption;
922         wxButton* _export_decryption_certificate;
923 };
924
925 class TMSPage : public StandardPage
926 {
927 public:
928         TMSPage (wxSize panel_size, int border)
929                 : StandardPage (panel_size, border)
930         {}
931
932         wxString GetName () const
933         {
934                 return _("TMS");
935         }
936
937 #ifdef DCPOMATIC_OSX
938         wxBitmap GetLargeIcon () const
939         {
940                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
941         }
942 #endif
943
944 private:
945         void setup ()
946         {
947                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
948                 table->AddGrowableCol (1, 1);
949                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
950
951                 add_label_to_sizer (table, _panel, _("Protocol"), true);
952                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
953                 table->Add (_tms_protocol, 1, wxEXPAND);
954
955                 add_label_to_sizer (table, _panel, _("IP address"), true);
956                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
957                 table->Add (_tms_ip, 1, wxEXPAND);
958
959                 add_label_to_sizer (table, _panel, _("Target path"), true);
960                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
961                 table->Add (_tms_path, 1, wxEXPAND);
962
963                 add_label_to_sizer (table, _panel, _("User name"), true);
964                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
965                 table->Add (_tms_user, 1, wxEXPAND);
966
967                 add_label_to_sizer (table, _panel, _("Password"), true);
968                 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
969                 table->Add (_tms_password, 1, wxEXPAND);
970
971                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
972                 _tms_protocol->Append (_("FTP (for Dolby)"));
973
974                 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
975                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
976                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
977                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
978                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
979         }
980
981         void config_changed ()
982         {
983                 Config* config = Config::instance ();
984
985                 checked_set (_tms_protocol, config->tms_protocol ());
986                 checked_set (_tms_ip, config->tms_ip ());
987                 checked_set (_tms_path, config->tms_path ());
988                 checked_set (_tms_user, config->tms_user ());
989                 checked_set (_tms_password, config->tms_password ());
990         }
991
992         void tms_protocol_changed ()
993         {
994                 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
995         }
996
997         void tms_ip_changed ()
998         {
999                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1000         }
1001
1002         void tms_path_changed ()
1003         {
1004                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1005         }
1006
1007         void tms_user_changed ()
1008         {
1009                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1010         }
1011
1012         void tms_password_changed ()
1013         {
1014                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1015         }
1016
1017         wxChoice* _tms_protocol;
1018         wxTextCtrl* _tms_ip;
1019         wxTextCtrl* _tms_path;
1020         wxTextCtrl* _tms_user;
1021         wxTextCtrl* _tms_password;
1022 };
1023
1024 class KDMEmailPage : public StandardPage
1025 {
1026 public:
1027
1028         KDMEmailPage (wxSize panel_size, int border)
1029 #ifdef DCPOMATIC_OSX
1030                 /* We have to force both width and height of this one */
1031                 : StandardPage (wxSize (480, 128), border)
1032 #else
1033                  : StandardPage (panel_size, border)
1034 #endif
1035         {}
1036
1037         wxString GetName () const
1038         {
1039                 return _("KDM Email");
1040         }
1041
1042 #ifdef DCPOMATIC_OSX
1043         wxBitmap GetLargeIcon () const
1044         {
1045                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1046         }
1047 #endif
1048
1049 private:
1050         void setup ()
1051         {
1052                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1053                 table->AddGrowableCol (1, 1);
1054                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1055
1056                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1057                 {
1058                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1059                         _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1060                         s->Add (_mail_server, 1, wxEXPAND | wxALL);
1061                         add_label_to_sizer (s, _panel, _("port"), false);
1062                         _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1063                         _mail_port->SetRange (0, 65535);
1064                         s->Add (_mail_port);
1065                         table->Add (s, 1, wxEXPAND | wxALL);
1066                 }
1067
1068                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1069                 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1070                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1071
1072                 add_label_to_sizer (table, _panel, _("Mail password"), true);
1073                 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1074                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1075
1076                 add_label_to_sizer (table, _panel, _("Subject"), true);
1077                 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1078                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1079
1080                 add_label_to_sizer (table, _panel, _("From address"), true);
1081                 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1082                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1083
1084                 add_label_to_sizer (table, _panel, _("CC address"), true);
1085                 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1086                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1087
1088                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1089                 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1090                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1091
1092                 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1093                 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1094
1095                 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1096                 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1097
1098                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1099                 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1100                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1101                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1102                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1103                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1104                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1105                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1106                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1107                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1108         }
1109
1110         void config_changed ()
1111         {
1112                 Config* config = Config::instance ();
1113
1114                 checked_set (_mail_server, config->mail_server ());
1115                 checked_set (_mail_port, config->mail_port ());
1116                 checked_set (_mail_user, config->mail_user ());
1117                 checked_set (_mail_password, config->mail_password ());
1118                 checked_set (_kdm_subject, config->kdm_subject ());
1119                 checked_set (_kdm_from, config->kdm_from ());
1120                 checked_set (_kdm_cc, config->kdm_cc ());
1121                 checked_set (_kdm_bcc, config->kdm_bcc ());
1122                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1123         }
1124
1125         void mail_server_changed ()
1126         {
1127                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1128         }
1129
1130         void mail_port_changed ()
1131         {
1132                 Config::instance()->set_mail_port (_mail_port->GetValue ());
1133         }
1134
1135         void mail_user_changed ()
1136         {
1137                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1138         }
1139
1140         void mail_password_changed ()
1141         {
1142                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1143         }
1144
1145         void kdm_subject_changed ()
1146         {
1147                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1148         }
1149
1150         void kdm_from_changed ()
1151         {
1152                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1153         }
1154
1155         void kdm_cc_changed ()
1156         {
1157                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1158         }
1159
1160         void kdm_bcc_changed ()
1161         {
1162                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1163         }
1164
1165         void kdm_email_changed ()
1166         {
1167                 if (_kdm_email->GetValue().IsEmpty ()) {
1168                         /* Sometimes we get sent an erroneous notification that the email
1169                            is empty; I don't know why.
1170                         */
1171                         return;
1172                 }
1173                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1174         }
1175
1176         void reset_kdm_email ()
1177         {
1178                 Config::instance()->reset_kdm_email ();
1179                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1180         }
1181
1182         wxTextCtrl* _mail_server;
1183         wxSpinCtrl* _mail_port;
1184         wxTextCtrl* _mail_user;
1185         wxTextCtrl* _mail_password;
1186         wxTextCtrl* _kdm_subject;
1187         wxTextCtrl* _kdm_from;
1188         wxTextCtrl* _kdm_cc;
1189         wxTextCtrl* _kdm_bcc;
1190         wxTextCtrl* _kdm_email;
1191         wxButton* _reset_kdm_email;
1192 };
1193
1194 /** @class AdvancedPage
1195  *  @brief Advanced page of the preferences dialog.
1196  */
1197 class AdvancedPage : public StockPage
1198 {
1199 public:
1200         AdvancedPage (wxSize panel_size, int border)
1201                 : StockPage (Kind_Advanced, panel_size, border)
1202                 , _maximum_j2k_bandwidth (0)
1203                 , _allow_any_dcp_frame_rate (0)
1204                 , _only_servers_encode (0)
1205                 , _log_general (0)
1206                 , _log_warning (0)
1207                 , _log_error (0)
1208                 , _log_timing (0)
1209                 , _log_debug_decode (0)
1210                 , _log_debug_encode (0)
1211         {}
1212
1213 private:
1214         void setup ()
1215         {
1216                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1217                 table->AddGrowableCol (1, 1);
1218                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1219
1220                 {
1221                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1222                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1223                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1224                         s->Add (_maximum_j2k_bandwidth, 1);
1225                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1226                         table->Add (s, 1);
1227                 }
1228
1229                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1230                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1231                 table->AddSpacer (0);
1232
1233                 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1234                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1235                 table->AddSpacer (0);
1236
1237 #ifdef __WXOSX__
1238                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1239                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1240 #else
1241                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1242                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1243 #endif
1244
1245                 {
1246                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1247                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1248                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1249                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1250                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1251                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1252                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1253                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1254                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1255                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1256                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1257                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1258                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1259                         table->Add (t, 0, wxALL, 6);
1260                 }
1261
1262 #ifdef DCPOMATIC_WINDOWS
1263                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1264                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1265                 table->AddSpacer (0);
1266 #endif
1267
1268                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1269                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1270                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1271                 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1272                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1273                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1274                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1275                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1276                 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1277                 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1278 #ifdef DCPOMATIC_WINDOWS
1279                 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1280 #endif
1281         }
1282
1283         void config_changed ()
1284         {
1285                 Config* config = Config::instance ();
1286
1287                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1288                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1289                 checked_set (_only_servers_encode, config->only_servers_encode ());
1290                 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1291                 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1292                 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1293                 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1294                 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1295                 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1296 #ifdef DCPOMATIC_WINDOWS
1297                 checked_set (_win32_console, config->win32_console());
1298 #endif
1299         }
1300
1301         void maximum_j2k_bandwidth_changed ()
1302         {
1303                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1304         }
1305
1306         void allow_any_dcp_frame_rate_changed ()
1307         {
1308                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1309         }
1310
1311         void only_servers_encode_changed ()
1312         {
1313                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1314         }
1315
1316         void log_changed ()
1317         {
1318                 int types = 0;
1319                 if (_log_general->GetValue ()) {
1320                         types |= Log::TYPE_GENERAL;
1321                 }
1322                 if (_log_warning->GetValue ()) {
1323                         types |= Log::TYPE_WARNING;
1324                 }
1325                 if (_log_error->GetValue ())  {
1326                         types |= Log::TYPE_ERROR;
1327                 }
1328                 if (_log_timing->GetValue ()) {
1329                         types |= Log::TYPE_TIMING;
1330                 }
1331                 if (_log_debug_decode->GetValue ()) {
1332                         types |= Log::TYPE_DEBUG_DECODE;
1333                 }
1334                 if (_log_debug_encode->GetValue ()) {
1335                         types |= Log::TYPE_DEBUG_ENCODE;
1336                 }
1337                 Config::instance()->set_log_types (types);
1338         }
1339
1340 #ifdef DCPOMATIC_WINDOWS
1341         void win32_console_changed ()
1342         {
1343                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1344         }
1345 #endif
1346
1347         wxSpinCtrl* _maximum_j2k_bandwidth;
1348         wxCheckBox* _allow_any_dcp_frame_rate;
1349         wxCheckBox* _only_servers_encode;
1350         wxCheckBox* _log_general;
1351         wxCheckBox* _log_warning;
1352         wxCheckBox* _log_error;
1353         wxCheckBox* _log_timing;
1354         wxCheckBox* _log_debug_decode;
1355         wxCheckBox* _log_debug_encode;
1356 #ifdef DCPOMATIC_WINDOWS
1357         wxCheckBox* _win32_console;
1358 #endif
1359 };
1360
1361 wxPreferencesEditor*
1362 create_config_dialog ()
1363 {
1364         wxPreferencesEditor* e = new wxPreferencesEditor ();
1365
1366 #ifdef DCPOMATIC_OSX
1367         /* Width that we force some of the config panels to be on OSX so that
1368            the containing window doesn't shrink too much when we select those panels.
1369            This is obviously an unpleasant hack.
1370         */
1371         wxSize ps = wxSize (520, -1);
1372         int const border = 16;
1373 #else
1374         wxSize ps = wxSize (-1, -1);
1375         int const border = 8;
1376 #endif
1377
1378         e->AddPage (new GeneralPage (ps, border));
1379         e->AddPage (new DefaultsPage (ps, border));
1380         e->AddPage (new EncodingServersPage (ps, border));
1381         e->AddPage (new KeysPage (ps, border));
1382         e->AddPage (new TMSPage (ps, border));
1383         e->AddPage (new KDMEmailPage (ps, border));
1384         e->AddPage (new AdvancedPage (ps, border));
1385         return e;
1386 }