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