Merge branch 'master' of ssh://houllier/home/carl/git/dvdomatic
[dcpomatic.git] / src / wx / film_editor.cc
1 /*
2     Copyright (C) 2012 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/film_editor.cc
21  *  @brief A wx widget to edit a film's metadata, and perform various functions.
22  */
23
24 #include <iostream>
25 #include <iomanip>
26 #include <wx/wx.h>
27 #include <wx/notebook.h>
28 #include <boost/thread.hpp>
29 #include <boost/filesystem.hpp>
30 #include <boost/lexical_cast.hpp>
31 #include "lib/format.h"
32 #include "lib/film.h"
33 #include "lib/transcode_job.h"
34 #include "lib/exceptions.h"
35 #include "lib/ab_transcode_job.h"
36 #include "lib/job_manager.h"
37 #include "lib/filter.h"
38 #include "lib/config.h"
39 #include "lib/ffmpeg_decoder.h"
40 #include "filter_dialog.h"
41 #include "wx_util.h"
42 #include "film_editor.h"
43 #include "gain_calculator_dialog.h"
44 #include "sound_processor.h"
45 #include "dci_metadata_dialog.h"
46 #include "scaler.h"
47 #include "audio_dialog.h"
48
49 using std::string;
50 using std::cout;
51 using std::stringstream;
52 using std::pair;
53 using std::fixed;
54 using std::setprecision;
55 using std::list;
56 using std::vector;
57 using boost::shared_ptr;
58 using boost::dynamic_pointer_cast;
59
60 /** @param f Film to edit */
61 FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent)
62         : wxPanel (parent)
63         , _film (f)
64         , _generally_sensitive (true)
65         , _audio_dialog (0)
66 {
67         wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
68         SetSizer (s);
69         _notebook = new wxNotebook (this, wxID_ANY);
70         s->Add (_notebook, 1);
71
72         make_film_panel ();
73         _notebook->AddPage (_film_panel, _("Film"), true);
74         make_video_panel ();
75         _notebook->AddPage (_video_panel, _("Video"), false);
76         make_audio_panel ();
77         _notebook->AddPage (_audio_panel, _("Audio"), false);
78         make_subtitle_panel ();
79         _notebook->AddPage (_subtitle_panel, _("Subtitles"), false);
80
81         set_film (_film);
82         connect_to_widgets ();
83
84         JobManager::instance()->ActiveJobsChanged.connect (
85                 bind (&FilmEditor::active_jobs_changed, this, _1)
86                 );
87         
88         setup_visibility ();
89         setup_formats ();
90 }
91
92 void
93 FilmEditor::make_film_panel ()
94 {
95         _film_panel = new wxPanel (_notebook);
96         _film_sizer = new wxBoxSizer (wxVERTICAL);
97         _film_panel->SetSizer (_film_sizer);
98
99         wxGridBagSizer* grid = new wxGridBagSizer (4, 4);
100         _film_sizer->Add (grid, 0, wxALL, 8);
101
102         int r = 0;
103         
104         add_label_to_grid_bag_sizer (grid, _film_panel, _("Name"), wxGBPosition (r, 0));
105         _name = new wxTextCtrl (_film_panel, wxID_ANY);
106         grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
107         ++r;
108         
109         add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Name"), wxGBPosition (r, 0));
110         _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
111         grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
112         ++r;
113
114         _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, _("Use DCI name"));
115         grid->Add (_use_dci_name, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
116         _edit_dci_button = new wxButton (_film_panel, wxID_ANY, _("Details..."));
117         grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan);
118         ++r;
119
120         add_label_to_grid_bag_sizer (grid, _film_panel, _("Content"), wxGBPosition (r, 0));
121         _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), _("Select Content File"), wxT("*.*"));
122         grid->Add (_content, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
123         ++r;
124
125         _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, _("Trust content's header"));
126         video_control (_trust_content_header);
127         grid->Add (_trust_content_header, wxGBPosition (r, 0), wxGBSpan(1, 2));
128         ++r;
129
130         add_label_to_grid_bag_sizer (grid, _film_panel, _("Content Type"), wxGBPosition (r, 0));
131         _dcp_content_type = new wxChoice (_film_panel, wxID_ANY);
132         grid->Add (_dcp_content_type, wxGBPosition (r, 1));
133         ++r;
134
135         video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Original Frame Rate"), wxGBPosition (r, 0)));
136         _source_frame_rate = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
137         grid->Add (video_control (_source_frame_rate), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
138         ++r;
139
140         {
141                 add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Frame Rate"), wxGBPosition (r, 0));
142                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
143                 _dcp_frame_rate = new wxChoice (_film_panel, wxID_ANY);
144                 s->Add (_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL);
145                 _best_dcp_frame_rate = new wxButton (_film_panel, wxID_ANY, _("Use best"));
146                 s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 6);
147                 grid->Add (s, wxGBPosition (r, 1));
148         }
149         ++r;
150
151         _frame_rate_description = new wxStaticText (_film_panel, wxID_ANY, wxT ("\n \n "), wxDefaultPosition, wxDefaultSize);
152         grid->Add (video_control (_frame_rate_description), wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
153         wxFont font = _frame_rate_description->GetFont();
154         font.SetStyle(wxFONTSTYLE_ITALIC);
155         font.SetPointSize(font.GetPointSize() - 1);
156         _frame_rate_description->SetFont(font);
157         ++r;
158         
159         video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Length"), wxGBPosition (r, 0)));
160         _length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
161         grid->Add (video_control (_length), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
162         ++r;
163
164
165         {
166                 video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), wxGBPosition (r, 0)));
167                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
168                 video_control (add_label_to_sizer (s, _film_panel, _("Start")));
169                 _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
170                 s->Add (video_control (_trim_start));
171                 video_control (add_label_to_sizer (s, _film_panel, _("End")));
172                 _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
173                 s->Add (video_control (_trim_end));
174
175                 grid->Add (s, wxGBPosition (r, 1));
176         }
177         ++r;
178
179         video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0)));
180         _trim_type = new wxChoice (_film_panel, wxID_ANY);
181         grid->Add (video_control (_trim_type), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
182         ++r;
183
184         _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
185         video_control (_dcp_ab);
186         grid->Add (_dcp_ab, wxGBPosition (r, 0));
187         ++r;
188
189         /* STILL-only stuff */
190         {
191                 still_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Duration"), wxGBPosition (r, 0)));
192                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
193                 _still_duration = new wxSpinCtrl (_film_panel);
194                 still_control (_still_duration);
195                 s->Add (_still_duration, 1, wxEXPAND);
196                 /// TRANSLATORS: `s' here is an abbreviation for seconds, the unit of time
197                 still_control (add_label_to_sizer (s, _film_panel, _("s")));
198                 grid->Add (s, wxGBPosition (r, 1));
199         }
200         ++r;
201
202         vector<DCPContentType const *> const ct = DCPContentType::all ();
203         for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
204                 _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
205         }
206
207         list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
208         for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
209                 _dcp_frame_rate->Append (std_to_wx (boost::lexical_cast<string> (*i)));
210         }
211
212         _trim_type->Append (_("encode all frames and play the subset"));
213         _trim_type->Append (_("encode only the subset"));
214 }
215
216 void
217 FilmEditor::connect_to_widgets ()
218 {
219         _name->Connect                 (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED,         wxCommandEventHandler (FilmEditor::name_changed), 0, this);
220         _use_dci_name->Connect         (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this);
221         _edit_dci_button->Connect      (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
222         _format->Connect               (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::format_changed), 0, this);
223         _content->Connect              (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED,   wxCommandEventHandler (FilmEditor::content_changed), 0, this);
224         _trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this);
225         _left_crop->Connect            (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
226         _right_crop->Connect           (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this);
227         _top_crop->Connect             (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this);
228         _bottom_crop->Connect          (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this);
229         _filters_button->Connect       (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this);
230         _scaler->Connect               (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
231         _dcp_content_type->Connect     (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
232         _dcp_frame_rate->Connect       (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
233         _best_dcp_frame_rate->Connect  (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
234         _dcp_ab->Connect               (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
235         _still_duration->Connect       (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
236         _trim_start->Connect           (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
237         _trim_end->Connect             (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
238         _trim_type->Connect            (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::trim_type_changed), 0, this);
239         _with_subtitles->Connect       (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
240         _subtitle_offset->Connect      (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
241         _subtitle_scale->Connect       (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this);
242         _colour_lut->Connect           (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
243         _j2k_bandwidth->Connect        (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
244         _subtitle_stream->Connect      (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
245         _audio_stream->Connect         (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
246         _audio_gain->Connect           (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
247         _audio_gain_calculate_button->Connect (
248                 wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this
249                 );
250         _show_audio->Connect           (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::show_audio_clicked), 0, this);
251         _audio_delay->Connect          (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
252         _use_content_audio->Connect    (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
253         _use_external_audio->Connect   (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
254         for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
255                 _external_audio[i]->Connect (
256                         wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::external_audio_changed), 0, this
257                         );
258         }
259 }
260
261 void
262 FilmEditor::make_video_panel ()
263 {
264         _video_panel = new wxPanel (_notebook);
265         _video_sizer = new wxBoxSizer (wxVERTICAL);
266         _video_panel->SetSizer (_video_sizer);
267         
268         wxGridBagSizer* grid = new wxGridBagSizer (4, 4);
269         _video_sizer->Add (grid, 0, wxALL, 8);
270
271         int r = 0;
272         add_label_to_grid_bag_sizer (grid, _video_panel, _("Format"), wxGBPosition (r, 0));
273         _format = new wxChoice (_video_panel, wxID_ANY);
274         grid->Add (_format, wxGBPosition (r, 1));
275         ++r;
276
277         add_label_to_grid_bag_sizer (grid, _video_panel, _("Left crop"), wxGBPosition (r, 0));
278         _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
279         grid->Add (_left_crop, wxGBPosition (r, 1));
280         ++r;
281
282         add_label_to_grid_bag_sizer (grid, _video_panel, _("Right crop"), wxGBPosition (r, 0));
283         _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
284         grid->Add (_right_crop, wxGBPosition (r, 1));
285         ++r;
286         
287         add_label_to_grid_bag_sizer (grid, _video_panel, _("Top crop"), wxGBPosition (r, 0));
288         _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
289         grid->Add (_top_crop, wxGBPosition (r, 1));
290         ++r;
291         
292         add_label_to_grid_bag_sizer (grid, _video_panel, _("Bottom crop"), wxGBPosition (r, 0));
293         _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
294         grid->Add (_bottom_crop, wxGBPosition (r, 1));
295         ++r;
296
297         _scaling_description = new wxStaticText (_video_panel, wxID_ANY, wxT ("\n \n \n \n"), wxDefaultPosition, wxDefaultSize);
298         grid->Add (_scaling_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
299         wxFont font = _scaling_description->GetFont();
300         font.SetStyle(wxFONTSTYLE_ITALIC);
301         font.SetPointSize(font.GetPointSize() - 1);
302         _scaling_description->SetFont(font);
303         ++r;
304
305         /* VIDEO-only stuff */
306         {
307                 video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), wxGBPosition (r, 0)));
308                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
309                 _filters = new wxStaticText (_video_panel, wxID_ANY, _("None"));
310                 video_control (_filters);
311                 s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
312                 _filters_button = new wxButton (_video_panel, wxID_ANY, _("Edit..."));
313                 video_control (_filters_button);
314                 s->Add (_filters_button, 0);
315                 grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
316         }
317         ++r;
318
319         video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Scaler"), wxGBPosition (r, 0)));
320         _scaler = new wxChoice (_video_panel, wxID_ANY);
321         grid->Add (video_control (_scaler), wxGBPosition (r, 1));
322         ++r;
323
324         vector<Scaler const *> const sc = Scaler::all ();
325         for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
326                 _scaler->Append (std_to_wx ((*i)->name()));
327         }
328
329         add_label_to_grid_bag_sizer (grid, _video_panel, _("Colour look-up table"), wxGBPosition (r, 0));
330         _colour_lut = new wxChoice (_video_panel, wxID_ANY);
331         for (int i = 0; i < 2; ++i) {
332                 _colour_lut->Append (std_to_wx (colour_lut_index_to_name (i)));
333         }
334         _colour_lut->SetSelection (0);
335         grid->Add (_colour_lut, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
336         ++r;
337
338         {
339                 add_label_to_grid_bag_sizer (grid, _video_panel, _("JPEG2000 bandwidth"), wxGBPosition (r, 0));
340                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
341                 _j2k_bandwidth = new wxSpinCtrl (_video_panel, wxID_ANY);
342                 s->Add (_j2k_bandwidth, 1);
343                 add_label_to_sizer (s, _video_panel, _("MBps"));
344                 grid->Add (s, wxGBPosition (r, 1));
345         }
346         ++r;
347
348         _left_crop->SetRange (0, 1024);
349         _top_crop->SetRange (0, 1024);
350         _right_crop->SetRange (0, 1024);
351         _bottom_crop->SetRange (0, 1024);
352         _still_duration->SetRange (1, 60 * 60);
353         _trim_start->SetRange (0, 100);
354         _trim_end->SetRange (0, 100);
355         _j2k_bandwidth->SetRange (50, 250);
356 }
357
358 void
359 FilmEditor::make_audio_panel ()
360 {
361         _audio_panel = new wxPanel (_notebook);
362         _audio_sizer = new wxBoxSizer (wxVERTICAL);
363         _audio_panel->SetSizer (_audio_sizer);
364         
365         wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
366         _audio_sizer->Add (grid, 0, wxALL, 8);
367
368         _show_audio = new wxButton (_audio_panel, wxID_ANY, _("Show Audio..."));
369         grid->Add (_show_audio, 1);
370         grid->AddSpacer (0);
371
372         {
373                 video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Gain")));
374                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
375                 _audio_gain = new wxSpinCtrl (_audio_panel);
376                 s->Add (video_control (_audio_gain), 1);
377                 video_control (add_label_to_sizer (s, _audio_panel, _("dB")));
378                 _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate..."));
379                 video_control (_audio_gain_calculate_button);
380                 s->Add (_audio_gain_calculate_button, 1, wxEXPAND);
381                 grid->Add (s);
382         }
383
384         {
385                 video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Delay")));
386                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
387                 _audio_delay = new wxSpinCtrl (_audio_panel);
388                 s->Add (video_control (_audio_delay), 1);
389                 /// TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
390                 video_control (add_label_to_sizer (s, _audio_panel, _("ms")));
391                 grid->Add (s);
392         }
393
394         {
395                 _use_content_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use content's audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
396                 grid->Add (video_control (_use_content_audio));
397                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
398                 _audio_stream = new wxChoice (_audio_panel, wxID_ANY);
399                 s->Add (video_control (_audio_stream), 1);
400                 _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT (""));
401                 s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8);
402                 grid->Add (s, 1, wxEXPAND);
403         }
404
405         _use_external_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use external audio"));
406         grid->Add (_use_external_audio);
407         grid->AddSpacer (0);
408
409         for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
410                 add_label_to_sizer (grid, _audio_panel, std_to_wx (audio_channel_name (i)));
411                 _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), _("Select Audio File"), wxT ("*.wav"));
412                 grid->Add (_external_audio[i], 1, wxEXPAND);
413         }
414
415         _audio_gain->SetRange (-60, 60);
416         _audio_delay->SetRange (-1000, 1000);
417 }
418
419 void
420 FilmEditor::make_subtitle_panel ()
421 {
422         _subtitle_panel = new wxPanel (_notebook);
423         _subtitle_sizer = new wxBoxSizer (wxVERTICAL);
424         _subtitle_panel->SetSizer (_subtitle_sizer);
425         wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
426         _subtitle_sizer->Add (grid, 0, wxALL, 8);
427
428         _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, _("With Subtitles"));
429         video_control (_with_subtitles);
430         grid->Add (_with_subtitles, 1);
431         
432         _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY);
433         grid->Add (video_control (_subtitle_stream));
434
435         {
436                 video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset")));
437                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
438                 _subtitle_offset = new wxSpinCtrl (_subtitle_panel);
439                 s->Add (_subtitle_offset);
440                 video_control (add_label_to_sizer (s, _subtitle_panel, _("pixels")));
441                 grid->Add (s);
442         }
443
444         {
445                 video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale")));
446                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
447                 _subtitle_scale = new wxSpinCtrl (_subtitle_panel);
448                 s->Add (video_control (_subtitle_scale));
449                 video_control (add_label_to_sizer (s, _subtitle_panel, _("%")));
450                 grid->Add (s);
451         }
452
453         _subtitle_offset->SetRange (-1024, 1024);
454         _subtitle_scale->SetRange (1, 1000);
455 }
456
457 /** Called when the left crop widget has been changed */
458 void
459 FilmEditor::left_crop_changed (wxCommandEvent &)
460 {
461         if (!_film) {
462                 return;
463         }
464
465         _film->set_left_crop (_left_crop->GetValue ());
466 }
467
468 /** Called when the right crop widget has been changed */
469 void
470 FilmEditor::right_crop_changed (wxCommandEvent &)
471 {
472         if (!_film) {
473                 return;
474         }
475
476         _film->set_right_crop (_right_crop->GetValue ());
477 }
478
479 /** Called when the top crop widget has been changed */
480 void
481 FilmEditor::top_crop_changed (wxCommandEvent &)
482 {
483         if (!_film) {
484                 return;
485         }
486
487         _film->set_top_crop (_top_crop->GetValue ());
488 }
489
490 /** Called when the bottom crop value has been changed */
491 void
492 FilmEditor::bottom_crop_changed (wxCommandEvent &)
493 {
494         if (!_film) {
495                 return;
496         }
497
498         _film->set_bottom_crop (_bottom_crop->GetValue ());
499 }
500
501 /** Called when the content filename has been changed */
502 void
503 FilmEditor::content_changed (wxCommandEvent &)
504 {
505         if (!_film) {
506                 return;
507         }
508
509         try {
510                 _film->set_content (wx_to_std (_content->GetPath ()));
511         } catch (std::exception& e) {
512                 _content->SetPath (std_to_wx (_film->directory ()));
513                 error_dialog (this, wxString::Format (_("Could not set content: %s"), std_to_wx (e.what()).data()));
514         }
515 }
516
517 void
518 FilmEditor::trust_content_header_changed (wxCommandEvent &)
519 {
520         if (!_film) {
521                 return;
522         }
523
524         _film->set_trust_content_header (_trust_content_header->GetValue ());
525 }
526
527 /** Called when the DCP A/B switch has been toggled */
528 void
529 FilmEditor::dcp_ab_toggled (wxCommandEvent &)
530 {
531         if (!_film) {
532                 return;
533         }
534         
535         _film->set_dcp_ab (_dcp_ab->GetValue ());
536 }
537
538 /** Called when the name widget has been changed */
539 void
540 FilmEditor::name_changed (wxCommandEvent &)
541 {
542         if (!_film) {
543                 return;
544         }
545
546         _film->set_name (string (_name->GetValue().mb_str()));
547 }
548
549 void
550 FilmEditor::subtitle_offset_changed (wxCommandEvent &)
551 {
552         if (!_film) {
553                 return;
554         }
555
556         _film->set_subtitle_offset (_subtitle_offset->GetValue ());
557 }
558
559 void
560 FilmEditor::subtitle_scale_changed (wxCommandEvent &)
561 {
562         if (!_film) {
563                 return;
564         }
565
566         _film->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0);
567 }
568
569 void
570 FilmEditor::colour_lut_changed (wxCommandEvent &)
571 {
572         if (!_film) {
573                 return;
574         }
575         
576         _film->set_colour_lut (_colour_lut->GetSelection ());
577 }
578
579 void
580 FilmEditor::j2k_bandwidth_changed (wxCommandEvent &)
581 {
582         if (!_film) {
583                 return;
584         }
585         
586         _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1e6);
587 }
588
589 void
590 FilmEditor::dcp_frame_rate_changed (wxCommandEvent &)
591 {
592         if (!_film) {
593                 return;
594         }
595
596         _film->set_dcp_frame_rate (
597                 boost::lexical_cast<int> (
598                         wx_to_std (_dcp_frame_rate->GetString (_dcp_frame_rate->GetSelection ()))
599                         )
600                 );
601 }
602
603
604 /** Called when the metadata stored in the Film object has changed;
605  *  so that we can update the GUI.
606  *  @param p Property of the Film that has changed.
607  */
608 void
609 FilmEditor::film_changed (Film::Property p)
610 {
611         ensure_ui_thread ();
612         
613         if (!_film) {
614                 return;
615         }
616
617         stringstream s;
618                 
619         switch (p) {
620         case Film::NONE:
621                 break;
622         case Film::CONTENT:
623                 checked_set (_content, _film->content ());
624                 setup_visibility ();
625                 setup_formats ();
626                 setup_subtitle_control_sensitivity ();
627                 setup_streams ();
628                 setup_show_audio_sensitivity ();
629                 setup_frame_rate_description ();
630                 break;
631         case Film::TRUST_CONTENT_HEADER:
632                 checked_set (_trust_content_header, _film->trust_content_header ());
633                 break;
634         case Film::SUBTITLE_STREAMS:
635                 setup_subtitle_control_sensitivity ();
636                 setup_streams ();
637                 break;
638         case Film::CONTENT_AUDIO_STREAMS:
639                 setup_streams ();
640                 setup_show_audio_sensitivity ();
641                 setup_frame_rate_description ();
642                 break;
643         case Film::FORMAT:
644         {
645                 int n = 0;
646                 vector<Format const *>::iterator i = _formats.begin ();
647                 while (i != _formats.end() && *i != _film->format ()) {
648                         ++i;
649                         ++n;
650                 }
651                 if (i == _formats.end()) {
652                         checked_set (_format, -1);
653                 } else {
654                         checked_set (_format, n);
655                 }
656                 setup_dcp_name ();
657                 setup_scaling_description ();
658                 break;
659         }
660         case Film::CROP:
661                 checked_set (_left_crop, _film->crop().left);
662                 checked_set (_right_crop, _film->crop().right);
663                 checked_set (_top_crop, _film->crop().top);
664                 checked_set (_bottom_crop, _film->crop().bottom);
665                 setup_scaling_description ();
666                 break;
667         case Film::FILTERS:
668         {
669                 pair<string, string> p = Filter::ffmpeg_strings (_film->filters ());
670                 if (p.first.empty () && p.second.empty ()) {
671                         _filters->SetLabel (_("None"));
672                 } else {
673                         string const b = p.first + " " + p.second;
674                         _filters->SetLabel (std_to_wx (b));
675                 }
676                 _film_sizer->Layout ();
677                 break;
678         }
679         case Film::NAME:
680                 checked_set (_name, _film->name());
681                 setup_dcp_name ();
682                 break;
683         case Film::SOURCE_FRAME_RATE:
684                 s << fixed << setprecision(2) << _film->source_frame_rate();
685                 _source_frame_rate->SetLabel (std_to_wx (s.str ()));
686                 setup_frame_rate_description ();
687                 break;
688         case Film::SIZE:
689                 setup_scaling_description ();
690                 break;
691         case Film::LENGTH:
692                 if (_film->source_frame_rate() > 0 && _film->length()) {
693                         s << _film->length().get() << " "
694                           << wx_to_std (_("frames")) << "; " << seconds_to_hms (_film->length().get() / _film->source_frame_rate());
695                 } else if (_film->length()) {
696                         s << _film->length().get() << " "
697                           << wx_to_std (_("frames"));
698                 } 
699                 _length->SetLabel (std_to_wx (s.str ()));
700                 if (_film->length()) {
701                         _trim_start->SetRange (0, _film->length().get());
702                         _trim_end->SetRange (0, _film->length().get());
703                 }
704                 break;
705         case Film::DCP_CONTENT_TYPE:
706                 checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
707                 setup_dcp_name ();
708                 break;
709         case Film::DCP_AB:
710                 checked_set (_dcp_ab, _film->dcp_ab ());
711                 break;
712         case Film::SCALER:
713                 checked_set (_scaler, Scaler::as_index (_film->scaler ()));
714                 break;
715         case Film::TRIM_START:
716                 checked_set (_trim_start, _film->trim_start());
717                 break;
718         case Film::TRIM_END:
719                 checked_set (_trim_end, _film->trim_end());
720                 break;
721         case Film::TRIM_TYPE:
722                 checked_set (_trim_type, _film->trim_type() == Film::CPL ? 0 : 1);
723                 break;
724         case Film::AUDIO_GAIN:
725                 checked_set (_audio_gain, _film->audio_gain ());
726                 break;
727         case Film::AUDIO_DELAY:
728                 checked_set (_audio_delay, _film->audio_delay ());
729                 break;
730         case Film::STILL_DURATION:
731                 checked_set (_still_duration, _film->still_duration ());
732                 break;
733         case Film::WITH_SUBTITLES:
734                 checked_set (_with_subtitles, _film->with_subtitles ());
735                 setup_subtitle_control_sensitivity ();
736                 setup_dcp_name ();
737                 break;
738         case Film::SUBTITLE_OFFSET:
739                 checked_set (_subtitle_offset, _film->subtitle_offset ());
740                 break;
741         case Film::SUBTITLE_SCALE:
742                 checked_set (_subtitle_scale, _film->subtitle_scale() * 100);
743                 break;
744         case Film::COLOUR_LUT:
745                 checked_set (_colour_lut, _film->colour_lut ());
746                 break;
747         case Film::J2K_BANDWIDTH:
748                 checked_set (_j2k_bandwidth, double (_film->j2k_bandwidth()) / 1e6);
749                 break;
750         case Film::USE_DCI_NAME:
751                 checked_set (_use_dci_name, _film->use_dci_name ());
752                 setup_dcp_name ();
753                 break;
754         case Film::DCI_METADATA:
755                 setup_dcp_name ();
756                 break;
757         case Film::CONTENT_AUDIO_STREAM:
758                 if (_film->content_audio_stream()) {
759                         checked_set (_audio_stream, _film->content_audio_stream()->to_string());
760                 }
761                 setup_dcp_name ();
762                 setup_audio_details ();
763                 setup_audio_control_sensitivity ();
764                 setup_show_audio_sensitivity ();
765                 setup_frame_rate_description ();
766                 break;
767         case Film::USE_CONTENT_AUDIO:
768                 checked_set (_use_content_audio, _film->use_content_audio());
769                 checked_set (_use_external_audio, !_film->use_content_audio());
770                 setup_dcp_name ();
771                 setup_audio_details ();
772                 setup_audio_control_sensitivity ();
773                 setup_show_audio_sensitivity ();
774                 setup_frame_rate_description ();
775                 break;
776         case Film::SUBTITLE_STREAM:
777                 if (_film->subtitle_stream()) {
778                         checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
779                 }
780                 break;
781         case Film::EXTERNAL_AUDIO:
782         {
783                 vector<string> a = _film->external_audio ();
784                 for (size_t i = 0; i < a.size() && i < MAX_AUDIO_CHANNELS; ++i) {
785                         checked_set (_external_audio[i], a[i]);
786                 }
787                 setup_audio_details ();
788                 setup_show_audio_sensitivity ();
789                 setup_frame_rate_description ();
790                 break;
791         }
792         case Film::DCP_FRAME_RATE:
793                 for (unsigned int i = 0; i < _dcp_frame_rate->GetCount(); ++i) {
794                         if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast<string> (_film->dcp_frame_rate())) {
795                                 if (_dcp_frame_rate->GetSelection() != int(i)) {
796                                         _dcp_frame_rate->SetSelection (i);
797                                         break;
798                                 }
799                         }
800                 }
801
802                 if (_film->source_frame_rate()) {
803                         _best_dcp_frame_rate->Enable (best_dcp_frame_rate (_film->source_frame_rate ()) != _film->dcp_frame_rate ());
804                 } else {
805                         _best_dcp_frame_rate->Disable ();
806                 }
807
808                 setup_frame_rate_description ();
809         }
810 }
811
812 void
813 FilmEditor::setup_frame_rate_description ()
814 {
815         wxString d;
816         int lines = 0;
817         
818         if (_film->source_frame_rate()) {
819                 d << std_to_wx (FrameRateConversion (_film->source_frame_rate(), _film->dcp_frame_rate()).description);
820                 ++lines;
821 #ifdef HAVE_SWRESAMPLE
822                 if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate ()) {
823                         d << wxString::Format (
824                                 _("Audio will be resampled from %dHz to %dHz\n"),
825                                 _film->audio_stream()->sample_rate(),
826                                 _film->target_audio_sample_rate()
827                                 );
828                         ++lines;
829                 }
830 #endif          
831         }
832
833         for (int i = lines; i < 2; ++i) {
834                 d << wxT ("\n ");
835         }
836
837         _frame_rate_description->SetLabel (d);
838 }
839
840 /** Called when the format widget has been changed */
841 void
842 FilmEditor::format_changed (wxCommandEvent &)
843 {
844         if (!_film) {
845                 return;
846         }
847
848         int const n = _format->GetSelection ();
849         if (n >= 0) {
850                 assert (n < int (_formats.size()));
851                 _film->set_format (_formats[n]);
852         }
853 }
854
855 /** Called when the DCP content type widget has been changed */
856 void
857 FilmEditor::dcp_content_type_changed (wxCommandEvent &)
858 {
859         if (!_film) {
860                 return;
861         }
862
863         int const n = _dcp_content_type->GetSelection ();
864         if (n != wxNOT_FOUND) {
865                 _film->set_dcp_content_type (DCPContentType::from_index (n));
866         }
867 }
868
869 /** Sets the Film that we are editing */
870 void
871 FilmEditor::set_film (shared_ptr<Film> f)
872 {
873         _film = f;
874
875         set_things_sensitive (_film != 0);
876
877         if (_film) {
878                 _film->Changed.connect (bind (&FilmEditor::film_changed, this, _1));
879         }
880
881         if (_film) {
882                 FileChanged (_film->directory ());
883         } else {
884                 FileChanged ("");
885         }
886
887         if (_audio_dialog) {
888                 _audio_dialog->set_film (_film);
889         }
890         
891         film_changed (Film::NAME);
892         film_changed (Film::USE_DCI_NAME);
893         film_changed (Film::CONTENT);
894         film_changed (Film::TRUST_CONTENT_HEADER);
895         film_changed (Film::DCP_CONTENT_TYPE);
896         film_changed (Film::FORMAT);
897         film_changed (Film::CROP);
898         film_changed (Film::FILTERS);
899         film_changed (Film::SCALER);
900         film_changed (Film::TRIM_START);
901         film_changed (Film::TRIM_END);
902         film_changed (Film::TRIM_TYPE);
903         film_changed (Film::DCP_AB);
904         film_changed (Film::CONTENT_AUDIO_STREAM);
905         film_changed (Film::EXTERNAL_AUDIO);
906         film_changed (Film::USE_CONTENT_AUDIO);
907         film_changed (Film::AUDIO_GAIN);
908         film_changed (Film::AUDIO_DELAY);
909         film_changed (Film::STILL_DURATION);
910         film_changed (Film::WITH_SUBTITLES);
911         film_changed (Film::SUBTITLE_OFFSET);
912         film_changed (Film::SUBTITLE_SCALE);
913         film_changed (Film::COLOUR_LUT);
914         film_changed (Film::J2K_BANDWIDTH);
915         film_changed (Film::DCI_METADATA);
916         film_changed (Film::SIZE);
917         film_changed (Film::LENGTH);
918         film_changed (Film::CONTENT_AUDIO_STREAMS);
919         film_changed (Film::SUBTITLE_STREAMS);
920         film_changed (Film::SOURCE_FRAME_RATE);
921         film_changed (Film::DCP_FRAME_RATE);
922 }
923
924 /** Updates the sensitivity of lots of widgets to a given value.
925  *  @param s true to make sensitive, false to make insensitive.
926  */
927 void
928 FilmEditor::set_things_sensitive (bool s)
929 {
930         _generally_sensitive = s;
931         
932         _name->Enable (s);
933         _use_dci_name->Enable (s);
934         _edit_dci_button->Enable (s);
935         _format->Enable (s);
936         _content->Enable (s);
937         _trust_content_header->Enable (s);
938         _left_crop->Enable (s);
939         _right_crop->Enable (s);
940         _top_crop->Enable (s);
941         _bottom_crop->Enable (s);
942         _filters_button->Enable (s);
943         _scaler->Enable (s);
944         _audio_stream->Enable (s);
945         _dcp_content_type->Enable (s);
946         _dcp_frame_rate->Enable (s);
947         _trim_start->Enable (s);
948         _trim_end->Enable (s);
949         _trim_type->Enable (s);
950         _dcp_ab->Enable (s);
951         _colour_lut->Enable (s);
952         _j2k_bandwidth->Enable (s);
953         _audio_gain->Enable (s);
954         _audio_gain_calculate_button->Enable (s);
955         _show_audio->Enable (s);
956         _audio_delay->Enable (s);
957         _still_duration->Enable (s);
958
959         setup_subtitle_control_sensitivity ();
960         setup_audio_control_sensitivity ();
961         setup_show_audio_sensitivity ();
962 }
963
964 /** Called when the `Edit filters' button has been clicked */
965 void
966 FilmEditor::edit_filters_clicked (wxCommandEvent &)
967 {
968         FilterDialog* d = new FilterDialog (this, _film->filters());
969         d->ActiveChanged.connect (bind (&Film::set_filters, _film, _1));
970         d->ShowModal ();
971         d->Destroy ();
972 }
973
974 /** Called when the scaler widget has been changed */
975 void
976 FilmEditor::scaler_changed (wxCommandEvent &)
977 {
978         if (!_film) {
979                 return;
980         }
981
982         int const n = _scaler->GetSelection ();
983         if (n >= 0) {
984                 _film->set_scaler (Scaler::from_index (n));
985         }
986 }
987
988 void
989 FilmEditor::audio_gain_changed (wxCommandEvent &)
990 {
991         if (!_film) {
992                 return;
993         }
994
995         _film->set_audio_gain (_audio_gain->GetValue ());
996 }
997
998 void
999 FilmEditor::audio_delay_changed (wxCommandEvent &)
1000 {
1001         if (!_film) {
1002                 return;
1003         }
1004
1005         _film->set_audio_delay (_audio_delay->GetValue ());
1006 }
1007
1008 wxControl *
1009 FilmEditor::video_control (wxControl* c)
1010 {
1011         _video_controls.push_back (c);
1012         return c;
1013 }
1014
1015 wxControl *
1016 FilmEditor::still_control (wxControl* c)
1017 {
1018         _still_controls.push_back (c);
1019         return c;
1020 }
1021
1022 void
1023 FilmEditor::setup_visibility ()
1024 {
1025         ContentType c = VIDEO;
1026
1027         if (_film) {
1028                 c = _film->content_type ();
1029         }
1030
1031         for (list<wxControl*>::iterator i = _video_controls.begin(); i != _video_controls.end(); ++i) {
1032                 (*i)->Show (c == VIDEO);
1033         }
1034
1035         for (list<wxControl*>::iterator i = _still_controls.begin(); i != _still_controls.end(); ++i) {
1036                 (*i)->Show (c == STILL);
1037         }
1038
1039         setup_notebook_size ();
1040 }
1041
1042 void
1043 FilmEditor::setup_notebook_size ()
1044 {
1045         _notebook->InvalidateBestSize ();
1046         
1047         _film_sizer->Layout ();
1048         _film_sizer->SetSizeHints (_film_panel);
1049         _video_sizer->Layout ();
1050         _video_sizer->SetSizeHints (_video_panel);
1051         _audio_sizer->Layout ();
1052         _audio_sizer->SetSizeHints (_audio_panel);
1053         _subtitle_sizer->Layout ();
1054         _subtitle_sizer->SetSizeHints (_subtitle_panel);
1055
1056         _notebook->Fit ();
1057         Fit ();
1058 }
1059
1060 void
1061 FilmEditor::still_duration_changed (wxCommandEvent &)
1062 {
1063         if (!_film) {
1064                 return;
1065         }
1066
1067         _film->set_still_duration (_still_duration->GetValue ());
1068 }
1069
1070 void
1071 FilmEditor::trim_start_changed (wxCommandEvent &)
1072 {
1073         if (!_film) {
1074                 return;
1075         }
1076
1077         _film->set_trim_start (_trim_start->GetValue ());
1078 }
1079
1080 void
1081 FilmEditor::trim_end_changed (wxCommandEvent &)
1082 {
1083         if (!_film) {
1084                 return;
1085         }
1086
1087         _film->set_trim_end (_trim_end->GetValue ());
1088 }
1089
1090 void
1091 FilmEditor::audio_gain_calculate_button_clicked (wxCommandEvent &)
1092 {
1093         GainCalculatorDialog* d = new GainCalculatorDialog (this);
1094         d->ShowModal ();
1095
1096         if (d->wanted_fader() == 0 || d->actual_fader() == 0) {
1097                 d->Destroy ();
1098                 return;
1099         }
1100         
1101         _audio_gain->SetValue (
1102                 Config::instance()->sound_processor()->db_for_fader_change (
1103                         d->wanted_fader (),
1104                         d->actual_fader ()
1105                         )
1106                 );
1107
1108         /* This appears to be necessary, as the change is not signalled,
1109            I think.
1110         */
1111         wxCommandEvent dummy;
1112         audio_gain_changed (dummy);
1113         
1114         d->Destroy ();
1115 }
1116
1117 void
1118 FilmEditor::setup_formats ()
1119 {
1120         ContentType c = VIDEO;
1121
1122         if (_film) {
1123                 c = _film->content_type ();
1124         }
1125         
1126         _formats.clear ();
1127
1128         vector<Format const *> fmt = Format::all ();
1129         for (vector<Format const *>::iterator i = fmt.begin(); i != fmt.end(); ++i) {
1130                 if (c == VIDEO || (c == STILL && dynamic_cast<VariableFormat const *> (*i))) {
1131                         _formats.push_back (*i);
1132                 }
1133         }
1134
1135         _format->Clear ();
1136         for (vector<Format const *>::iterator i = _formats.begin(); i != _formats.end(); ++i) {
1137                 _format->Append (std_to_wx ((*i)->name ()));
1138         }
1139
1140         _film_sizer->Layout ();
1141 }
1142
1143 void
1144 FilmEditor::with_subtitles_toggled (wxCommandEvent &)
1145 {
1146         if (!_film) {
1147                 return;
1148         }
1149
1150         _film->set_with_subtitles (_with_subtitles->GetValue ());
1151 }
1152
1153 void
1154 FilmEditor::setup_subtitle_control_sensitivity ()
1155 {
1156         bool h = false;
1157         if (_generally_sensitive && _film) {
1158                 h = !_film->subtitle_streams().empty();
1159         }
1160         
1161         _with_subtitles->Enable (h);
1162
1163         bool j = false;
1164         if (_film) {
1165                 j = _film->with_subtitles ();
1166         }
1167         
1168         _subtitle_stream->Enable (j);
1169         _subtitle_offset->Enable (j);
1170         _subtitle_scale->Enable (j);
1171 }
1172
1173 void
1174 FilmEditor::setup_audio_control_sensitivity ()
1175 {
1176         _use_content_audio->Enable (_generally_sensitive && _film && !_film->content_audio_streams().empty());
1177         _use_external_audio->Enable (_generally_sensitive);
1178         
1179         bool const source = _generally_sensitive && _use_content_audio->GetValue();
1180         bool const external = _generally_sensitive && _use_external_audio->GetValue();
1181
1182         _audio_stream->Enable (source);
1183         for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
1184                 _external_audio[i]->Enable (external);
1185         }
1186 }
1187
1188 void
1189 FilmEditor::use_dci_name_toggled (wxCommandEvent &)
1190 {
1191         if (!_film) {
1192                 return;
1193         }
1194
1195         _film->set_use_dci_name (_use_dci_name->GetValue ());
1196 }
1197
1198 void
1199 FilmEditor::edit_dci_button_clicked (wxCommandEvent &)
1200 {
1201         if (!_film) {
1202                 return;
1203         }
1204
1205         DCIMetadataDialog* d = new DCIMetadataDialog (this, _film->dci_metadata ());
1206         d->ShowModal ();
1207         _film->set_dci_metadata (d->dci_metadata ());
1208         d->Destroy ();
1209 }
1210
1211 void
1212 FilmEditor::setup_streams ()
1213 {
1214         _audio_stream->Clear ();
1215         vector<shared_ptr<AudioStream> > a = _film->content_audio_streams ();
1216         for (vector<shared_ptr<AudioStream> >::iterator i = a.begin(); i != a.end(); ++i) {
1217                 shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (*i);
1218                 assert (ffa);
1219                 _audio_stream->Append (std_to_wx (ffa->name()), new wxStringClientData (std_to_wx (ffa->to_string ())));
1220         }
1221         
1222         if (_film->use_content_audio() && _film->audio_stream()) {
1223                 checked_set (_audio_stream, _film->audio_stream()->to_string());
1224         }
1225
1226         _subtitle_stream->Clear ();
1227         vector<shared_ptr<SubtitleStream> > s = _film->subtitle_streams ();
1228         for (vector<shared_ptr<SubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
1229                 _subtitle_stream->Append (std_to_wx ((*i)->name()), new wxStringClientData (std_to_wx ((*i)->to_string ())));
1230         }
1231         if (_film->subtitle_stream()) {
1232                 checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
1233         } else {
1234                 _subtitle_stream->SetSelection (wxNOT_FOUND);
1235         }
1236 }
1237
1238 void
1239 FilmEditor::audio_stream_changed (wxCommandEvent &)
1240 {
1241         if (!_film) {
1242                 return;
1243         }
1244
1245         _film->set_content_audio_stream (
1246                 audio_stream_factory (
1247                         string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())),
1248                         Film::state_version
1249                         )
1250                 );
1251 }
1252
1253 void
1254 FilmEditor::subtitle_stream_changed (wxCommandEvent &)
1255 {
1256         if (!_film) {
1257                 return;
1258         }
1259
1260         _film->set_subtitle_stream (
1261                 subtitle_stream_factory (
1262                         string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())),
1263                         Film::state_version
1264                         )
1265                 );
1266 }
1267
1268 void
1269 FilmEditor::setup_audio_details ()
1270 {
1271         if (!_film->content_audio_stream()) {
1272                 _audio->SetLabel (wxT (""));
1273         } else {
1274                 wxString s;
1275                 if (_film->audio_stream()->channels() == 1) {
1276                         s << _("1 channel");
1277                 } else {
1278                         s << _film->audio_stream()->channels () << wxT (" ") << _("channels");
1279                 }
1280                 s << wxT (", ") << _film->audio_stream()->sample_rate() << _("Hz");
1281                 _audio->SetLabel (s);
1282         }
1283
1284         setup_notebook_size ();
1285 }
1286
1287 void
1288 FilmEditor::active_jobs_changed (bool a)
1289 {
1290         set_things_sensitive (!a);
1291 }
1292
1293 void
1294 FilmEditor::use_audio_changed (wxCommandEvent &)
1295 {
1296         _film->set_use_content_audio (_use_content_audio->GetValue());
1297 }
1298
1299 void
1300 FilmEditor::external_audio_changed (wxCommandEvent &)
1301 {
1302         vector<string> a;
1303         for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
1304                 a.push_back (wx_to_std (_external_audio[i]->GetPath()));
1305         }
1306
1307         _film->set_external_audio (a);
1308 }
1309
1310 void
1311 FilmEditor::setup_dcp_name ()
1312 {
1313         string s = _film->dcp_name (true);
1314         if (s.length() > 28) {
1315                 _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("..."));
1316                 _dcp_name->SetToolTip (std_to_wx (s));
1317         } else {
1318                 _dcp_name->SetLabel (std_to_wx (s));
1319         }
1320 }
1321
1322 void
1323 FilmEditor::show_audio_clicked (wxCommandEvent &)
1324 {
1325         if (_audio_dialog) {
1326                 _audio_dialog->Destroy ();
1327                 _audio_dialog = 0;
1328         }
1329         
1330         _audio_dialog = new AudioDialog (this);
1331         _audio_dialog->Show ();
1332         _audio_dialog->set_film (_film);
1333 }
1334
1335 void
1336 FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &)
1337 {
1338         if (!_film) {
1339                 return;
1340         }
1341         
1342         _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->source_frame_rate ()));
1343 }
1344
1345 void
1346 FilmEditor::setup_show_audio_sensitivity ()
1347 {
1348         _show_audio->Enable (_film && _film->has_audio ());
1349 }
1350
1351 void
1352 FilmEditor::setup_scaling_description ()
1353 {
1354         wxString d;
1355
1356         int lines = 0;
1357
1358         if (_film->size().width && _film->size().height) {
1359                 d << wxString::Format (
1360                         _("Original video is %dx%d (%.2f:1)\n"),
1361                         _film->size().width, _film->size().height,
1362                         float (_film->size().width) / _film->size().height
1363                         );
1364                 ++lines;
1365         }
1366
1367         Crop const crop = _film->crop ();
1368         if (crop.left || crop.right || crop.top || crop.bottom) {
1369                 libdcp::Size const cropped = _film->cropped_size (_film->size ());
1370                 d << wxString::Format (
1371                         _("Cropped to %dx%d (%.2f:1)\n"),
1372                         cropped.width, cropped.height,
1373                         float (cropped.width) / cropped.height
1374                         );
1375                 ++lines;
1376         }
1377
1378         Format const * format = _film->format ();
1379         if (format) {
1380                 int const padding = format->dcp_padding (_film);
1381                 libdcp::Size scaled = format->dcp_size ();
1382                 scaled.width -= padding * 2;
1383                 d << wxString::Format (
1384                         _("Scaled to %dx%d (%.2f:1)\n"),
1385                         scaled.width, scaled.height,
1386                         float (scaled.width) / scaled.height
1387                         );
1388                 ++lines;
1389
1390                 if (padding) {
1391                         d << wxString::Format (
1392                                 _("Padded with black to %dx%d (%.2f:1)\n"),
1393                                 format->dcp_size().width, format->dcp_size().height,
1394                                 float (format->dcp_size().width) / format->dcp_size().height
1395                                 );
1396                         ++lines;
1397                 }
1398         }
1399
1400         for (int i = lines; i < 4; ++i) {
1401                 d << wxT ("\n ");
1402         }
1403
1404         _scaling_description->SetLabel (d);
1405 }
1406
1407 void
1408 FilmEditor::trim_type_changed (wxCommandEvent &)
1409 {
1410         _film->set_trim_type (_trim_type->GetSelection () == 0 ? Film::CPL : Film::ENCODE);
1411 }