2
private const string[] SUBTITLE_EXTENSIONS = {
10
public class PlayerPage : Gtk.Bin {
11
public GtkClutter.Embed clutter;
12
public Audience.Widgets.VideoPlayer video_player;
13
private Audience.Widgets.BottomBar bottom_bar;
14
private Clutter.Stage stage;
15
private Gtk.Revealer unfullscreen_bar;
16
private GtkClutter.Actor unfullscreen_actor;
17
private GtkClutter.Actor bottom_actor;
18
private GnomeMediaKeys mediakeys;
20
private bool mouse_primary_down = false;
21
private bool fullscreened = false;
25
return bottom_bar.get_repeat ();
29
bottom_bar.set_repeat (value);
33
private int bottom_bar_size = 0;
35
public signal void ended ();
37
public PlayerPage () {
38
video_player = new Widgets.VideoPlayer();
39
video_player.notify["playing"].connect (() => {bottom_bar.toggle_play_pause ();});
41
clutter = new GtkClutter.Embed ();
42
stage = (Clutter.Stage)clutter.get_stage ();
43
stage.background_color = {0, 0, 0, 0};
44
stage.use_alpha = true;
46
video_player.add_constraint (new Clutter.BindConstraint (stage, Clutter.BindCoordinate.WIDTH, 0));
47
video_player.add_constraint (new Clutter.BindConstraint (stage, Clutter.BindCoordinate.HEIGHT, 0));
49
stage.add_child (video_player);
51
bottom_bar = new Widgets.BottomBar (video_player);
52
bottom_bar.set_valign (Gtk.Align.END);
53
bottom_bar.play_toggled.connect (() => { video_player.playing = !video_player.playing; });
54
bottom_bar.seeked.connect ((val) => { video_player.progress = val; });
55
bottom_bar.unfullscreen.connect (()=>{set_fullscreen (false);});
56
bottom_bar.set_repeat (false);
58
unfullscreen_bar = bottom_bar.get_unfullscreen_button ();
60
bottom_actor = new GtkClutter.Actor.with_contents (bottom_bar);
61
bottom_actor.opacity = GLOBAL_OPACITY;
62
stage.add_child (bottom_actor);
64
unfullscreen_actor = new GtkClutter.Actor.with_contents (unfullscreen_bar);
65
unfullscreen_actor.opacity = GLOBAL_OPACITY;
66
stage.add_child (unfullscreen_actor);
68
this.size_allocate.connect (on_size_allocate);
69
App.get_instance ().mainwindow.key_press_event.connect (on_key_press_event);
70
App.get_instance ().mainwindow.window_state_event.connect (on_window_state_event);
71
if (App.get_instance ().mainwindow.is_maximized)
72
set_fullscreen (true);
76
mediakeys = Bus.get_proxy_sync (BusType.SESSION,
77
"org.gnome.SettingsDaemon", "/org/gnome/SettingsDaemon/MediaKeys");
78
mediakeys.MediaPlayerKeyPressed.connect ((bus, app, key) => {
79
if (app != "audience")
83
get_playlist_widget ().previous ();
86
get_playlist_widget ().next ();
89
video_player.playing = !video_player.playing;
96
mediakeys.GrabMediaPlayerKeys("audience", 0);
101
App.get_instance ().mainwindow.motion_notify_event.connect ((event) => {
102
if (mouse_primary_down && settings.move_window) {
103
mouse_primary_down = false;
104
App.get_instance ().mainwindow.begin_move_drag (Gdk.BUTTON_PRIMARY,
105
(int)event.x_root, (int)event.y_root, event.time);
108
Gtk.Allocation allocation;
109
clutter.get_allocation (out allocation);
110
return update_pointer_position (event.y, allocation.height);
113
this.button_press_event.connect ((event) => {
114
if (event.button == Gdk.BUTTON_PRIMARY
115
&& event.type == Gdk.EventType.2BUTTON_PRESS) // double left click
116
set_fullscreen(!fullscreened);
118
if (event.button == Gdk.BUTTON_SECONDARY) // right click
119
bottom_bar.play_toggled ();
121
if (event.button == Gdk.BUTTON_PRIMARY)
122
mouse_primary_down = true;
127
this.button_release_event.connect ((event) => {
128
if (event.button == Gdk.BUTTON_PRIMARY)
129
mouse_primary_down = false;
134
this.leave_notify_event.connect ((event) => {
135
Gtk.Allocation allocation;
136
clutter.get_allocation (out allocation);
138
if (event.x == event.window.get_width ())
139
return update_pointer_position (event.window.get_height (), allocation.height);
140
else if (event.x == 0)
141
return update_pointer_position (event.window.get_height (), allocation.height);
143
return update_pointer_position (event.y, allocation.height);
146
this.destroy.connect (() => {
147
// FIXME:should find better way to decide if its end of playlist
148
if (video_player.progress > 0.99)
149
settings.last_stopped = 0;
151
settings.last_stopped = video_player.progress;
153
get_playlist_widget ().save_playlist ();
157
video_player.text_tags_changed.connect (bottom_bar.preferences_popover.setup_text);
158
video_player.audio_tags_changed.connect (bottom_bar.preferences_popover.setup_audio);
159
video_player.progression_changed.connect ((current_time, total_time) => {
160
bottom_bar.set_progression_time (current_time, total_time);
164
video_player.ended.connect (() => {
166
video_player.progress = 0;
167
if (!get_playlist_widget ().next ()) {
169
play_file (get_playlist_widget ().get_first_item ().get_uri ());
170
video_player.playing = true;
172
video_player.playing = false;
180
video_player.error.connect (() => {
181
App.get_instance ().page = Page.WELCOME;
184
video_player.plugin_install_done.connect (() => {
185
App.get_instance ().page = Page.PLAYER;
188
video_player.notify["playing"].connect (() => {
189
App.get_instance ().mainwindow.set_keep_above (video_player.playing && settings.stay_on_top);
192
bottom_bar.time_widget.slider_motion_event.connect ((event) => {
194
bottom_bar.translate_coordinates (App.get_instance ().mainwindow, (int)event.x, (int)event.y, out x, out y);
195
Gtk.Allocation allocation;
196
clutter.get_allocation (out allocation);
197
update_pointer_position (y, allocation.height);
200
//playlist wants us to open a file
201
get_playlist_widget ().play.connect ((file) => {
202
this.play_file (file.get_uri ());
205
bottom_bar.notify["child-revealed"].connect (() => {
206
if (bottom_bar.child_revealed == true) {
207
App.get_instance ().mainwindow.get_window ().set_cursor (null);
209
App.get_instance ().mainwindow.get_window ().set_cursor (new Gdk.Cursor (Gdk.CursorType.BLANK_CURSOR));
213
stage.notify["allocation"].connect (() => {allocate_bottombar ();});
222
video_player.playing = false;
224
App.get_instance ().set_content_size (0, 0, 0);
225
this.size_allocate.disconnect (on_size_allocate);
226
App.get_instance ().mainwindow.window_state_event.disconnect (on_window_state_event);
227
App.get_instance ().mainwindow.key_press_event.disconnect (on_key_press_event);
228
if (App.get_instance ().mainwindow.get_window () != null)
229
App.get_instance ().mainwindow.get_window ().set_cursor (null);
231
App.get_instance ().mainwindow.unfullscreen ();
233
App.get_instance ().mainwindow.maximize ();
235
video_player.text_tags_changed.disconnect (bottom_bar.preferences_popover.setup_text);
236
video_player.audio_tags_changed.disconnect (bottom_bar.preferences_popover.setup_audio);
239
public void play_file (string uri, bool from_beginning = true) {
240
debug ("Opening %s", uri);
241
video_player.uri = uri;
242
get_playlist_widget ().set_current (uri);
243
bottom_bar.set_preview_uri (uri);
245
string? sub_uri = get_subtitle_for_uri (uri);
247
video_player.set_subtitle_uri (sub_uri);
249
App.get_instance ().set_window_title (get_title (uri));
250
video_player.relayout ();
252
int target_width, target_height, center_x, center_y;
253
get_target_size (out target_width, out target_height, out center_x, out center_y);
254
get_window ().move_resize (center_x, center_y, target_width, target_height);
256
update_aspect_ratio ();
257
video_player.playing = !settings.playback_wait;
259
video_player.progress = 0.0;
261
Gtk.RecentManager recent_manager = Gtk.RecentManager.get_default ();
262
recent_manager.add_item (uri);
264
/*subtitles/audio tracks*/
265
bottom_bar.preferences_popover.setup_text ();
266
bottom_bar.preferences_popover.setup_audio ();
269
public void next () {
270
get_playlist_widget ().next ();
273
public void prev () {
274
get_playlist_widget ().next ();
277
public void resume_last_videos () {
278
play_file (settings.current_video);
279
video_player.playing = false;
281
if (settings.resume_videos)
282
video_player.progress = settings.last_stopped;
284
video_player.progress = 0.0;
288
video_player.playing = !settings.playback_wait;
291
public void append_to_playlist (File file) {
292
get_playlist_widget ().add_item (file);
295
public void play_first_in_playlist () {
296
var file = get_playlist_widget ().get_first_item ();
297
play_file (file.get_uri ());
298
video_player.progress = 0.0;
301
private Widgets.Playlist get_playlist_widget () {
302
return bottom_bar.playlist_popover.playlist;
305
private string? get_subtitle_for_uri (string uri) {
307
int last_dot = uri.last_index_of (".", 0);
308
int last_slash = uri.last_index_of ("/", 0);
310
if (last_dot < last_slash) //we dont have extension
313
without_ext = uri.slice (0, last_dot);
315
foreach (string ext in SUBTITLE_EXTENSIONS){
316
string sub_uri = without_ext + "." + ext;
317
if (File.new_for_uri (sub_uri).query_exists ())
323
public static bool is_subtitle (string uri) {
324
if (uri.length < 4 || uri.get_char (uri.length-4) != '.')
327
foreach (string ext in SUBTITLE_EXTENSIONS) {
328
if (uri.down ().has_suffix (ext))
335
private void allocate_bottombar () {
336
bottom_actor.width = stage.get_width ();
337
bottom_bar.queue_resize ();
338
bottom_actor.y = stage.get_height () - bottom_bar_size;
339
unfullscreen_actor.y = 6;
340
unfullscreen_actor.x = stage.get_width () - bottom_bar_size - 6;
343
public bool update_pointer_position (double y, int window_height) {
344
allocate_bottombar ();
345
App.get_instance ().mainwindow.get_window ().set_cursor (null);
346
if (bottom_bar_size == 0) {
348
bottom_bar.get_preferred_height (out minimum, out bottom_bar_size);
351
bottom_bar.reveal_control ();
356
private void get_target_size (out int target_width, out int target_height,
357
out int center_x, out int center_y) {
358
Gdk.Rectangle monitor;
359
var screen = Gdk.Screen.get_default ();
360
screen.get_monitor_geometry (screen.get_monitor_at_window (get_window ()),
363
if (monitor.width > video_player.video_width
364
&& monitor.height > video_player.video_height) {
365
target_width = (int) video_player.video_width;
366
target_height = (int) video_player.video_height;
368
target_width = (int)(monitor.width * 0.9);
369
target_height = (int)((double) video_player.video_height / video_player.video_width * target_width);
371
center_x = monitor.width / 2 - target_width /2 + monitor.x;
372
center_y = monitor.height / 2 - target_height /2 + monitor.y;
375
private bool on_key_press_event (Gdk.EventKey e) {
379
video_player.playing = !video_player.playing;
383
set_fullscreen (false);
385
App.get_instance ().mainwindow.destroy ();
389
if (modifier_is_pressed (e, Gdk.ModifierType.SHIFT_MASK)) {
390
video_player.seek_jump_seconds (-5); // 5 secs
392
video_player.seek_jump_seconds (-60); // 1 min
394
bottom_bar.reveal_control ();
397
if (modifier_is_pressed (e, Gdk.ModifierType.SHIFT_MASK)) {
398
video_player.seek_jump_seconds (-1); // 1 sec
400
video_player.seek_jump_seconds (-10); // 10 secs
402
bottom_bar.reveal_control ();
405
if (modifier_is_pressed (e, Gdk.ModifierType.SHIFT_MASK)) {
406
video_player.seek_jump_seconds (1); // 1 sec
408
video_player.seek_jump_seconds (10); // 10 secs
410
bottom_bar.reveal_control ();
413
if (modifier_is_pressed (e, Gdk.ModifierType.SHIFT_MASK)) {
414
video_player.seek_jump_seconds (5); // 5 secs
416
video_player.seek_jump_seconds (60); // 1 min
418
bottom_bar.reveal_control ();
420
case Gdk.Key.Page_Down:
421
video_player.seek_jump_seconds (-600); // 10 mins
422
bottom_bar.reveal_control ();
424
case Gdk.Key.Page_Up:
425
video_player.seek_jump_seconds (600); // 10 mins
426
bottom_bar.reveal_control ();
429
bottom_bar.preferences_popover.next_audio ();
432
bottom_bar.preferences_popover.next_text ();
436
set_fullscreen (false);
438
set_fullscreen (true);
448
private bool on_window_state_event (Gdk.EventWindowState e){
449
switch (e.changed_mask){
450
case Gdk.WindowState.FULLSCREEN:
451
fullscreened= ((e.new_window_state & Gdk.WindowState.FULLSCREEN)!=0);
453
case Gdk.WindowState.MAXIMIZED:
454
bool currently_maximixed = ((e.new_window_state & Gdk.WindowState.MAXIMIZED)!=0);
455
set_fullscreen (currently_maximixed);
461
private void set_fullscreen (bool full){
464
App.get_instance ().mainwindow.fullscreen ();
466
// unfullscreen shoulnd't be call from elsewhere other than here
467
App.get_instance ().mainwindow.maximize ();
468
App.get_instance ().mainwindow.unfullscreen ();
470
bottom_bar.fullscreen = full;
473
private uint update_aspect_ratio_timeout = 0;
474
private bool update_aspect_ratio_locked = false;
475
private int prev_width = 0;
476
private int prev_height = 0;
477
private int old_h = -1;
478
private int old_w = -1;
480
* Updates the window's aspect ratio locking if enabled.
481
* Return type is just there to make it compatible with Idle.add()
483
private bool update_aspect_ratio () {
484
if (!settings.keep_aspect
485
|| video_player.video_width < 1
486
|| video_player.height < 1
490
if (update_aspect_ratio_timeout != 0)
491
Source.remove (update_aspect_ratio_timeout);
493
update_aspect_ratio_timeout = Timeout.add (200, () => {
495
clutter.get_allocation (out a);
496
print ("%i %i %i,%i\n", a.x, a.y, (this.get_allocated_width () - this.clutter.get_allocated_width ()) / 2, (this.get_allocated_height () - this.clutter.get_allocated_height ()) / 2);
498
double width, height;
499
width = clutter.get_allocated_width ();
500
height = width * video_player.video_height / (double) video_player.video_width;
502
App.get_instance ().set_content_size (width, height,clutter.get_allocated_height ());
504
prev_width = this.get_allocated_width ();
505
prev_height = this.get_allocated_height ();
507
update_aspect_ratio_timeout = 0;
514
private void on_size_allocate (Gtk.Allocation alloc) {
515
if (alloc.width != old_w || alloc.height != old_h) {
516
if (video_player.relayout ()) {
518
old_h = alloc.height;
522
if (prev_width != this.get_allocated_width () || prev_height != this.get_allocated_height ())
523
Idle.add (update_aspect_ratio);