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