Move things round a bit.
[dcpomatic.git] / src / gtk / film_player.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 #include "lib/screen.h"
21 #include "lib/config.h"
22 #include "lib/player_manager.h"
23 #include "lib/film.h"
24 #include "film_player.h"
25 #include "gtk_util.h"
26
27 using namespace std;
28 using namespace boost;
29
30 FilmPlayer::FilmPlayer (Film const * f)
31         : _play ("Play")
32         , _pause ("Pause")
33         , _stop ("Stop")
34         , _ab ("A/B")
35         , _ignore_position_changed (false)
36 {
37         set_film (f);
38
39         vector<shared_ptr<Screen> > const scr = Config::instance()->screens ();
40         for (vector<shared_ptr<Screen> >::const_iterator i = scr.begin(); i != scr.end(); ++i) {
41                 _screen.append_text ((*i)->name ());
42         }
43         
44         if (!scr.empty ()) {
45                 _screen.set_active (0);
46         }
47
48         _status.set_use_markup (true);
49
50         _position.set_digits (0);
51
52         _main_vbox.set_spacing (12);
53
54         Gtk::HBox* l = manage (new Gtk::HBox);
55         l->pack_start (_play);
56         l->pack_start (_pause);
57         l->pack_start (_stop);
58
59         Gtk::VBox* r = manage (new Gtk::VBox);
60         r->pack_start (_screen, false, false);
61         r->pack_start (_ab, false, false);
62         r->pack_start (*manage (new Gtk::Label ("")), true, true);
63
64         Gtk::HBox* t = manage (new Gtk::HBox);
65         t->pack_start (*l, true, true);
66         t->pack_start (*r, false, false);
67
68         _main_vbox.pack_start (*t, true, true);
69         _main_vbox.pack_start (_position, false, false);
70         _main_vbox.pack_start (_status, false, false);
71
72         _play.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::play_clicked));
73         _pause.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::pause_clicked));
74         _stop.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::stop_clicked));
75         _position.signal_value_changed().connect (sigc::mem_fun (*this, &FilmPlayer::position_changed));
76         _position.signal_format_value().connect (sigc::mem_fun (*this, &FilmPlayer::format_position));
77
78         set_button_states ();
79         Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &FilmPlayer::update), true), 1000);
80
81         Config::instance()->Changed.connect (sigc::mem_fun (*this, &FilmPlayer::update_screens));
82 }
83
84 void
85 FilmPlayer::set_film (Film const * f)
86 {
87         _film = f;
88
89         if (_film && _film->length() != 0 && _film->frames_per_second() != 0) {
90                 _position.set_range (0, _film->length() / _film->frames_per_second());
91         }
92
93         if (_film) {
94                 _film->Changed.connect (sigc::mem_fun (*this, &FilmPlayer::film_changed));
95         }
96 }
97
98 Gtk::Widget &
99 FilmPlayer::widget ()
100 {
101         return _main_vbox;
102 }
103
104 void
105 FilmPlayer::set_button_states ()
106 {
107         if (_film == 0) {
108                 _play.set_sensitive (false);
109                 _pause.set_sensitive (false);
110                 _stop.set_sensitive (false);
111                 _screen.set_sensitive (false);
112                 _position.set_sensitive (false);
113                 _ab.set_sensitive (false);
114                 return;
115         }
116         
117         PlayerManager::State s = PlayerManager::instance()->state ();
118
119         switch (s) {
120         case PlayerManager::QUIESCENT:
121                 _play.set_sensitive (true);
122                 _pause.set_sensitive (false);
123                 _stop.set_sensitive (false);
124                 _screen.set_sensitive (true);
125                 _position.set_sensitive (false);
126                 _ab.set_sensitive (true);
127                 break;
128         case PlayerManager::PLAYING:
129                 _play.set_sensitive (false);
130                 _pause.set_sensitive (true);
131                 _stop.set_sensitive (true);
132                 _screen.set_sensitive (false);
133                 _position.set_sensitive (true);
134                 _ab.set_sensitive (false);
135                 break;
136         case PlayerManager::PAUSED:
137                 _play.set_sensitive (true);
138                 _pause.set_sensitive (false);
139                 _stop.set_sensitive (true);
140                 _screen.set_sensitive (false);
141                 _position.set_sensitive (false);
142                 _ab.set_sensitive (false);
143                 break;
144         }
145 }
146
147 void
148 FilmPlayer::play_clicked ()
149 {
150         PlayerManager* p = PlayerManager::instance ();
151
152         switch (p->state ()) {
153         case PlayerManager::QUIESCENT:
154                 _last_play_fs = _film->state_copy ();
155                 if (_ab.get_active ()) {
156                         shared_ptr<FilmState> fs_a = _film->state_copy ();
157                         fs_a->filters.clear ();
158                         /* This is somewhat arbitrary, but hey ho */
159                         fs_a->scaler = Scaler::from_id ("bicubic");
160                         p->setup (fs_a, _last_play_fs, screen ());
161                 } else {
162                         p->setup (_last_play_fs, screen ());
163                 }
164                 p->pause_or_unpause ();
165                 break;
166         case PlayerManager::PLAYING:
167                 break;
168         case PlayerManager::PAUSED:
169                 p->pause_or_unpause ();
170                 break;
171         }
172 }
173
174 void
175 FilmPlayer::pause_clicked ()
176 {
177         PlayerManager* p = PlayerManager::instance ();
178
179         switch (p->state ()) {
180         case PlayerManager::QUIESCENT:
181                 break;
182         case PlayerManager::PLAYING:
183                 p->pause_or_unpause ();
184                 break;
185         case PlayerManager::PAUSED:
186                 break;
187         }
188 }
189
190 void
191 FilmPlayer::stop_clicked ()
192 {
193         PlayerManager::instance()->stop ();
194 }
195
196 shared_ptr<Screen>
197 FilmPlayer::screen () const
198 {
199         vector<shared_ptr<Screen> > const s = Config::instance()->screens ();
200         if (s.empty ()) {
201                 return shared_ptr<Screen> ();
202         }
203                          
204         int const r = _screen.get_active_row_number ();
205         if (r >= int (s.size ())) {
206                 return s[0];
207         }
208         
209         return s[r];
210 }
211
212 void
213 FilmPlayer::update ()
214 {
215         set_button_states ();
216         set_status ();
217 }
218
219 void
220 FilmPlayer::set_status ()
221 {
222         PlayerManager::State s = PlayerManager::instance()->state ();
223
224         stringstream m;
225         switch (s) {
226         case PlayerManager::QUIESCENT:
227                 m << "Idle";
228                 break;
229         case PlayerManager::PLAYING:
230                 m << "<span foreground=\"red\" weight=\"bold\">PLAYING</span>";
231                 break;
232         case PlayerManager::PAUSED:
233                 m << "<b>Paused</b>";
234                 break;
235         }
236
237         _ignore_position_changed = true;
238         
239         if (s != PlayerManager::QUIESCENT) {
240                 float const p = PlayerManager::instance()->position ();
241                 if (_last_play_fs->frames_per_second != 0 && _last_play_fs->length != 0) {
242                         m << " <i>(" << seconds_to_hms (_last_play_fs->length / _last_play_fs->frames_per_second - p) << " remaining)</i>";
243                 }
244
245                 _position.set_value (p);
246         } else {
247                 _position.set_value (0);
248         }
249
250         _ignore_position_changed = false;
251         
252         _status.set_markup (m.str ());
253 }
254
255 void
256 FilmPlayer::position_changed ()
257 {
258         if (_ignore_position_changed) {
259                 return;
260         }
261
262         PlayerManager::instance()->set_position (_position.get_value ());
263 }
264
265 string
266 FilmPlayer::format_position (double v)
267 {
268         return seconds_to_hms (v);
269 }
270
271 void
272 FilmPlayer::update_screens ()
273 {
274         string const c = _screen.get_active_text ();
275         
276         _screen.clear ();
277         
278         vector<shared_ptr<Screen> > const scr = Config::instance()->screens ();
279         bool have_last_active_text = false;
280         for (vector<shared_ptr<Screen> >::const_iterator i = scr.begin(); i != scr.end(); ++i) {
281                 _screen.append_text ((*i)->name ());
282                 if ((*i)->name() == c) {
283                         have_last_active_text = true;
284                 }
285         }
286
287         if (have_last_active_text) {
288                 _screen.set_active_text (c);
289         } else if (!scr.empty ()) {
290                 _screen.set_active (0);
291         }
292 }
293
294 void
295 FilmPlayer::film_changed (Film::Property p)
296 {
297         if (p == Film::CONTENT) {
298                 setup_visibility ();
299         }
300 }
301
302 void
303 FilmPlayer::setup_visibility ()
304 {
305         if (!_film) {
306                 return;
307         }
308         
309         widget().property_visible() = (_film->content_type() == VIDEO);
310 }