1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- 2/*- 3 * Copyright (c) 2013-2014 Audience Developers (http://launchpad.net/pantheon-chat) 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * Authored by: Corentin Noël <corentin@elementaryos.org> 19 */ 20 21public class Audience.Widgets.PreviewPopover : Gtk.Popover { 22 private enum PlayFlags { 23 VIDEO = (1 << 0), 24 AUDIO = (1 << 1), 25 TEXT = (1 << 2), 26 VIS = (1 << 3), 27 SOFT_VOLUME = (1 << 4), 28 NATIVE_AUDIO = (1 << 5), 29 NATIVE_VIDEO = (1 << 6), 30 DOWNLOAD = (1 << 7), 31 BUFFERING = (1 << 8), 32 DEINTERLACE = (1 << 9), 33 SOFT_COLORBALANCE = (1 << 10) 34 } 35 36 ClutterGst.Playback playback; 37 GtkClutter.Embed clutter; 38 uint loop_timer_id = 0; 39 uint show_timer_id = 0; 40 uint hide_timer_id = 0; 41 uint idle_id = 0; 42 double req_progress = -1; 43 bool req_loop = false; 44 45 public PreviewPopover (ClutterGst.Playback main_playback) { 46 opacity = GLOBAL_OPACITY; 47 can_focus = false; 48 sensitive = false; 49 modal = false; 50 51 playback = new ClutterGst.Playback (); 52 playback.ready.connect (() => { 53 unowned Gst.Element pipeline = playback.get_pipeline (); 54 int flags; 55 pipeline.get ("flags", out flags); 56 flags &= ~PlayFlags.TEXT; //disable subtitle 57 flags &= ~PlayFlags.AUDIO; //disable audio sink 58 pipeline.set ("flags", flags); 59 }); 60 61 playback.set_seek_flags (ClutterGst.SeekFlags.ACCURATE); 62 playback.uri = main_playback.uri; 63 playback.playing = false; 64 clutter = new GtkClutter.Embed (); 65 clutter.margin = 3; 66 var stage = (Clutter.Stage)clutter.get_stage (); 67 stage.background_color = {0, 0, 0, 0}; 68 69 var video_actor = new Clutter.Actor (); 70#if VALA_0_34 71 var aspect_ratio = new ClutterGst.Aspectratio (); 72#else 73 var aspect_ratio = ClutterGst.Aspectratio.@new (); 74#endif 75 ((ClutterGst.Aspectratio) aspect_ratio).paint_borders = false; 76 ((ClutterGst.Content) aspect_ratio).player = playback; 77 video_actor.content = aspect_ratio; 78 ((ClutterGst.Content) aspect_ratio).size_change.connect ((width, height) => { 79 if (width > 0 && height > 0) { 80 double diagonal = Math.sqrt ((width * width) + (height * height)); 81 double k = 230 / diagonal; // for 16:9 ratio it produces width of ~200px 82 clutter.set_size_request ((int)(width * k), (int)(height * k)); 83 } 84 }); 85 86 video_actor.add_constraint (new Clutter.BindConstraint (stage, Clutter.BindCoordinate.WIDTH, 0)); 87 video_actor.add_constraint (new Clutter.BindConstraint (stage, Clutter.BindCoordinate.HEIGHT, 0)); 88 89 stage.add_child (video_actor); 90 add (clutter); 91 92 closed.connect (() => { 93 playback.playing = false; 94 cancel_loop_timer (); 95 cancel_timer (ref show_timer_id); 96 cancel_timer (ref hide_timer_id); 97 }); 98 99 hide.connect (() => { 100 playback.playing = false; 101 cancel_loop_timer (); 102 }); 103 } 104 105 ~PreviewPopover () { 106 playback.playing = false; 107 cancel_loop_timer (); 108 } 109 110 public void set_preview_progress (double progress, bool loop = false) { 111 req_progress = progress; 112 req_loop = loop; 113 114 if (!visible || idle_id > 0) { 115 return; 116 } 117 118 if (loop) { 119 cancel_loop_timer (); 120 } 121 122 idle_id = Idle.add_full (GLib.Priority.LOW, () => { 123 playback.playing = true; 124 playback.progress = progress; 125 playback.playing = loop; 126 if (loop) { 127 loop_timer_id = Timeout.add_seconds (5, () => { 128 set_preview_progress (progress, true); 129 loop_timer_id = 0; 130 return false; 131 }); 132 } 133 idle_id = 0; 134 return false; 135 }); 136 } 137 138 public void update_pointing (int x) { 139 var pointing = pointing_to; 140 pointing.x = x; 141 142 // changing the width properly updates arrow position when popover hits the edge 143 if (pointing.width == 0) { 144 pointing.width = 2; 145 pointing.x -= 1; 146 } else { 147 pointing.width = 0; 148 } 149 150 set_pointing_to (pointing); 151 } 152 153 public void realign_pointing (int parent_width) { 154 if (visible) { 155 update_pointing ((int)(req_progress * parent_width)); 156 } 157 } 158 159 public void schedule_show () { 160 if (show_timer_id > 0) { 161 return; 162 } 163 cancel_timer (ref hide_timer_id); 164 165 show_timer_id = Timeout.add (300, () => { 166 show_all (); 167 if (req_progress >= 0) { 168 set_preview_progress (req_progress, req_loop); 169 } 170 show_timer_id = 0; 171 return false; 172 }); 173 } 174 175 public void schedule_hide () { 176 if (hide_timer_id > 0) { 177 return; 178 } 179 cancel_timer (ref show_timer_id); 180 181 hide_timer_id = Timeout.add (300, () => { 182 hide (); 183 hide_timer_id = 0; 184 return false; 185 }); 186 } 187 188 private void cancel_loop_timer () { 189 cancel_timer (ref loop_timer_id); 190 } 191 192 private void cancel_timer (ref uint timer_id) { 193 if (timer_id > 0) { 194 Source.remove (timer_id); 195 timer_id = 0; 196 } 197 } 198} 199