Merge master.
[dcpomatic.git] / src / wx / config_dialog.cc
1 /*
2     Copyright (C) 2012-2014 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 <iostream>
25 #include <boost/lexical_cast.hpp>
26 #include <boost/filesystem.hpp>
27 #include <wx/stdpaths.h>
28 #include <wx/preferences.h>
29 #include <wx/filepicker.h>
30 #include <wx/spinctrl.h>
31 #include <dcp/colour_matrix.h>
32 #include "lib/config.h"
33 #include "lib/ratio.h"
34 #include "lib/scaler.h"
35 #include "lib/filter.h"
36 #include "lib/dcp_content_type.h"
37 #include "lib/colour_conversion.h"
38 #include "config_dialog.h"
39 #include "wx_util.h"
40 #include "editable_list.h"
41 #include "filter_dialog.h"
42 #include "dir_picker_ctrl.h"
43 #include "dci_metadata_dialog.h"
44 #include "preset_colour_conversion_dialog.h"
45 #include "server_dialog.h"
46
47 using std::vector;
48 using std::string;
49 using std::list;
50 using std::cout;
51 using boost::bind;
52 using boost::shared_ptr;
53 using boost::lexical_cast;
54
55 class Page
56 {
57 public:
58         Page (wxSize panel_size, int border)
59                 : _panel_size (panel_size)
60                 , _border (border)
61         {}
62
63 protected:
64         wxSize _panel_size;
65         int _border;
66 };
67
68 class GeneralPage : public wxStockPreferencesPage, public Page
69 {
70 public:
71         GeneralPage (wxSize panel_size, int border)
72                 : wxStockPreferencesPage (Kind_General)
73                 , Page (panel_size, border)
74         {}
75
76         wxWindow* CreateWindow (wxWindow* parent)
77         {
78                 wxPanel* panel = new wxPanel (parent);
79                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
80                 panel->SetSizer (s);
81
82                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83                 table->AddGrowableCol (1, 1);
84                 s->Add (table, 1, wxALL | wxEXPAND, _border);
85                 
86                 _set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
87                 table->Add (_set_language, 1);
88                 _language = new wxChoice (panel, wxID_ANY);
89                 _language->Append (wxT ("Deutsch"));
90                 _language->Append (wxT ("English"));
91                 _language->Append (wxT ("Español"));
92                 _language->Append (wxT ("Français"));
93                 _language->Append (wxT ("Italiano"));
94                 _language->Append (wxT ("Nederlands"));
95                 _language->Append (wxT ("Svenska"));
96                 table->Add (_language);
97                 
98                 wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
99                 wxFont font = restart->GetFont();
100                 font.SetStyle (wxFONTSTYLE_ITALIC);
101                 font.SetPointSize (font.GetPointSize() - 1);
102                 restart->SetFont (font);
103                 table->AddSpacer (0);
104                 
105                 add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
106                 _num_local_encoding_threads = new wxSpinCtrl (panel);
107                 table->Add (_num_local_encoding_threads, 1);
108
109                 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
110                 _mail_server = new wxTextCtrl (panel, wxID_ANY);
111                 table->Add (_mail_server, 1, wxEXPAND | wxALL);
112                 
113                 add_label_to_sizer (table, panel, _("Mail user name"), true);
114                 _mail_user = new wxTextCtrl (panel, wxID_ANY);
115                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
116                 
117                 add_label_to_sizer (table, panel, _("Mail password"), true);
118                 _mail_password = new wxTextCtrl (panel, wxID_ANY);
119                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
120                 
121                 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
122                 plain->SetFont (font);
123                 table->AddSpacer (0);
124                 
125                 add_label_to_sizer (table, panel, _("From address for KDM emails"), true);
126                 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
127                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
128                 
129                 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
130                 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
131                 table->AddSpacer (0);
132                 
133                 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
134                 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
135                 table->AddSpacer (0);
136                 
137                 Config* config = Config::instance ();
138                 
139                 _set_language->SetValue (config->language ());
140                 
141                 if (config->language().get_value_or ("") == "fr") {
142                         _language->SetSelection (3);
143                 } else if (config->language().get_value_or ("") == "it") {
144                         _language->SetSelection (4);
145                 } else if (config->language().get_value_or ("") == "es") {
146                         _language->SetSelection (2);
147                 } else if (config->language().get_value_or ("") == "sv") {
148                         _language->SetSelection (6);
149                 } else if (config->language().get_value_or ("") == "de") {
150                         _language->SetSelection (0);
151                 } else if (config->language().get_value_or ("") == "nl") {
152                         _language->SetSelection (5);
153                 } else {
154                         _language->SetSelection (1);
155                 }
156                 
157                 setup_language_sensitivity ();
158                 
159                 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
160                 _language->Bind     (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&GeneralPage::language_changed,     this));
161                 
162                 _num_local_encoding_threads->SetRange (1, 128);
163                 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
164                 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
165
166                 _mail_server->SetValue (std_to_wx (config->mail_server ()));
167                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_server_changed, this));
168                 _mail_user->SetValue (std_to_wx (config->mail_user ()));
169                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_user_changed, this));
170                 _mail_password->SetValue (std_to_wx (config->mail_password ()));
171                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_password_changed, this));
172                 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
173                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::kdm_from_changed, this));
174                 _check_for_updates->SetValue (config->check_for_updates ());
175                 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
176                 _check_for_test_updates->SetValue (config->check_for_test_updates ());
177                 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
178                 
179                 return panel;
180         }
181
182 private:        
183         void setup_language_sensitivity ()
184         {
185                 _language->Enable (_set_language->GetValue ());
186         }
187
188         void set_language_changed ()
189         {
190                 setup_language_sensitivity ();
191                 if (_set_language->GetValue ()) {
192                         language_changed ();
193                 } else {
194                         Config::instance()->unset_language ();
195                 }
196         }
197
198         void language_changed ()
199         {
200                 switch (_language->GetSelection ()) {
201                 case 0:
202                         Config::instance()->set_language ("de");
203                         break;
204                 case 1:
205                         Config::instance()->set_language ("en");
206                         break;
207                 case 2:
208                         Config::instance()->set_language ("es");
209                         break;
210                 case 3:
211                         Config::instance()->set_language ("fr");
212                         break;
213                 case 4:
214                         Config::instance()->set_language ("it");
215                         break;
216                 case 5:
217                         Config::instance()->set_language ("nl");
218                         break;
219                 case 6:
220                         Config::instance()->set_language ("sv");
221                         break;
222                 }
223         }
224         
225         void mail_server_changed ()
226         {
227                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
228         }
229         
230         void mail_user_changed ()
231         {
232                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
233         }
234         
235         void mail_password_changed ()
236         {
237                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
238         }
239         
240         void kdm_from_changed ()
241         {
242                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
243         }
244
245         void check_for_updates_changed ()
246         {
247                 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
248         }
249         
250         void check_for_test_updates_changed ()
251         {
252                 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
253         }
254
255         void num_local_encoding_threads_changed ()
256         {
257                 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
258         }
259
260         wxCheckBox* _set_language;
261         wxChoice* _language;
262         wxSpinCtrl* _num_local_encoding_threads;
263         wxTextCtrl* _mail_server;
264         wxTextCtrl* _mail_user;
265         wxTextCtrl* _mail_password;
266         wxTextCtrl* _kdm_from;
267         wxCheckBox* _check_for_updates;
268         wxCheckBox* _check_for_test_updates;
269 };
270
271 class DefaultsPage : public wxPreferencesPage, public Page
272 {
273 public:
274         DefaultsPage (wxSize panel_size, int border)
275                 : Page (panel_size, border)
276         {}
277         
278         wxString GetName () const
279         {
280                 return _("Defaults");
281         }
282
283 #ifdef DCPOMATIC_OSX    
284         wxBitmap GetLargeIcon () const
285         {
286                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
287         }
288 #endif  
289
290         wxWindow* CreateWindow (wxWindow* parent)
291         {
292                 wxPanel* panel = new wxPanel (parent);
293                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
294                 panel->SetSizer (s);
295
296                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
297                 table->AddGrowableCol (1, 1);
298                 s->Add (table, 1, wxALL | wxEXPAND, _border);
299                 
300                 {
301                         add_label_to_sizer (table, panel, _("Default duration of still images"), true);
302                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
303                         _still_length = new wxSpinCtrl (panel);
304                         s->Add (_still_length);
305                         add_label_to_sizer (s, panel, _("s"), false);
306                         table->Add (s, 1);
307                 }
308                 
309                 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
310 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
311                 _directory = new DirPickerCtrl (panel);
312 #else   
313                 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
314 #endif
315                 table->Add (_directory, 1, wxEXPAND);
316                 
317                 add_label_to_sizer (table, panel, _("Default DCI name details"), true);
318                 _dci_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
319                 table->Add (_dci_metadata_button);
320                 
321                 add_label_to_sizer (table, panel, _("Default container"), true);
322                 _container = new wxChoice (panel, wxID_ANY);
323                 table->Add (_container);
324                 
325                 add_label_to_sizer (table, panel, _("Default content type"), true);
326                 _dcp_content_type = new wxChoice (panel, wxID_ANY);
327                 table->Add (_dcp_content_type);
328                 
329                 {
330                         add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
331                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
332                         _j2k_bandwidth = new wxSpinCtrl (panel);
333                         s->Add (_j2k_bandwidth);
334                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
335                         table->Add (s, 1);
336                 }
337                 
338                 {
339                         add_label_to_sizer (table, panel, _("Default audio delay"), true);
340                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
341                         _audio_delay = new wxSpinCtrl (panel);
342                         s->Add (_audio_delay);
343                         add_label_to_sizer (s, panel, _("ms"), false);
344                         table->Add (s, 1);
345                 }
346
347                 add_label_to_sizer (table, panel, _("Default issuer"), true);
348                 _issuer = new wxTextCtrl (panel, wxID_ANY);
349                 table->Add (_issuer, 1, wxEXPAND);
350
351                 add_label_to_sizer (table, panel, _("Default creator"), true);
352                 _creator = new wxTextCtrl (panel, wxID_ANY);
353                 table->Add (_creator, 1, wxEXPAND);
354                 
355                 Config* config = Config::instance ();
356                 
357                 _still_length->SetRange (1, 3600);
358                 _still_length->SetValue (config->default_still_length ());
359                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
360                 
361                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
362                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
363                 
364                 _dci_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_dci_metadata_clicked, this, parent));
365                 
366                 vector<Ratio const *> ratio = Ratio::all ();
367                 int n = 0;
368                 for (vector<Ratio const *>::iterator i = ratio.begin(); i != ratio.end(); ++i) {
369                         _container->Append (std_to_wx ((*i)->nickname ()));
370                         if (*i == config->default_container ()) {
371                                 _container->SetSelection (n);
372                         }
373                         ++n;
374                 }
375                 
376                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
377                 
378                 vector<DCPContentType const *> const ct = DCPContentType::all ();
379                 n = 0;
380                 for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
381                         _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
382                         if (*i == config->default_dcp_content_type ()) {
383                                 _dcp_content_type->SetSelection (n);
384                         }
385                         ++n;
386                 }
387                 
388                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
389                 
390                 _j2k_bandwidth->SetRange (50, 250);
391                 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
392                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
393                 
394                 _audio_delay->SetRange (-1000, 1000);
395                 _audio_delay->SetValue (config->default_audio_delay ());
396                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
397
398                 _issuer->SetValue (std_to_wx (config->dcp_metadata().issuer));
399                 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
400                 _creator->SetValue (std_to_wx (config->dcp_metadata().creator));
401                 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::creator_changed, this));
402
403                 return panel;
404         }
405
406 private:
407         void j2k_bandwidth_changed ()
408         {
409                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
410         }
411         
412         void audio_delay_changed ()
413         {
414                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
415         }
416
417         void directory_changed ()
418         {
419                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
420         }
421
422         void edit_dci_metadata_clicked (wxWindow* parent)
423         {
424                 DCIMetadataDialog* d = new DCIMetadataDialog (parent, Config::instance()->default_dci_metadata ());
425                 d->ShowModal ();
426                 Config::instance()->set_default_dci_metadata (d->dci_metadata ());
427                 d->Destroy ();
428         }
429
430         void still_length_changed ()
431         {
432                 Config::instance()->set_default_still_length (_still_length->GetValue ());
433         }
434         
435         void container_changed ()
436         {
437                 vector<Ratio const *> ratio = Ratio::all ();
438                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
439         }
440         
441         void dcp_content_type_changed ()
442         {
443                 vector<DCPContentType const *> ct = DCPContentType::all ();
444                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
445         }
446
447         void issuer_changed ()
448         {
449                 dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
450                 m.issuer = wx_to_std (_issuer->GetValue ());
451                 Config::instance()->set_dcp_metadata (m);
452         }
453         
454         void creator_changed ()
455         {
456                 dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
457                 m.creator = wx_to_std (_creator->GetValue ());
458                 Config::instance()->set_dcp_metadata (m);
459         }
460         
461         wxSpinCtrl* _j2k_bandwidth;
462         wxSpinCtrl* _audio_delay;
463         wxButton* _dci_metadata_button;
464         wxSpinCtrl* _still_length;
465 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
466         DirPickerCtrl* _directory;
467 #else
468         wxDirPickerCtrl* _directory;
469 #endif
470         wxChoice* _container;
471         wxChoice* _dcp_content_type;
472         wxTextCtrl* _issuer;
473         wxTextCtrl* _creator;
474 };
475
476 class EncodingServersPage : public wxPreferencesPage, public Page
477 {
478 public:
479         EncodingServersPage (wxSize panel_size, int border)
480                 : Page (panel_size, border)
481         {}
482         
483         wxString GetName () const
484         {
485                 return _("Servers");
486         }
487
488 #ifdef DCPOMATIC_OSX    
489         wxBitmap GetLargeIcon () const
490         {
491                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
492         }
493 #endif  
494
495         wxWindow* CreateWindow (wxWindow* parent)
496         {
497                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
498                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
499                 panel->SetSizer (s);
500                 
501                 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
502                 s->Add (_use_any_servers, 0, wxALL, _border);
503                 
504                 vector<string> columns;
505                 columns.push_back (wx_to_std (_("IP address / host name")));
506                 _servers_list = new EditableList<string, ServerDialog> (
507                         panel,
508                         columns,
509                         boost::bind (&Config::servers, Config::instance()),
510                         boost::bind (&Config::set_servers, Config::instance(), _1),
511                         boost::bind (&EncodingServersPage::server_column, this, _1)
512                         );
513                 
514                 s->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
515                 
516                 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
517                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
518
519                 return panel;
520         }
521
522 private:        
523
524         void use_any_servers_changed ()
525         {
526                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
527         }
528
529         string server_column (string s)
530         {
531                 return s;
532         }
533
534         wxCheckBox* _use_any_servers;
535         EditableList<string, ServerDialog>* _servers_list;
536 };
537
538 class ColourConversionsPage : public wxPreferencesPage, public Page
539 {
540 public:
541         ColourConversionsPage (wxSize panel_size, int border)
542                 : Page (panel_size, border)
543         {}
544         
545         wxString GetName () const
546         {
547                 return _("Colour Conversions");
548         }
549
550 #ifdef DCPOMATIC_OSX    
551         wxBitmap GetLargeIcon () const
552         {
553                 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
554         }
555 #endif  
556         wxWindow* CreateWindow (wxWindow* parent)
557         {
558                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
559                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
560                 panel->SetSizer (s);
561
562                 vector<string> columns;
563                 columns.push_back (wx_to_std (_("Name")));
564                 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
565                         panel,
566                         columns,
567                         boost::bind (&Config::colour_conversions, Config::instance()),
568                         boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
569                         boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
570                         300
571                         );
572
573                 s->Add (list, 1, wxEXPAND | wxALL, _border);
574                 return panel;
575         }
576
577 private:
578         string colour_conversion_column (PresetColourConversion c)
579         {
580                 return c.name;
581         }
582 };
583
584 class TMSPage : public wxPreferencesPage, public Page
585 {
586 public:
587         TMSPage (wxSize panel_size, int border)
588                 : Page (panel_size, border)
589         {}
590
591         wxString GetName () const
592         {
593                 return _("TMS");
594         }
595
596 #ifdef DCPOMATIC_OSX    
597         wxBitmap GetLargeIcon () const
598         {
599                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
600         }
601 #endif  
602
603         wxWindow* CreateWindow (wxWindow* parent)
604         {
605                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
606                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
607                 panel->SetSizer (s);
608
609                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
610                 table->AddGrowableCol (1, 1);
611                 s->Add (table, 1, wxALL | wxEXPAND, _border);
612                 
613                 add_label_to_sizer (table, panel, _("IP address"), true);
614                 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
615                 table->Add (_tms_ip, 1, wxEXPAND);
616                 
617                 add_label_to_sizer (table, panel, _("Target path"), true);
618                 _tms_path = new wxTextCtrl (panel, wxID_ANY);
619                 table->Add (_tms_path, 1, wxEXPAND);
620                 
621                 add_label_to_sizer (table, panel, _("User name"), true);
622                 _tms_user = new wxTextCtrl (panel, wxID_ANY);
623                 table->Add (_tms_user, 1, wxEXPAND);
624                 
625                 add_label_to_sizer (table, panel, _("Password"), true);
626                 _tms_password = new wxTextCtrl (panel, wxID_ANY);
627                 table->Add (_tms_password, 1, wxEXPAND);
628                 
629                 Config* config = Config::instance ();
630                 
631                 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
632                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
633                 _tms_path->SetValue (std_to_wx (config->tms_path ()));
634                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
635                 _tms_user->SetValue (std_to_wx (config->tms_user ()));
636                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
637                 _tms_password->SetValue (std_to_wx (config->tms_password ()));
638                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
639
640                 return panel;
641         }
642
643 private:
644         void tms_ip_changed ()
645         {
646                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
647         }
648         
649         void tms_path_changed ()
650         {
651                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
652         }
653         
654         void tms_user_changed ()
655         {
656                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
657         }
658         
659         void tms_password_changed ()
660         {
661                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
662         }
663
664         wxTextCtrl* _tms_ip;
665         wxTextCtrl* _tms_path;
666         wxTextCtrl* _tms_user;
667         wxTextCtrl* _tms_password;
668 };
669
670 class KDMEmailPage : public wxPreferencesPage, public Page
671 {
672 public:
673
674         KDMEmailPage (wxSize panel_size, int border)
675                 : Page (panel_size, border)
676         {}
677         
678         wxString GetName () const
679         {
680                 return _("KDM Email");
681         }
682
683 #ifdef DCPOMATIC_OSX    
684         wxBitmap GetLargeIcon () const
685         {
686                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
687         }
688 #endif  
689
690         wxWindow* CreateWindow (wxWindow* parent)
691         {
692                 /* We have to force both width and height of this one */
693 #ifdef DCPOMATIC_OSX
694                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
695 #else           
696                 wxPanel* panel = new wxPanel (parent);
697 #endif          
698                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
699                 panel->SetSizer (s);
700                 
701                 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
702                 s->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
703                 
704                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
705                 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
706
707                 return panel;
708         }
709
710 private:        
711         void kdm_email_changed ()
712         {
713                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
714         }
715
716         wxTextCtrl* _kdm_email;
717 };
718
719 class AdvancedPage : public wxStockPreferencesPage, public Page
720 {
721 public:
722
723         AdvancedPage (wxSize panel_size, int border)
724                 : wxStockPreferencesPage (Kind_Advanced)
725                 , Page (panel_size, border)
726         {}
727         
728         wxWindow* CreateWindow (wxWindow* parent)
729         {
730                 wxPanel* panel = new wxPanel (parent);
731
732                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
733                 panel->SetSizer (s);
734
735                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
736                 table->AddGrowableCol (1, 1);
737                 s->Add (table, 1, wxALL | wxEXPAND, _border);
738                 
739                 add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
740                 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
741                 table->Add (_maximum_j2k_bandwidth, 1);
742
743                 _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
744                 table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
745                 table->AddSpacer (0);
746
747                 add_label_to_sizer (table, panel, _("Log"), true);
748                 _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
749                 table->Add (_log_general, 1, wxEXPAND | wxALL);
750                 _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
751                 table->AddSpacer (0);
752                 table->Add (_log_warning, 1, wxEXPAND | wxALL);
753                 _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
754                 table->AddSpacer (0);
755                 table->Add (_log_error, 1, wxEXPAND | wxALL);
756                 _log_timing = new wxCheckBox (panel, wxID_ANY, _("Timing"));
757                 table->AddSpacer (0);
758                 table->Add (_log_timing, 1, wxEXPAND | wxALL);
759
760                 Config* config = Config::instance ();
761                 
762                 _maximum_j2k_bandwidth->SetRange (1, 500);
763                 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
764                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
765                 _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
766                 _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
767                 _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
768                 _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
769                 _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
770                 _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
771                 _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
772                 _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
773                 _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
774                 _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
775                 
776                 return panel;
777         }
778
779 private:
780
781         void maximum_j2k_bandwidth_changed ()
782         {
783                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
784         }
785
786         void allow_any_dcp_frame_rate_changed ()
787         {
788                 Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
789         }
790
791         void log_changed ()
792         {
793                 int types = 0;
794                 if (_log_general->GetValue ()) {
795                         types |= Log::TYPE_GENERAL;
796                 }
797                 if (_log_warning->GetValue ()) {
798                         types |= Log::TYPE_WARNING;
799                 }
800                 if (_log_error->GetValue ())  {
801                         types |= Log::TYPE_ERROR;
802                 }
803                 if (_log_timing->GetValue ()) {
804                         types |= Log::TYPE_TIMING;
805                 }
806                 Config::instance()->set_log_types (types);
807         }
808         
809         wxSpinCtrl* _maximum_j2k_bandwidth;
810         wxCheckBox* _allow_any_dcp_frame_rate;
811         wxCheckBox* _log_general;
812         wxCheckBox* _log_warning;
813         wxCheckBox* _log_error;
814         wxCheckBox* _log_timing;
815 };
816         
817 wxPreferencesEditor*
818 create_config_dialog ()
819 {
820         wxPreferencesEditor* e = new wxPreferencesEditor ();
821
822 #ifdef DCPOMATIC_OSX
823         /* Width that we force some of the config panels to be on OSX so that
824            the containing window doesn't shrink too much when we select those panels.
825            This is obviously an unpleasant hack.
826         */
827         wxSize ps = wxSize (480, -1);
828         int const border = 16;
829 #else
830         /* We seem to need to specify height here, otherwise the general panel
831            is too short (at least on Linux).
832          */
833         wxSize ps = wxSize (-1, 400);
834         int const border = 8;
835 #endif
836         
837         e->AddPage (new GeneralPage (ps, border));
838         e->AddPage (new DefaultsPage (ps, border));
839         e->AddPage (new EncodingServersPage (ps, border));
840         e->AddPage (new ColourConversionsPage (ps, border));
841         e->AddPage (new TMSPage (ps, border));
842         e->AddPage (new KDMEmailPage (ps, border));
843         e->AddPage (new AdvancedPage (ps, border));
844         return e;
845 }