Don't overlap simultaneous video content in the timeline. Fix keep-aligned for separ...
[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 <libdcp/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, _("Maximum JPEG2000 bandwidth"), true);
110                 _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
111                 table->Add (_maximum_j2k_bandwidth, 1);
112                 
113                 add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
114                 _mail_server = new wxTextCtrl (panel, wxID_ANY);
115                 table->Add (_mail_server, 1, wxEXPAND | wxALL);
116                 
117                 add_label_to_sizer (table, panel, _("Mail user name"), true);
118                 _mail_user = new wxTextCtrl (panel, wxID_ANY);
119                 table->Add (_mail_user, 1, wxEXPAND | wxALL);
120                 
121                 add_label_to_sizer (table, panel, _("Mail password"), true);
122                 _mail_password = new wxTextCtrl (panel, wxID_ANY);
123                 table->Add (_mail_password, 1, wxEXPAND | wxALL);
124                 
125                 wxStaticText* plain = add_label_to_sizer (table, panel, _("(password will be stored on disk in plaintext)"), false);
126                 plain->SetFont (font);
127                 table->AddSpacer (0);
128                 
129                 add_label_to_sizer (table, panel, _("From address for KDM emails"), true);
130                 _kdm_from = new wxTextCtrl (panel, wxID_ANY);
131                 table->Add (_kdm_from, 1, wxEXPAND | wxALL);
132                 
133                 _check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
134                 table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
135                 table->AddSpacer (0);
136                 
137                 _check_for_test_updates = new wxCheckBox (panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
138                 table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
139                 table->AddSpacer (0);
140                 
141                 Config* config = Config::instance ();
142                 
143                 _set_language->SetValue (config->language ());
144                 
145                 if (config->language().get_value_or ("") == "fr") {
146                         _language->SetSelection (3);
147                 } else if (config->language().get_value_or ("") == "it") {
148                         _language->SetSelection (4);
149                 } else if (config->language().get_value_or ("") == "es") {
150                         _language->SetSelection (2);
151                 } else if (config->language().get_value_or ("") == "sv") {
152                         _language->SetSelection (6);
153                 } else if (config->language().get_value_or ("") == "de") {
154                         _language->SetSelection (0);
155                 } else if (config->language().get_value_or ("") == "nl") {
156                         _language->SetSelection (5);
157                 } else {
158                         _language->SetSelection (1);
159                 }
160                 
161                 setup_language_sensitivity ();
162                 
163                 _set_language->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::set_language_changed, this));
164                 _language->Bind     (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&GeneralPage::language_changed,     this));
165                 
166                 _num_local_encoding_threads->SetRange (1, 128);
167                 _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
168                 _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
169
170                 _maximum_j2k_bandwidth->SetRange (1, 500);
171                 _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
172                 _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::maximum_j2k_bandwidth_changed, this));
173                 
174                 _mail_server->SetValue (std_to_wx (config->mail_server ()));
175                 _mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_server_changed, this));
176                 _mail_user->SetValue (std_to_wx (config->mail_user ()));
177                 _mail_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_user_changed, this));
178                 _mail_password->SetValue (std_to_wx (config->mail_password ()));
179                 _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_password_changed, this));
180                 _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
181                 _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::kdm_from_changed, this));
182                 _check_for_updates->SetValue (config->check_for_updates ());
183                 _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
184                 _check_for_test_updates->SetValue (config->check_for_test_updates ());
185                 _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
186                 
187                 return panel;
188         }
189
190 private:        
191         void setup_language_sensitivity ()
192         {
193                 _language->Enable (_set_language->GetValue ());
194         }
195
196         void set_language_changed ()
197         {
198                 setup_language_sensitivity ();
199                 if (_set_language->GetValue ()) {
200                         language_changed ();
201                 } else {
202                         Config::instance()->unset_language ();
203                 }
204         }
205
206         void language_changed ()
207         {
208                 switch (_language->GetSelection ()) {
209                 case 0:
210                         Config::instance()->set_language ("de");
211                         break;
212                 case 1:
213                         Config::instance()->set_language ("en");
214                         break;
215                 case 2:
216                         Config::instance()->set_language ("es");
217                         break;
218                 case 3:
219                         Config::instance()->set_language ("fr");
220                         break;
221                 case 4:
222                         Config::instance()->set_language ("it");
223                         break;
224                 case 5:
225                         Config::instance()->set_language ("nl");
226                         break;
227                 case 6:
228                         Config::instance()->set_language ("sv");
229                         break;
230                 }
231         }
232         
233         void mail_server_changed ()
234         {
235                 Config::instance()->set_mail_server (wx_to_std (_mail_server->GetValue ()));
236         }
237         
238         void mail_user_changed ()
239         {
240                 Config::instance()->set_mail_user (wx_to_std (_mail_user->GetValue ()));
241         }
242         
243         void mail_password_changed ()
244         {
245                 Config::instance()->set_mail_password (wx_to_std (_mail_password->GetValue ()));
246         }
247         
248         void kdm_from_changed ()
249         {
250                 Config::instance()->set_kdm_from (wx_to_std (_kdm_from->GetValue ()));
251         }
252
253         void check_for_updates_changed ()
254         {
255                 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
256         }
257         
258         void check_for_test_updates_changed ()
259         {
260                 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
261         }
262
263         void num_local_encoding_threads_changed ()
264         {
265                 Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
266         }
267
268         void maximum_j2k_bandwidth_changed ()
269         {
270                 Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
271         }
272         
273         wxCheckBox* _set_language;
274         wxChoice* _language;
275         wxSpinCtrl* _num_local_encoding_threads;
276         wxSpinCtrl* _maximum_j2k_bandwidth;
277         wxTextCtrl* _mail_server;
278         wxTextCtrl* _mail_user;
279         wxTextCtrl* _mail_password;
280         wxTextCtrl* _kdm_from;
281         wxCheckBox* _check_for_updates;
282         wxCheckBox* _check_for_test_updates;
283 };
284
285 class DefaultsPage : public wxPreferencesPage, public Page
286 {
287 public:
288         DefaultsPage (wxSize panel_size, int border)
289                 : Page (panel_size, border)
290         {}
291         
292         wxString GetName () const
293         {
294                 return _("Defaults");
295         }
296
297 #ifdef DCPOMATIC_OSX    
298         wxBitmap GetLargeIcon () const
299         {
300                 return wxBitmap ("defaults", wxBITMAP_TYPE_PNG_RESOURCE);
301         }
302 #endif  
303
304         wxWindow* CreateWindow (wxWindow* parent)
305         {
306                 wxPanel* panel = new wxPanel (parent);
307                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
308                 panel->SetSizer (s);
309
310                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
311                 table->AddGrowableCol (1, 1);
312                 s->Add (table, 1, wxALL | wxEXPAND, _border);
313                 
314                 {
315                         add_label_to_sizer (table, panel, _("Default duration of still images"), true);
316                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
317                         _still_length = new wxSpinCtrl (panel);
318                         s->Add (_still_length);
319                         add_label_to_sizer (s, panel, _("s"), false);
320                         table->Add (s, 1);
321                 }
322                 
323                 add_label_to_sizer (table, panel, _("Default directory for new films"), true);
324 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
325                 _directory = new DirPickerCtrl (panel);
326 #else   
327                 _directory = new wxDirPickerCtrl (panel, wxDD_DIR_MUST_EXIST);
328 #endif
329                 table->Add (_directory, 1, wxEXPAND);
330                 
331                 add_label_to_sizer (table, panel, _("Default DCI name details"), true);
332                 _dci_metadata_button = new wxButton (panel, wxID_ANY, _("Edit..."));
333                 table->Add (_dci_metadata_button);
334                 
335                 add_label_to_sizer (table, panel, _("Default container"), true);
336                 _container = new wxChoice (panel, wxID_ANY);
337                 table->Add (_container);
338                 
339                 add_label_to_sizer (table, panel, _("Default content type"), true);
340                 _dcp_content_type = new wxChoice (panel, wxID_ANY);
341                 table->Add (_dcp_content_type);
342                 
343                 {
344                         add_label_to_sizer (table, panel, _("Default JPEG2000 bandwidth"), true);
345                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
346                         _j2k_bandwidth = new wxSpinCtrl (panel);
347                         s->Add (_j2k_bandwidth);
348                         add_label_to_sizer (s, panel, _("Mbit/s"), false);
349                         table->Add (s, 1);
350                 }
351                 
352                 {
353                         add_label_to_sizer (table, panel, _("Default audio delay"), true);
354                         wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
355                         _audio_delay = new wxSpinCtrl (panel);
356                         s->Add (_audio_delay);
357                         add_label_to_sizer (s, panel, _("ms"), false);
358                         table->Add (s, 1);
359                 }
360
361                 add_label_to_sizer (table, panel, _("Default issuer"), true);
362                 _issuer = new wxTextCtrl (panel, wxID_ANY);
363                 table->Add (_issuer, 1, wxEXPAND);
364
365                 add_label_to_sizer (table, panel, _("Default creator"), true);
366                 _creator = new wxTextCtrl (panel, wxID_ANY);
367                 table->Add (_creator, 1, wxEXPAND);
368                 
369                 Config* config = Config::instance ();
370                 
371                 _still_length->SetRange (1, 3600);
372                 _still_length->SetValue (config->default_still_length ());
373                 _still_length->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::still_length_changed, this));
374                 
375                 _directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()));
376                 _directory->Bind (wxEVT_COMMAND_DIRPICKER_CHANGED, boost::bind (&DefaultsPage::directory_changed, this));
377                 
378                 _dci_metadata_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DefaultsPage::edit_dci_metadata_clicked, this, parent));
379                 
380                 vector<Ratio const *> ratio = Ratio::all ();
381                 int n = 0;
382                 for (vector<Ratio const *>::iterator i = ratio.begin(); i != ratio.end(); ++i) {
383                         _container->Append (std_to_wx ((*i)->nickname ()));
384                         if (*i == config->default_container ()) {
385                                 _container->SetSelection (n);
386                         }
387                         ++n;
388                 }
389                 
390                 _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::container_changed, this));
391                 
392                 vector<DCPContentType const *> const ct = DCPContentType::all ();
393                 n = 0;
394                 for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
395                         _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
396                         if (*i == config->default_dcp_content_type ()) {
397                                 _dcp_content_type->SetSelection (n);
398                         }
399                         ++n;
400                 }
401                 
402                 _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DefaultsPage::dcp_content_type_changed, this));
403                 
404                 _j2k_bandwidth->SetRange (50, 250);
405                 _j2k_bandwidth->SetValue (config->default_j2k_bandwidth() / 1000000);
406                 _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::j2k_bandwidth_changed, this));
407                 
408                 _audio_delay->SetRange (-1000, 1000);
409                 _audio_delay->SetValue (config->default_audio_delay ());
410                 _audio_delay->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DefaultsPage::audio_delay_changed, this));
411
412                 _issuer->SetValue (std_to_wx (config->dcp_metadata().issuer));
413                 _issuer->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::issuer_changed, this));
414                 _creator->SetValue (std_to_wx (config->dcp_metadata().creator));
415                 _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DefaultsPage::creator_changed, this));
416
417                 return panel;
418         }
419
420 private:
421         void j2k_bandwidth_changed ()
422         {
423                 Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
424         }
425         
426         void audio_delay_changed ()
427         {
428                 Config::instance()->set_default_audio_delay (_audio_delay->GetValue());
429         }
430
431         void directory_changed ()
432         {
433                 Config::instance()->set_default_directory (wx_to_std (_directory->GetPath ()));
434         }
435
436         void edit_dci_metadata_clicked (wxWindow* parent)
437         {
438                 DCIMetadataDialog* d = new DCIMetadataDialog (parent, Config::instance()->default_dci_metadata ());
439                 d->ShowModal ();
440                 Config::instance()->set_default_dci_metadata (d->dci_metadata ());
441                 d->Destroy ();
442         }
443
444         void still_length_changed ()
445         {
446                 Config::instance()->set_default_still_length (_still_length->GetValue ());
447         }
448         
449         void container_changed ()
450         {
451                 vector<Ratio const *> ratio = Ratio::all ();
452                 Config::instance()->set_default_container (ratio[_container->GetSelection()]);
453         }
454         
455         void dcp_content_type_changed ()
456         {
457                 vector<DCPContentType const *> ct = DCPContentType::all ();
458                 Config::instance()->set_default_dcp_content_type (ct[_dcp_content_type->GetSelection()]);
459         }
460
461         void issuer_changed ()
462         {
463                 libdcp::XMLMetadata m = Config::instance()->dcp_metadata ();
464                 m.issuer = wx_to_std (_issuer->GetValue ());
465                 Config::instance()->set_dcp_metadata (m);
466         }
467         
468         void creator_changed ()
469         {
470                 libdcp::XMLMetadata m = Config::instance()->dcp_metadata ();
471                 m.creator = wx_to_std (_creator->GetValue ());
472                 Config::instance()->set_dcp_metadata (m);
473         }
474         
475         wxSpinCtrl* _j2k_bandwidth;
476         wxSpinCtrl* _audio_delay;
477         wxButton* _dci_metadata_button;
478         wxSpinCtrl* _still_length;
479 #ifdef DCPOMATIC_USE_OWN_DIR_PICKER
480         DirPickerCtrl* _directory;
481 #else
482         wxDirPickerCtrl* _directory;
483 #endif
484         wxChoice* _container;
485         wxChoice* _dcp_content_type;
486         wxTextCtrl* _issuer;
487         wxTextCtrl* _creator;
488 };
489
490 class EncodingServersPage : public wxPreferencesPage, public Page
491 {
492 public:
493         EncodingServersPage (wxSize panel_size, int border)
494                 : Page (panel_size, border)
495         {}
496         
497         wxString GetName () const
498         {
499                 return _("Servers");
500         }
501
502 #ifdef DCPOMATIC_OSX    
503         wxBitmap GetLargeIcon () const
504         {
505                 return wxBitmap ("servers", wxBITMAP_TYPE_PNG_RESOURCE);
506         }
507 #endif  
508
509         wxWindow* CreateWindow (wxWindow* parent)
510         {
511                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
512                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
513                 panel->SetSizer (s);
514                 
515                 _use_any_servers = new wxCheckBox (panel, wxID_ANY, _("Use all servers"));
516                 s->Add (_use_any_servers, 0, wxALL, _border);
517                 
518                 vector<string> columns;
519                 columns.push_back (wx_to_std (_("IP address / host name")));
520                 _servers_list = new EditableList<string, ServerDialog> (
521                         panel,
522                         columns,
523                         boost::bind (&Config::servers, Config::instance()),
524                         boost::bind (&Config::set_servers, Config::instance(), _1),
525                         boost::bind (&EncodingServersPage::server_column, this, _1)
526                         );
527                 
528                 s->Add (_servers_list, 1, wxEXPAND | wxALL, _border);
529                 
530                 _use_any_servers->SetValue (Config::instance()->use_any_servers ());
531                 _use_any_servers->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&EncodingServersPage::use_any_servers_changed, this));
532
533                 return panel;
534         }
535
536 private:        
537
538         void use_any_servers_changed ()
539         {
540                 Config::instance()->set_use_any_servers (_use_any_servers->GetValue ());
541         }
542
543         string server_column (string s)
544         {
545                 return s;
546         }
547
548         wxCheckBox* _use_any_servers;
549         EditableList<string, ServerDialog>* _servers_list;
550 };
551
552 class ColourConversionsPage : public wxPreferencesPage, public Page
553 {
554 public:
555         ColourConversionsPage (wxSize panel_size, int border)
556                 : Page (panel_size, border)
557         {}
558         
559         wxString GetName () const
560         {
561                 return _("Colour Conversions");
562         }
563
564 #ifdef DCPOMATIC_OSX    
565         wxBitmap GetLargeIcon () const
566         {
567                 return wxBitmap ("colour_conversions", wxBITMAP_TYPE_PNG_RESOURCE);
568         }
569 #endif  
570         wxWindow* CreateWindow (wxWindow* parent)
571         {
572                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
573                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
574                 panel->SetSizer (s);
575
576                 vector<string> columns;
577                 columns.push_back (wx_to_std (_("Name")));
578                 wxPanel* list = new EditableList<PresetColourConversion, PresetColourConversionDialog> (
579                         panel,
580                         columns,
581                         boost::bind (&Config::colour_conversions, Config::instance()),
582                         boost::bind (&Config::set_colour_conversions, Config::instance(), _1),
583                         boost::bind (&ColourConversionsPage::colour_conversion_column, this, _1),
584                         300
585                         );
586
587                 s->Add (list, 1, wxEXPAND | wxALL, _border);
588                 return panel;
589         }
590
591 private:
592         string colour_conversion_column (PresetColourConversion c)
593         {
594                 return c.name;
595         }
596 };
597
598 class TMSPage : public wxPreferencesPage, public Page
599 {
600 public:
601         TMSPage (wxSize panel_size, int border)
602                 : Page (panel_size, border)
603         {}
604
605         wxString GetName () const
606         {
607                 return _("TMS");
608         }
609
610 #ifdef DCPOMATIC_OSX    
611         wxBitmap GetLargeIcon () const
612         {
613                 return wxBitmap ("tms", wxBITMAP_TYPE_PNG_RESOURCE);
614         }
615 #endif  
616
617         wxWindow* CreateWindow (wxWindow* parent)
618         {
619                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
620                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
621                 panel->SetSizer (s);
622
623                 wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
624                 table->AddGrowableCol (1, 1);
625                 s->Add (table, 1, wxALL | wxEXPAND, _border);
626                 
627                 add_label_to_sizer (table, panel, _("IP address"), true);
628                 _tms_ip = new wxTextCtrl (panel, wxID_ANY);
629                 table->Add (_tms_ip, 1, wxEXPAND);
630                 
631                 add_label_to_sizer (table, panel, _("Target path"), true);
632                 _tms_path = new wxTextCtrl (panel, wxID_ANY);
633                 table->Add (_tms_path, 1, wxEXPAND);
634                 
635                 add_label_to_sizer (table, panel, _("User name"), true);
636                 _tms_user = new wxTextCtrl (panel, wxID_ANY);
637                 table->Add (_tms_user, 1, wxEXPAND);
638                 
639                 add_label_to_sizer (table, panel, _("Password"), true);
640                 _tms_password = new wxTextCtrl (panel, wxID_ANY);
641                 table->Add (_tms_password, 1, wxEXPAND);
642                 
643                 Config* config = Config::instance ();
644                 
645                 _tms_ip->SetValue (std_to_wx (config->tms_ip ()));
646                 _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
647                 _tms_path->SetValue (std_to_wx (config->tms_path ()));
648                 _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
649                 _tms_user->SetValue (std_to_wx (config->tms_user ()));
650                 _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
651                 _tms_password->SetValue (std_to_wx (config->tms_password ()));
652                 _tms_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_password_changed, this));
653
654                 return panel;
655         }
656
657 private:
658         void tms_ip_changed ()
659         {
660                 Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
661         }
662         
663         void tms_path_changed ()
664         {
665                 Config::instance()->set_tms_path (wx_to_std (_tms_path->GetValue ()));
666         }
667         
668         void tms_user_changed ()
669         {
670                 Config::instance()->set_tms_user (wx_to_std (_tms_user->GetValue ()));
671         }
672         
673         void tms_password_changed ()
674         {
675                 Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
676         }
677
678         wxTextCtrl* _tms_ip;
679         wxTextCtrl* _tms_path;
680         wxTextCtrl* _tms_user;
681         wxTextCtrl* _tms_password;
682 };
683
684 class KDMEmailPage : public wxPreferencesPage, public Page
685 {
686 public:
687
688         KDMEmailPage (wxSize panel_size, int border)
689                 : Page (panel_size, border)
690         {}
691         
692         wxString GetName () const
693         {
694                 return _("KDM Email");
695         }
696
697 #ifdef DCPOMATIC_OSX    
698         wxBitmap GetLargeIcon () const
699         {
700                 return wxBitmap ("kdm_email", wxBITMAP_TYPE_PNG_RESOURCE);
701         }
702 #endif  
703
704         wxWindow* CreateWindow (wxWindow* parent)
705         {
706                 /* We have to force both width and height of this one */
707 #ifdef DCPOMATIC_OSX
708                 wxPanel* panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, wxSize (480, 128));
709 #else           
710                 wxPanel* panel = new wxPanel (parent);
711 #endif          
712                 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
713                 panel->SetSizer (s);
714                 
715                 _kdm_email = new wxTextCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (480, 128), wxTE_MULTILINE);
716                 s->Add (_kdm_email, 1, wxEXPAND | wxALL, _border);
717                 
718                 _kdm_email->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&KDMEmailPage::kdm_email_changed, this));
719                 _kdm_email->SetValue (wx_to_std (Config::instance()->kdm_email ()));
720
721                 return panel;
722         }
723
724 private:        
725         void kdm_email_changed ()
726         {
727                 Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
728         }
729
730         wxTextCtrl* _kdm_email;
731 };
732
733 wxPreferencesEditor*
734 create_config_dialog ()
735 {
736         wxPreferencesEditor* e = new wxPreferencesEditor ();
737
738 #ifdef DCPOMATIC_OSX
739         /* Width that we force some of the config panels to be on OSX so that
740            the containing window doesn't shrink too much when we select those panels.
741            This is obviously an unpleasant hack.
742         */
743         wxSize ps = wxSize (480, -1);
744         int const border = 16;
745 #else
746         /* We seem to need to specify height here, otherwise the general panel
747            is too short (at least on Linux).
748          */
749         wxSize ps = wxSize (-1, 400);
750         int const border = 8;
751 #endif
752         
753         e->AddPage (new GeneralPage (ps, border));
754         e->AddPage (new DefaultsPage (ps, border));
755         e->AddPage (new EncodingServersPage (ps, border));
756         e->AddPage (new ColourConversionsPage (ps, border));
757         e->AddPage (new TMSPage (ps, border));
758         e->AddPage (new KDMEmailPage (ps, border));
759         return e;
760 }