Basic UI.
[dcpomatic.git] / src / wx / video_panel.cc
1 /*
2     Copyright (C) 2012-2013 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 #include <wx/spinctrl.h>
21 #include "lib/ratio.h"
22 #include "lib/filter.h"
23 #include "filter_dialog.h"
24 #include "video_panel.h"
25 #include "wx_util.h"
26 #include "film_editor.h"
27
28 using std::vector;
29 using std::string;
30 using std::pair;
31 using boost::shared_ptr;
32 using boost::dynamic_pointer_cast;
33 using boost::bind;
34
35 VideoPanel::VideoPanel (FilmEditor* e)
36         : FilmEditorPanel (e, _("Video"))
37 {
38         wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
39         _sizer->Add (grid, 0, wxALL, 8);
40
41         int r = 0;
42
43         add_label_to_grid_bag_sizer (grid, this, _("Type"), true, wxGBPosition (r, 0));
44         _frame_type = new wxChoice (this, wxID_ANY);
45         grid->Add (_frame_type, wxGBPosition (r, 1));
46         ++r;
47         
48         add_label_to_grid_bag_sizer (grid, this, _("Left crop"), true, wxGBPosition (r, 0));
49         _left_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
50         grid->Add (_left_crop, wxGBPosition (r, 1));
51         ++r;
52
53         add_label_to_grid_bag_sizer (grid, this, _("Right crop"), true, wxGBPosition (r, 0));
54         _right_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
55         grid->Add (_right_crop, wxGBPosition (r, 1));
56         ++r;
57         
58         add_label_to_grid_bag_sizer (grid, this, _("Top crop"), true, wxGBPosition (r, 0));
59         _top_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
60         grid->Add (_top_crop, wxGBPosition (r, 1));
61         ++r;
62         
63         add_label_to_grid_bag_sizer (grid, this, _("Bottom crop"), true, wxGBPosition (r, 0));
64         _bottom_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
65         grid->Add (_bottom_crop, wxGBPosition (r, 1));
66         ++r;
67
68         add_label_to_grid_bag_sizer (grid, this, _("Scale to"), true, wxGBPosition (r, 0));
69         _ratio = new wxChoice (this, wxID_ANY);
70         grid->Add (_ratio, wxGBPosition (r, 1));
71         ++r;
72
73         _scaling_description = new wxStaticText (this, wxID_ANY, wxT ("\n \n \n \n"), wxDefaultPosition, wxDefaultSize);
74         grid->Add (_scaling_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
75         wxFont font = _scaling_description->GetFont();
76         font.SetStyle(wxFONTSTYLE_ITALIC);
77         font.SetPointSize(font.GetPointSize() - 1);
78         _scaling_description->SetFont(font);
79         ++r;
80
81         {
82                 add_label_to_grid_bag_sizer (grid, this, _("Filters"), true, wxGBPosition (r, 0));
83                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
84                 _filters = new wxStaticText (this, wxID_ANY, _("None"));
85                 s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
86                 _filters_button = new wxButton (this, wxID_ANY, _("Edit..."));
87                 s->Add (_filters_button, 0, wxALIGN_CENTER_VERTICAL);
88                 grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
89         }
90         ++r;
91
92         _left_crop->SetRange (0, 1024);
93         _top_crop->SetRange (0, 1024);
94         _right_crop->SetRange (0, 1024);
95         _bottom_crop->SetRange (0, 1024);
96
97         vector<Ratio const *> ratios = Ratio::all ();
98         _ratio->Clear ();
99         for (vector<Ratio const *>::iterator i = ratios.begin(); i != ratios.end(); ++i) {
100                 _ratio->Append (std_to_wx ((*i)->nickname ()));
101         }
102
103         _frame_type->Append (_("2D"));
104         _frame_type->Append (_("3D left/right"));
105
106         _frame_type->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, bind (&VideoPanel::frame_type_changed, this));
107         _left_crop->Connect      (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (VideoPanel::left_crop_changed), 0, this);
108         _right_crop->Connect     (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (VideoPanel::right_crop_changed), 0, this);
109         _top_crop->Connect       (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (VideoPanel::top_crop_changed), 0, this);
110         _bottom_crop->Connect    (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (VideoPanel::bottom_crop_changed), 0, this);
111         _ratio->Connect          (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,  wxCommandEventHandler (VideoPanel::ratio_changed), 0, this);
112         _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,   wxCommandEventHandler (VideoPanel::edit_filters_clicked), 0, this);
113 }
114
115
116 /** Called when the left crop widget has been changed */
117 void
118 VideoPanel::left_crop_changed (wxCommandEvent &)
119 {
120         shared_ptr<VideoContent> c = _editor->selected_video_content ();
121         if (!c) {
122                 return;
123         }
124
125         c->set_left_crop (_left_crop->GetValue ());
126 }
127
128 /** Called when the right crop widget has been changed */
129 void
130 VideoPanel::right_crop_changed (wxCommandEvent &)
131 {
132         shared_ptr<VideoContent> c = _editor->selected_video_content ();
133         if (!c) {
134                 return;
135         }
136
137         c->set_right_crop (_right_crop->GetValue ());
138 }
139
140 /** Called when the top crop widget has been changed */
141 void
142 VideoPanel::top_crop_changed (wxCommandEvent &)
143 {
144         shared_ptr<VideoContent> c = _editor->selected_video_content ();
145         if (!c) {
146                 return;
147         }
148
149         c->set_top_crop (_top_crop->GetValue ());
150 }
151
152 /** Called when the bottom crop value has been changed */
153 void
154 VideoPanel::bottom_crop_changed (wxCommandEvent &)
155 {
156         shared_ptr<VideoContent> c = _editor->selected_video_content ();
157         if (!c) {
158                 return;
159         }
160
161         c->set_bottom_crop (_bottom_crop->GetValue ());
162 }
163
164 void
165 VideoPanel::film_changed (Film::Property property)
166 {
167         switch (property) {
168         case Film::CONTAINER:
169                 setup_scaling_description ();
170                 break;
171         default:
172                 break;
173         }
174 }
175
176 void
177 VideoPanel::film_content_changed (shared_ptr<Content> c, int property)
178 {
179         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c);
180         shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c);
181
182         if (property == VideoContentProperty::VIDEO_FRAME_TYPE) {
183                 checked_set (_frame_type, vc->video_frame_type ());
184         } else if (property == VideoContentProperty::VIDEO_CROP) {
185                 checked_set (_left_crop,   vc ? vc->crop().left : 0);
186                 checked_set (_right_crop,  vc ? vc->crop().right        : 0);
187                 checked_set (_top_crop,    vc ? vc->crop().top  : 0);
188                 checked_set (_bottom_crop, vc ? vc->crop().bottom : 0);
189                 setup_scaling_description ();
190         } else if (property == VideoContentProperty::VIDEO_RATIO) {
191                 if (vc) {
192                         int n = 0;
193                         vector<Ratio const *> ratios = Ratio::all ();
194                         vector<Ratio const *>::iterator i = ratios.begin ();
195                         while (i != ratios.end() && *i != vc->ratio()) {
196                                 ++i;
197                                 ++n;
198                         }
199
200                         if (i == ratios.end()) {
201                                 checked_set (_ratio, -1);
202                         } else {
203                                 checked_set (_ratio, n);
204                         }
205                 } else {
206                         checked_set (_ratio, -1);
207                 }
208                 setup_scaling_description ();
209         } else if (property == FFmpegContentProperty::FILTERS) {
210                 if (fc) {
211                         pair<string, string> p = Filter::ffmpeg_strings (fc->filters ());
212                         if (p.first.empty () && p.second.empty ()) {
213                                 _filters->SetLabel (_("None"));
214                         } else {
215                                 string const b = p.first + " " + p.second;
216                                 _filters->SetLabel (std_to_wx (b));
217                         }
218                 }
219         }
220 }
221
222 /** Called when the `Edit filters' button has been clicked */
223 void
224 VideoPanel::edit_filters_clicked (wxCommandEvent &)
225 {
226         shared_ptr<Content> c = _editor->selected_content ();
227         if (!c) {
228                 return;
229         }
230
231         shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c);
232         if (!fc) {
233                 return;
234         }
235         
236         FilterDialog* d = new FilterDialog (this, fc->filters());
237         d->ActiveChanged.connect (bind (&FFmpegContent::set_filters, fc, _1));
238         d->ShowModal ();
239         d->Destroy ();
240 }
241
242 void
243 VideoPanel::setup_scaling_description ()
244 {
245         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
246         if (!vc) {
247                 _scaling_description->SetLabel ("");
248                 return;
249         }
250
251         wxString d;
252
253         int lines = 0;
254
255         if (vc->video_size().width && vc->video_size().height) {
256                 d << wxString::Format (
257                         _("Original video is %dx%d (%.2f:1)\n"),
258                         vc->video_size().width, vc->video_size().height,
259                         float (vc->video_size().width) / vc->video_size().height
260                         );
261                 ++lines;
262         }
263
264         Crop const crop = vc->crop ();
265         if ((crop.left || crop.right || crop.top || crop.bottom) && vc->video_size() != libdcp::Size (0, 0)) {
266                 libdcp::Size cropped = vc->video_size ();
267                 cropped.width -= crop.left + crop.right;
268                 cropped.height -= crop.top + crop.bottom;
269                 d << wxString::Format (
270                         _("Cropped to %dx%d (%.2f:1)\n"),
271                         cropped.width, cropped.height,
272                         float (cropped.width) / cropped.height
273                         );
274                 ++lines;
275         }
276
277         Ratio const * ratio = vc->ratio ();
278         if (ratio) {
279                 libdcp::Size container_size = _editor->film()->container()->size (_editor->film()->full_frame ());
280                 
281                 libdcp::Size const scaled = ratio->size (container_size);
282                 d << wxString::Format (
283                         _("Scaled to %dx%d (%.2f:1)\n"),
284                         scaled.width, scaled.height,
285                         float (scaled.width) / scaled.height
286                         );
287                 ++lines;
288
289                 if (scaled != container_size) {
290                         d << wxString::Format (
291                                 _("Padded with black to %dx%d (%.2f:1)\n"),
292                                 container_size.width, container_size.height,
293                                 float (container_size.width) / container_size.height
294                                 );
295                         ++lines;
296                 }
297         }
298
299         for (int i = lines; i < 4; ++i) {
300                 d << wxT ("\n ");
301         }
302
303         _scaling_description->SetLabel (d);
304         _sizer->Layout ();
305 }
306
307
308 void
309 VideoPanel::ratio_changed (wxCommandEvent &)
310 {
311         if (!_editor->film ()) {
312                 return;
313         }
314
315         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
316         
317         int const n = _ratio->GetSelection ();
318         if (n >= 0) {
319                 vector<Ratio const *> ratios = Ratio::all ();
320                 assert (n < int (ratios.size()));
321                 vc->set_ratio (ratios[n]);
322         }
323 }
324
325 void
326 VideoPanel::frame_type_changed ()
327 {
328         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
329         if (vc) {
330                 vc->set_video_frame_type (static_cast<VideoFrameType> (_frame_type->GetSelection ()));
331         }
332 }