722fa59ce2fa4987378de2cc0976cddc85c11a4b
[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, static_cast<bool>(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_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                 add_label_to_sizer (table, _panel, _("Default standard"), true);
429                 _standard = new wxChoice (_panel, wxID_ANY);
430                 table->Add (_standard);
431
432                 _still_length->SetRange (1, 3600);
433                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
434
435                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
436
437                 _isdcf_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_isdcf_metadata_clicked, this));
438
439                 vector<Ratio const *> ratios = Ratio::all ();
440                 for (size_t i = 0; i < ratios.size(); ++i) {
441                         _container->Append (std_to_wx (ratios[i]->nickname ()));
442                 }
443
444                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
445
446                 vector<DCPContentType const *> const ct = DCPContentType::all ();
447                 for (size_t i = 0; i < ct.size(); ++i) {
448                         _dcp_content_type->Append (std_to_wx (ct[i]->pretty_name ()));
449                 }
450
451                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
452
453                 _j2k_bandwidth->SetRange (50, 250);
454                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
455
456                 _audio_delay->SetRange (-1000, 1000);
457                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
458
459                 _standard->Append (_("SMPTE"));
460                 _standard->Append (_("Interop"));
461                 _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::standard_changed, this));
462         }
463
464         void config_changed ()
465         {
466                 Config* config = Config::instance ();
467
468                 vector<Ratio const *> ratios = Ratio::all ();
469                 for (size_t i = 0; i < ratios.size(); ++i) {
470                         if (ratios[i] == config->default_container ()) {
471                                 _container->SetSelection (i);
472                         }
473                 }
474
475                 vector<DCPContentType const *> const ct = DCPContentType::all ();
476                 for (size_t i = 0; i < ct.size(); ++i) {
477                         if (ct[i] == config->default_dcp_content_type ()) {
478                                 _dcp_content_type->SetSelection (i);
479                         }
480                 }
481
482                 checked_set (_still_length, config->default_still_length ());
483                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
484                 checked_set (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
485                 _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 1000000);
486                 checked_set (_audio_delay, config->default_audio_delay ());
487                 checked_set (_standard, config->default_interop() ? 1 : 0);
488         }
489
490         void j2k_bandwidth_changed ()
491         {
492                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
493         }
494
495         void audio_delay_changed ()
496         {
497                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
498         }
499
500         void directory_changed ()
501         {
502                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
503         }
504
505         void edit_isdcf_metadata_clicked ()
506         {
507                 ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, Config::instance()->default_isdcf_metadata (), false);
508                 d->ShowModal ();
509                 Config::instance()->set_default_isdcf_metadata (d->isdcf_metadata ());
510                 d->Destroy ();
511         }
512
513         void still_length_changed ()
514         {
515                 Config::instance()->set_default_still_length (_still_length->GetValue ());
516         }
517
518         void container_changed ()
519         {
520                 vector<Ratio const *> ratio = Ratio::all ();
521                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
522         }
523
524         void dcp_content_type_changed ()
525         {
526                 vector<DCPContentType const *> ct = DCPContentType::all ();
527                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
528         }
529
530         void standard_changed ()
531         {
532                 Config::instance()->set_default_interop (_standard->GetSelection() == 1);
533         }
534
535         wxSpinCtrl* _j2k_bandwidth;
536         wxSpinCtrl* _audio_delay;
537         wxButton* _isdcf_metadata_button;
538         wxSpinCtrl* _still_length;
539 #ifdef DCPOMATIC_USE_OWN_PICKER
540         DirPickerCtrl* _directory;
541 #else
542         wxDirPickerCtrl* _directory;
543 #endif
544         wxChoice* _container;
545         wxChoice* _dcp_content_type;
546         wxChoice* _standard;
547 };
548
549 class EncodingServersPage : public StandardPage
550 {
551 public:
552         EncodingServersPage (wxSize panel_size, int border)
553                 : StandardPage (panel_size, border)
554         {}
555
556         wxString GetName () const
557         {
558                 return _("Servers");
559         }
560
561 #ifdef DCPOMATIC_OSX
562         wxBitmap GetLargeIcon () const
563         {
564                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
565         }
566 #endif
567
568 private:
569         void setup ()
570         {
571                 _use_any_servers = new wxCheckBox (_panel, wxID_ANY, _("Use all servers"));
572                 _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border);
573
574                 vector<string> columns;
575                 columns.push_back (wx_to_std (_("IP address / host name")));
576                 _servers_list = new EditableList<string, ServerDialog> (
577                         _panel,
578                         columns,
579                         boost::bind (&Config::servers, Config::instance()),
580                         boost::bind (&Config::set_servers, Config::instance(), _1),
581                         boost::bind (&EncodingServersPage::server_column, this, _1)
582                         );
583
584                 _panel->GetSizer()->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
585
586                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
587         }
588
589         void config_changed ()
590         {
591                 checked_set (_use_any_servers, Config::instance()->use_any_servers ());
592                 _servers_list->refresh ();
593         }
594
595         void use_any_servers_changed ()
596         {
597                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
598         }
599
600         string server_column (string s)
601         {
602                 return s;
603         }
604
605         wxCheckBox* _use_any_servers;
606         EditableList<string, ServerDialog>* _servers_list;
607 };
608
609 class CertificateChainEditor : public wxPanel
610 {
611 public:
612         CertificateChainEditor (
613                 wxWindow* parent,
614                 wxString title,
615                 int border,
616                 function<void (shared_ptr<dcp::CertificateChain>)> set,
617                 function<shared_ptr<const dcp::CertificateChain> (void)> get
618                 )
619                 : wxPanel (parent)
620                 , _set (set)
621                 , _get (get)
622         {
623                 wxFont subheading_font (*wxNORMAL_FONT);
624                 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
625
626                 _sizer = new wxBoxSizer (wxVERTICAL);
627
628                 {
629                         wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
630                         m->SetFont (subheading_font);
631                         _sizer->Add (m, 0, wxALL, border);
632                 }
633
634                 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
635                 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
636
637                 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
638
639                 {
640                         wxListItem ip;
641                         ip.SetId (0);
642                         ip.SetText (_("Type"));
643                         ip.SetWidth (100);
644                         _certificates->InsertColumn (0, ip);
645                 }
646
647                 {
648                         wxListItem ip;
649                         ip.SetId (1);
650                         ip.SetText (_("Thumbprint"));
651                         ip.SetWidth (300);
652
653                         wxFont font = ip.GetFont ();
654                         font.SetFamily (wxFONTFAMILY_TELETYPE);
655                         ip.SetFont (font);
656
657                         _certificates->InsertColumn (1, ip);
658                 }
659
660                 certificates_sizer->Add (_certificates, 1, wxEXPAND);
661
662                 {
663                         wxSizer* s = new wxBoxSizer (wxVERTICAL);
664                         _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
665                         s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
666                         _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
667                         s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
668                         _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
669                         s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
670                         certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
671                 }
672
673                 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
674                 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
675                 int r = 0;
676
677                 add_label_to_grid_bag_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
678                 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
679                 wxFont font = _private_key->GetFont ();
680                 font.SetFamily (wxFONTFAMILY_TELETYPE);
681                 _private_key->SetFont (font);
682                 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
683                 _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
684                 table->Add (_load_private_key, wxGBPosition (r, 2));
685                 ++r;
686
687                 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
688                 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
689                 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
690                 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 3));
691                 ++r;
692
693                 _add_certificate->Bind     (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::add_certificate, this));
694                 _remove_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remove_certificate, this));
695                 _export_certificate->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::export_certificate, this));
696                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
697                 _certificates->Bind        (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
698                 _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::remake_certificates, this));
699                 _load_private_key->Bind    (wxEVT_COMMAND_BUTTON_CLICKED,       boost::bind (&CertificateChainEditor::load_private_key, this));
700
701                 SetSizerAndFit (_sizer);
702         }
703
704         void config_changed ()
705         {
706                 _chain.reset (new dcp::CertificateChain (*_get().get ()));
707
708                 update_certificate_list ();
709                 update_private_key ();
710                 update_sensitivity ();
711         }
712
713         void add_button (wxWindow* button)
714         {
715                 _button_sizer->Add (button);
716                 _sizer->Layout ();
717         }
718
719 private:
720         void add_certificate ()
721         {
722                 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
723
724                 if (d->ShowModal() == wxID_OK) {
725                         try {
726                                 dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
727                                 _chain->add (c);
728                                 _set (_chain);
729                                 update_certificate_list ();
730                         } catch (dcp::MiscError& e) {
731                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
732                         }
733                 }
734
735                 d->Destroy ();
736
737                 update_sensitivity ();
738         }
739
740         void remove_certificate ()
741         {
742                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
743                 if (i == -1) {
744                         return;
745                 }
746
747                 _certificates->DeleteItem (i);
748                 _chain->remove (i);
749                 _set (_chain);
750
751                 update_sensitivity ();
752         }
753
754         void export_certificate ()
755         {
756                 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
757                 if (i == -1) {
758                         return;
759                 }
760
761                 wxFileDialog* d = new wxFileDialog (
762                         this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
763                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
764                         );
765
766                 dcp::CertificateChain::List all = _chain->root_to_leaf ();
767                 dcp::CertificateChain::List::iterator j = all.begin ();
768                 for (int k = 0; k < i; ++k) {
769                         ++j;
770                 }
771
772                 if (d->ShowModal () == wxID_OK) {
773                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
774                         if (!f) {
775                                 throw OpenFileError (wx_to_std (d->GetPath ()));
776                         }
777
778                         string const s = j->certificate (true);
779                         fwrite (s.c_str(), 1, s.length(), f);
780                         fclose (f);
781                 }
782                 d->Destroy ();
783         }
784
785         void update_certificate_list ()
786         {
787                 _certificates->DeleteAllItems ();
788                 size_t n = 0;
789                 dcp::CertificateChain::List certs = _chain->root_to_leaf ();
790                 BOOST_FOREACH (dcp::Certificate const & i, certs) {
791                         wxListItem item;
792                         item.SetId (n);
793                         _certificates->InsertItem (item);
794                         _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
795
796                         if (n == 0) {
797                                 _certificates->SetItem (n, 0, _("Root"));
798                         } else if (n == (certs.size() - 1)) {
799                                 _certificates->SetItem (n, 0, _("Leaf"));
800                         } else {
801                                 _certificates->SetItem (n, 0, _("Intermediate"));
802                         }
803
804                         ++n;
805                 }
806         }
807
808         void remake_certificates ()
809         {
810                 shared_ptr<const dcp::CertificateChain> chain = _get ();
811
812                 string subject_organization_name;
813                 string subject_organizational_unit_name;
814                 string root_common_name;
815                 string intermediate_common_name;
816                 string leaf_common_name;
817
818                 dcp::CertificateChain::List all = chain->root_to_leaf ();
819
820                 if (all.size() >= 1) {
821                         /* Have a root */
822                         subject_organization_name = chain->root().subject_organization_name ();
823                         subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
824                         root_common_name = chain->root().subject_common_name ();
825                 }
826
827                 if (all.size() >= 2) {
828                         /* Have a leaf */
829                         leaf_common_name = chain->leaf().subject_common_name ();
830                 }
831
832                 if (all.size() >= 3) {
833                         /* Have an intermediate */
834                         dcp::CertificateChain::List::iterator i = all.begin ();
835                         ++i;
836                         intermediate_common_name = i->subject_common_name ();
837                 }
838
839                 MakeChainDialog* d = new MakeChainDialog (
840                         this,
841                         subject_organization_name,
842                         subject_organizational_unit_name,
843                         root_common_name,
844                         intermediate_common_name,
845                         leaf_common_name
846                         );
847
848                 if (d->ShowModal () == wxID_OK) {
849                         _chain.reset (
850                                 new dcp::CertificateChain (
851                                         openssl_path (),
852                                         d->organisation (),
853                                         d->organisational_unit (),
854                                         d->root_common_name (),
855                                         d->intermediate_common_name (),
856                                         d->leaf_common_name ()
857                                         )
858                                 );
859
860                         _set (_chain);
861                         update_certificate_list ();
862                         update_private_key ();
863                 }
864
865                 d->Destroy ();
866         }
867
868         void update_sensitivity ()
869         {
870                 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
871                 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
872         }
873
874         void update_private_key ()
875         {
876                 checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
877                 _sizer->Layout ();
878         }
879
880         void load_private_key ()
881         {
882                 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
883
884                 if (d->ShowModal() == wxID_OK) {
885                         try {
886                                 boost::filesystem::path p (wx_to_std (d->GetPath ()));
887                                 if (boost::filesystem::file_size (p) > 1024) {
888                                         error_dialog (this, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
889                                         return;
890                                 }
891
892                                 _chain->set_key (dcp::file_to_string (p));
893                                 _set (_chain);
894                                 update_private_key ();
895                         } catch (dcp::MiscError& e) {
896                                 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
897                         }
898                 }
899
900                 d->Destroy ();
901
902                 update_sensitivity ();
903         }
904
905         wxListCtrl* _certificates;
906         wxButton* _add_certificate;
907         wxButton* _export_certificate;
908         wxButton* _remove_certificate;
909         wxButton* _remake_certificates;
910         wxStaticText* _private_key;
911         wxButton* _load_private_key;
912         wxSizer* _sizer;
913         wxBoxSizer* _button_sizer;
914         shared_ptr<dcp::CertificateChain> _chain;
915         boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
916         boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
917 };
918
919 class KeysPage : public StandardPage
920 {
921 public:
922         KeysPage (wxSize panel_size, int border)
923                 : StandardPage (panel_size, border)
924         {}
925
926         wxString GetName () const
927         {
928                 return _("Keys");
929         }
930
931 #ifdef DCPOMATIC_OSX
932         wxBitmap GetLargeIcon () const
933         {
934                 return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
935         }
936 #endif
937
938 private:
939
940         void setup ()
941         {
942                 _signer = new CertificateChainEditor (
943                         _panel, _("Signing DCPs and KDMs"), _border,
944                         boost::bind (&Config::set_signer_chain, Config::instance (), _1),
945                         boost::bind (&Config::signer_chain, Config::instance ())
946                         );
947
948                 _panel->GetSizer()->Add (_signer);
949
950                 _decryption = new CertificateChainEditor (
951                         _panel, _("Decrypting DCPs"), _border,
952                         boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
953                         boost::bind (&Config::decryption_chain, Config::instance ())
954                         );
955
956                 _panel->GetSizer()->Add (_decryption);
957
958                 _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export DCP decryption certificate..."));
959                 _decryption->add_button (_export_decryption_certificate);
960
961                 _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
962         }
963
964         void export_decryption_certificate ()
965         {
966                 wxFileDialog* d = new wxFileDialog (
967                         _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
968                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT
969                         );
970
971                 if (d->ShowModal () == wxID_OK) {
972                         FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
973                         if (!f) {
974                                 throw OpenFileError (wx_to_std (d->GetPath ()));
975                         }
976
977                         string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
978                         fwrite (s.c_str(), 1, s.length(), f);
979                         fclose (f);
980                 }
981                 d->Destroy ();
982         }
983
984         void config_changed ()
985         {
986                 _signer->config_changed ();
987                 _decryption->config_changed ();
988         }
989
990         CertificateChainEditor* _signer;
991         CertificateChainEditor* _decryption;
992         wxButton* _export_decryption_certificate;
993 };
994
995 class TMSPage : public StandardPage
996 {
997 public:
998         TMSPage (wxSize panel_size, int border)
999                 : StandardPage (panel_size, border)
1000         {}
1001
1002         wxString GetName () const
1003         {
1004                 return _("TMS");
1005         }
1006
1007 #ifdef DCPOMATIC_OSX
1008         wxBitmap GetLargeIcon () const
1009         {
1010                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
1011         }
1012 #endif
1013
1014 private:
1015         void setup ()
1016         {
1017                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1018                 table->AddGrowableCol (1, 1);
1019                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1020
1021                 add_label_to_sizer (table, _panel, _("Protocol"), true);
1022                 _tms_protocol = new wxChoice (_panel, wxID_ANY);
1023                 table->Add (_tms_protocol, 1, wxEXPAND);
1024
1025                 add_label_to_sizer (table, _panel, _("IP address"), true);
1026                 _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
1027                 table->Add (_tms_ip, 1, wxEXPAND);
1028
1029                 add_label_to_sizer (table, _panel, _("Target path"), true);
1030                 _tms_path = new wxTextCtrl (_panel, wxID_ANY);
1031                 table->Add (_tms_path, 1, wxEXPAND);
1032
1033                 add_label_to_sizer (table, _panel, _("User name"), true);
1034                 _tms_user = new wxTextCtrl (_panel, wxID_ANY);
1035                 table->Add (_tms_user, 1, wxEXPAND);
1036
1037                 add_label_to_sizer (table, _panel, _("Password"), true);
1038                 _tms_password = new wxTextCtrl (_panel, wxID_ANY);
1039                 table->Add (_tms_password, 1, wxEXPAND);
1040
1041                 _tms_protocol->Append (_("SCP (for AAM and Doremi)"));
1042                 _tms_protocol->Append (_("FTP (for Dolby)"));
1043
1044                 _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
1045                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
1046                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
1047                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
1048                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
1049         }
1050
1051         void config_changed ()
1052         {
1053                 Config* config = Config::instance ();
1054
1055                 checked_set (_tms_protocol, config->tms_protocol ());
1056                 checked_set (_tms_ip, config->tms_ip ());
1057                 checked_set (_tms_path, config->tms_path ());
1058                 checked_set (_tms_user, config->tms_user ());
1059                 checked_set (_tms_password, config->tms_password ());
1060         }
1061
1062         void tms_protocol_changed ()
1063         {
1064                 Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
1065         }
1066
1067         void tms_ip_changed ()
1068         {
1069                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
1070         }
1071
1072         void tms_path_changed ()
1073         {
1074                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
1075         }
1076
1077         void tms_user_changed ()
1078         {
1079                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
1080         }
1081
1082         void tms_password_changed ()
1083         {
1084                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
1085         }
1086
1087         wxChoice* _tms_protocol;
1088         wxTextCtrl* _tms_ip;
1089         wxTextCtrl* _tms_path;
1090         wxTextCtrl* _tms_user;
1091         wxTextCtrl* _tms_password;
1092 };
1093
1094 class KDMEmailPage : public StandardPage
1095 {
1096 public:
1097
1098         KDMEmailPage (wxSize panel_size, int border)
1099 #ifdef DCPOMATIC_OSX
1100                 /* We have to force both width and height of this one */
1101                 : StandardPage (wxSize (480, 128), border)
1102 #else
1103                  : StandardPage (panel_size, border)
1104 #endif
1105         {}
1106
1107         wxString GetName () const
1108         {
1109                 return _("KDM Email");
1110         }
1111
1112 #ifdef DCPOMATIC_OSX
1113         wxBitmap GetLargeIcon () const
1114         {
1115                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
1116         }
1117 #endif
1118
1119 private:
1120         void setup ()
1121         {
1122                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1123                 table->AddGrowableCol (1, 1);
1124                 _panel->GetSizer()->Add (table, 1, wxEXPAND | wxALL, _border);
1125
1126                 add_label_to_sizer (table, _panel, _("Outgoing mail server"), true);
1127                 {
1128                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1129                         _mail_server = new wxTextCtrl (_panel, wxID_ANY);
1130                         s->Add (_mail_server, 1, wxEXPAND | wxALL);
1131                         add_label_to_sizer (s, _panel, _("port"), false);
1132                         _mail_port = new wxSpinCtrl (_panel, wxID_ANY);
1133                         _mail_port->SetRange (0, 65535);
1134                         s->Add (_mail_port);
1135                         table->Add (s, 1, wxEXPAND | wxALL);
1136                 }
1137
1138                 add_label_to_sizer (table, _panel, _("Mail user name"), true);
1139                 _mail_user = new wxTextCtrl (_panel, wxID_ANY);
1140                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
1141
1142                 add_label_to_sizer (table, _panel, _("Mail password"), true);
1143                 _mail_password = new wxTextCtrl (_panel, wxID_ANY);
1144                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
1145
1146                 add_label_to_sizer (table, _panel, _("Subject"), true);
1147                 _kdm_subject = new wxTextCtrl (_panel, wxID_ANY);
1148                 table->Add (_kdm_subject, 1, wxEXPAND | wxALL);
1149
1150                 add_label_to_sizer (table, _panel, _("From address"), true);
1151                 _kdm_from = new wxTextCtrl (_panel, wxID_ANY);
1152                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
1153
1154                 add_label_to_sizer (table, _panel, _("CC address"), true);
1155                 _kdm_cc = new wxTextCtrl (_panel, wxID_ANY);
1156                 table->Add (_kdm_cc, 1, wxEXPAND | wxALL);
1157
1158                 add_label_to_sizer (table, _panel, _("BCC address"), true);
1159                 _kdm_bcc = new wxTextCtrl (_panel, wxID_ANY);
1160                 table->Add (_kdm_bcc, 1, wxEXPAND | wxALL);
1161
1162                 _kdm_email = new wxTextCtrl (_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
1163                 _panel->GetSizer()->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
1164
1165                 _reset_kdm_email = new wxButton (_panel, wxID_ANY, _("Reset to default text"));
1166                 _panel->GetSizer()->Add (_reset_kdm_email, 0, wxEXPAND | wxALL, _border);
1167
1168                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_server_changed, this));
1169                 _mail_port->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&KDMEmailPage::mail_port_changed, this));
1170                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_user_changed, this));
1171                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::mail_password_changed, this));
1172                 _kdm_subject->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_subject_changed, this));
1173                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_from_changed, this));
1174                 _kdm_cc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_cc_changed, this));
1175                 _kdm_bcc->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_bcc_changed, this));
1176                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
1177                 _reset_kdm_email->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMEmailPage::reset_kdm_email, this));
1178         }
1179
1180         void config_changed ()
1181         {
1182                 Config* config = Config::instance ();
1183
1184                 checked_set (_mail_server, config->mail_server ());
1185                 checked_set (_mail_port, config->mail_port ());
1186                 checked_set (_mail_user, config->mail_user ());
1187                 checked_set (_mail_password, config->mail_password ());
1188                 checked_set (_kdm_subject, config->kdm_subject ());
1189                 checked_set (_kdm_from, config->kdm_from ());
1190                 checked_set (_kdm_cc, config->kdm_cc ());
1191                 checked_set (_kdm_bcc, config->kdm_bcc ());
1192                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1193         }
1194
1195         void mail_server_changed ()
1196         {
1197                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
1198         }
1199
1200         void mail_port_changed ()
1201         {
1202                 Config::instance()->set_mail_port (_mail_port->GetValue ());
1203         }
1204
1205         void mail_user_changed ()
1206         {
1207                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
1208         }
1209
1210         void mail_password_changed ()
1211         {
1212                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
1213         }
1214
1215         void kdm_subject_changed ()
1216         {
1217                 Config::instance()->set_kdm_subject (wx_to_std (_kdm_subject->GetValue ()));
1218         }
1219
1220         void kdm_from_changed ()
1221         {
1222                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
1223         }
1224
1225         void kdm_cc_changed ()
1226         {
1227                 Config::instance()->set_kdm_cc (wx_to_std (_kdm_cc->GetValue ()));
1228         }
1229
1230         void kdm_bcc_changed ()
1231         {
1232                 Config::instance()->set_kdm_bcc (wx_to_std (_kdm_bcc->GetValue ()));
1233         }
1234
1235         void kdm_email_changed ()
1236         {
1237                 if (_kdm_email->GetValue().IsEmpty ()) {
1238                         /* Sometimes we get sent an erroneous notification that the email
1239                            is empty; I don't know why.
1240                         */
1241                         return;
1242                 }
1243                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
1244         }
1245
1246         void reset_kdm_email ()
1247         {
1248                 Config::instance()->reset_kdm_email ();
1249                 checked_set (_kdm_email, Config::instance()->kdm_email ());
1250         }
1251
1252         wxTextCtrl* _mail_server;
1253         wxSpinCtrl* _mail_port;
1254         wxTextCtrl* _mail_user;
1255         wxTextCtrl* _mail_password;
1256         wxTextCtrl* _kdm_subject;
1257         wxTextCtrl* _kdm_from;
1258         wxTextCtrl* _kdm_cc;
1259         wxTextCtrl* _kdm_bcc;
1260         wxTextCtrl* _kdm_email;
1261         wxButton* _reset_kdm_email;
1262 };
1263
1264 /** @class AdvancedPage
1265  *  @brief Advanced page of the preferences dialog.
1266  */
1267 class AdvancedPage : public StockPage
1268 {
1269 public:
1270         AdvancedPage (wxSize panel_size, int border)
1271                 : StockPage (Kind_Advanced, panel_size, border)
1272                 , _maximum_j2k_bandwidth (0)
1273                 , _allow_any_dcp_frame_rate (0)
1274                 , _only_servers_encode (0)
1275                 , _log_general (0)
1276                 , _log_warning (0)
1277                 , _log_error (0)
1278                 , _log_timing (0)
1279                 , _log_debug_decode (0)
1280                 , _log_debug_encode (0)
1281         {}
1282
1283 private:
1284         void setup ()
1285         {
1286                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1287                 table->AddGrowableCol (1, 1);
1288                 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1289
1290                 {
1291                         add_label_to_sizer (table, _panel, _("Maximum JPEG2000 bandwidth"), true);
1292                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
1293                         _maximum_j2k_bandwidth = new wxSpinCtrl (_panel);
1294                         s->Add (_maximum_j2k_bandwidth, 1);
1295                         add_label_to_sizer (s, _panel, _("Mbit/s"), false);
1296                         table->Add (s, 1);
1297                 }
1298
1299                 _allow_any_dcp_frame_rate = new wxCheckBox (_panel, wxID_ANY, _("Allow any DCP frame rate"));
1300                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
1301                 table->AddSpacer (0);
1302
1303                 _only_servers_encode = new wxCheckBox (_panel, wxID_ANY, _("Only servers encode"));
1304                 table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
1305                 table->AddSpacer (0);
1306
1307 #ifdef __WXOSX__
1308                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
1309                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
1310 #else
1311                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log"));
1312                 table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL, 6);
1313 #endif
1314
1315                 {
1316                         wxBoxSizer* t = new wxBoxSizer (wxVERTICAL);
1317                         _log_general = new wxCheckBox (_panel, wxID_ANY, _("General"));
1318                         t->Add (_log_general, 1, wxEXPAND | wxALL);
1319                         _log_warning = new wxCheckBox (_panel, wxID_ANY, _("Warnings"));
1320                         t->Add (_log_warning, 1, wxEXPAND | wxALL);
1321                         _log_error = new wxCheckBox (_panel, wxID_ANY, _("Errors"));
1322                         t->Add (_log_error, 1, wxEXPAND | wxALL);
1323                         _log_timing = new wxCheckBox (_panel, wxID_ANY, S_("Config|Timing"));
1324                         t->Add (_log_timing, 1, wxEXPAND | wxALL);
1325                         _log_debug_decode = new wxCheckBox (_panel, wxID_ANY, _("Debug: decode"));
1326                         t->Add (_log_debug_decode, 1, wxEXPAND | wxALL);
1327                         _log_debug_encode = new wxCheckBox (_panel, wxID_ANY, _("Debug: encode"));
1328                         t->Add (_log_debug_encode, 1, wxEXPAND | wxALL);
1329                         table->Add (t, 0, wxALL, 6);
1330                 }
1331
1332 #ifdef DCPOMATIC_WINDOWS
1333                 _win32_console = new wxCheckBox (_panel, wxID_ANY, _("Open console window"));
1334                 table->Add (_win32_console, 1, wxEXPAND | wxALL);
1335                 table->AddSpacer (0);
1336 #endif
1337
1338                 _maximum_j2k_bandwidth->SetRange (1, 1000);
1339                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
1340                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
1341                 _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
1342                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1343                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1344                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1345                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1346                 _log_debug_decode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1347                 _log_debug_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
1348 #ifdef DCPOMATIC_WINDOWS
1349                 _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
1350 #endif
1351         }
1352
1353         void config_changed ()
1354         {
1355                 Config* config = Config::instance ();
1356
1357                 checked_set (_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 1000000);
1358                 checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
1359                 checked_set (_only_servers_encode, config->only_servers_encode ());
1360                 checked_set (_log_general, config->log_types() & Log::TYPE_GENERAL);
1361                 checked_set (_log_warning, config->log_types() & Log::TYPE_WARNING);
1362                 checked_set (_log_error, config->log_types() & Log::TYPE_ERROR);
1363                 checked_set (_log_timing, config->log_types() & Log::TYPE_TIMING);
1364                 checked_set (_log_debug_decode, config->log_types() & Log::TYPE_DEBUG_DECODE);
1365                 checked_set (_log_debug_encode, config->log_types() & Log::TYPE_DEBUG_ENCODE);
1366 #ifdef DCPOMATIC_WINDOWS
1367                 checked_set (_win32_console, config->win32_console());
1368 #endif
1369         }
1370
1371         void maximum_j2k_bandwidth_changed ()
1372         {
1373                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
1374         }
1375
1376         void allow_any_dcp_frame_rate_changed ()
1377         {
1378                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
1379         }
1380
1381         void only_servers_encode_changed ()
1382         {
1383                 Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
1384         }
1385
1386         void log_changed ()
1387         {
1388                 int types = 0;
1389                 if (_log_general->GetValue ()) {
1390                         types |= Log::TYPE_GENERAL;
1391                 }
1392                 if (_log_warning->GetValue ()) {
1393                         types |= Log::TYPE_WARNING;
1394                 }
1395                 if (_log_error->GetValue ())  {
1396                         types |= Log::TYPE_ERROR;
1397                 }
1398                 if (_log_timing->GetValue ()) {
1399                         types |= Log::TYPE_TIMING;
1400                 }
1401                 if (_log_debug_decode->GetValue ()) {
1402                         types |= Log::TYPE_DEBUG_DECODE;
1403                 }
1404                 if (_log_debug_encode->GetValue ()) {
1405                         types |= Log::TYPE_DEBUG_ENCODE;
1406                 }
1407                 Config::instance()->set_log_types (types);
1408         }
1409
1410 #ifdef DCPOMATIC_WINDOWS
1411         void win32_console_changed ()
1412         {
1413                 Config::instance()->set_win32_console (_win32_console->GetValue ());
1414         }
1415 #endif
1416
1417         wxSpinCtrl* _maximum_j2k_bandwidth;
1418         wxCheckBox* _allow_any_dcp_frame_rate;
1419         wxCheckBox* _only_servers_encode;
1420         wxCheckBox* _log_general;
1421         wxCheckBox* _log_warning;
1422         wxCheckBox* _log_error;
1423         wxCheckBox* _log_timing;
1424         wxCheckBox* _log_debug_decode;
1425         wxCheckBox* _log_debug_encode;
1426 #ifdef DCPOMATIC_WINDOWS
1427         wxCheckBox* _win32_console;
1428 #endif
1429 };
1430
1431 wxPreferencesEditor*
1432 create_config_dialog ()
1433 {
1434         wxPreferencesEditor* e = new wxPreferencesEditor ();
1435
1436 #ifdef DCPOMATIC_OSX
1437         /* Width that we force some of the config panels to be on OSX so that
1438            the containing window doesn't shrink too much when we select those panels.
1439            This is obviously an unpleasant hack.
1440         */
1441         wxSize ps = wxSize (520, -1);
1442         int const border = 16;
1443 #else
1444         wxSize ps = wxSize (-1, -1);
1445         int const border = 8;
1446 #endif
1447
1448         e->AddPage (new GeneralPage (ps, border));
1449         e->AddPage (new DefaultsPage (ps, border));
1450         e->AddPage (new EncodingServersPage (ps, border));
1451         e->AddPage (new KeysPage (ps, border));
1452         e->AddPage (new TMSPage (ps, border));
1453         e->AddPage (new KDMEmailPage (ps, border));
1454         e->AddPage (new AdvancedPage (ps, border));
1455         return e;
1456 }