Merge branch 'master' of /home/carl/git/dvdomatic
[dcpomatic.git] / src / wx / film_viewer.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_viewer.cc
21  *  @brief A wx widget to view `thumbnails' of a Film.
22  */
23
24 #include <iostream>
25 #include <iomanip>
26 #include "lib/film.h"
27 #include "lib/format.h"
28 #include "lib/util.h"
29 #include "lib/thumbs_job.h"
30 #include "lib/job_manager.h"
31 #include "lib/film_state.h"
32 #include "lib/options.h"
33 #include "film_viewer.h"
34 #include "wx_util.h"
35
36 using namespace std;
37 using namespace boost;
38
39 class ThumbPanel : public wxPanel
40 {
41 public:
42         ThumbPanel (wxPanel* parent, Film* film)
43                 : wxPanel (parent)
44                 , _film (film)
45                 , _image (0)
46                 , _bitmap (0)
47         {
48         }
49
50         void paint_event (wxPaintEvent& ev)
51         {
52                 if (_current_image != _pending_image) {
53                         delete _image;
54                         _image = new wxImage (std_to_wx (_pending_image));
55                         _current_image = _pending_image;
56                         setup ();
57                 }
58
59                 if (_current_crop != _pending_crop) {
60                         _current_crop = _pending_crop;
61                         setup ();
62                 }
63
64                 wxPaintDC dc (this);
65                 if (_bitmap) {
66                         dc.DrawBitmap (*_bitmap, 0, 0, false);
67                 }
68         }
69
70         void size_event (wxSizeEvent &)
71         {
72                 if (!_image) {
73                         return;
74                 }
75
76                 setup ();
77                 Refresh ();
78         }
79
80         void set (string f)
81         {
82                 _pending_image = f;
83                 Refresh ();
84         }
85
86         void set_crop (Crop c)
87         {
88                 _pending_crop = c;
89                 Refresh ();
90         }
91
92         void set_film (Film* f)
93         {
94                 _film = f;
95                 if (!_film) {
96                         clear ();
97                         Refresh ();
98                 } else {
99                         setup ();
100                         Refresh ();
101                 }
102         }
103
104         void clear ()
105         {
106                 delete _bitmap;
107                 _bitmap = 0;
108                 delete _image;
109                 _image = 0;
110         }
111
112         void refresh ()
113         {
114                 setup ();
115                 Refresh ();
116         }
117
118         DECLARE_EVENT_TABLE ();
119
120 private:
121
122         void setup ()
123         {
124                 if (!_film || !_image) {
125                         return;
126                 }
127                 
128                 int vw, vh;
129                 GetSize (&vw, &vh);
130
131                 float const target = _film->format() ? _film->format()->ratio_as_float () : 1.78;
132
133                 _cropped_image = _image->GetSubImage (
134                         wxRect (
135                                 _current_crop.left,
136                                 _current_crop.top,
137                                 _image->GetWidth() - (_current_crop.left + _current_crop.right),
138                                 _image->GetHeight() - (_current_crop.top + _current_crop.bottom)
139                                 )
140                         );
141
142                 if ((float (vw) / vh) > target) {
143                         /* view is longer (horizontally) than the ratio; fit height */
144                         _cropped_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH);
145                 } else {
146                         /* view is shorter (horizontally) than the ratio; fit width */
147                         _cropped_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH);
148                 }
149
150                 delete _bitmap;
151                 _bitmap = new wxBitmap (_cropped_image);
152         }
153
154         Film* _film;
155         wxImage* _image;
156         std::string _current_image;
157         std::string _pending_image;
158         wxImage _cropped_image;
159         wxBitmap* _bitmap;
160         Crop _current_crop;
161         Crop _pending_crop;
162 };
163
164 BEGIN_EVENT_TABLE (ThumbPanel, wxPanel)
165 EVT_PAINT (ThumbPanel::paint_event)
166 EVT_SIZE (ThumbPanel::size_event)
167 END_EVENT_TABLE ()
168
169 FilmViewer::FilmViewer (Film* f, wxWindow* p)
170         : wxPanel (p)
171         , _film (f)
172 {
173         _sizer = new wxBoxSizer (wxVERTICAL);
174         SetSizer (_sizer);
175         
176         _thumb_panel = new ThumbPanel (this, f);
177         _sizer->Add (_thumb_panel, 1, wxEXPAND);
178
179         int const max = f ? f->num_thumbs() - 1 : 0;
180         _slider = new wxSlider (this, wxID_ANY, 0, 0, max);
181         _sizer->Add (_slider, 0, wxEXPAND | wxLEFT | wxRIGHT);
182         set_thumbnail (0);
183
184         _slider->Connect (wxID_ANY, wxEVT_COMMAND_SLIDER_UPDATED, wxCommandEventHandler (FilmViewer::slider_changed), 0, this);
185
186         set_film (_film);
187 }
188
189 void
190 FilmViewer::set_thumbnail (int n)
191 {
192         if (_film == 0 || _film->num_thumbs() <= n) {
193                 return;
194         }
195
196         _thumb_panel->set (_film->thumb_file(n));
197 }
198
199 void
200 FilmViewer::slider_changed (wxCommandEvent &)
201 {
202         set_thumbnail (_slider->GetValue ());
203 }
204
205 void
206 FilmViewer::film_changed (Film::Property p)
207 {
208         if (p == Film::CROP) {
209                 _thumb_panel->set_crop (_film->crop ());
210         } else if (p == Film::THUMBS) {
211                 if (_film && _film->num_thumbs() > 1) {
212                         _slider->SetRange (0, _film->num_thumbs () - 1);
213                 } else {
214                         _thumb_panel->clear ();
215                         _slider->SetRange (0, 1);
216                 }
217                 
218                 _slider->SetValue (0);
219                 set_thumbnail (0);
220         } else if (p == Film::FORMAT) {
221                 _thumb_panel->refresh ();
222         } else if (p == Film::CONTENT) {
223                 setup_visibility ();
224                 _film->examine_content ();
225                 update_thumbs ();
226         }
227 }
228
229 void
230 FilmViewer::set_film (Film* f)
231 {
232         _film = f;
233         _thumb_panel->set_film (_film);
234
235         if (!_film) {
236                 return;
237         }
238
239         _film->Changed.connect (sigc::mem_fun (*this, &FilmViewer::film_changed));
240         film_changed (Film::THUMBS);
241         _thumb_panel->refresh ();
242         setup_visibility ();
243 }
244
245 void
246 FilmViewer::update_thumbs ()
247 {
248         if (!_film) {
249                 return;
250         }
251
252         _film->update_thumbs_pre_gui ();
253
254         shared_ptr<const FilmState> s = _film->state_copy ();
255         shared_ptr<Options> o (new Options (s->dir ("thumbs"), ".tiff", ""));
256         o->out_size = _film->size ();
257         o->apply_crop = false;
258         o->decode_audio = false;
259         o->decode_video_frequency = 128;
260         
261         shared_ptr<Job> j (new ThumbsJob (s, o, _film->log ()));
262         j->Finished.connect (sigc::mem_fun (_film, &Film::update_thumbs_post_gui));
263         JobManager::instance()->add (j);
264 }
265
266 void
267 FilmViewer::setup_visibility ()
268 {
269         if (!_film) {
270                 return;
271         }
272
273         ContentType const c = _film->content_type ();
274         _slider->Show (c == VIDEO);
275 }