~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to platform/osx/plat/frontends/widgets/video.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Miro - an RSS based video player application
2
 
# Copyright (C) 2005-2010 Participatory Culture Foundation
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17
 
#
18
 
# In addition, as a special exception, the copyright holders give
19
 
# permission to link the code of portions of this program with the OpenSSL
20
 
# library.
21
 
#
22
 
# You must obey the GNU General Public License in all respects for all of
23
 
# the code used other than OpenSSL. If you modify file(s) with this
24
 
# exception, you may extend this exception to your version of the file(s),
25
 
# but you are not obligated to do so. If you do not wish to do so, delete
26
 
# this exception statement from your version. If you delete this exception
27
 
# statement from all source files in the program, then also delete it here.
28
 
 
29
 
import logging
30
 
 
31
 
from objc import YES, NO, nil, pathForFramework, loadBundleFunctions
32
 
from Foundation import *
33
 
from AppKit import *
34
 
from QTKit import *
35
 
 
36
 
from miro import app
37
 
from miro.plat.frontends.widgets import mediatypes
38
 
from miro.plat.frontends.widgets import threads
39
 
from miro.plat.frontends.widgets import overlay
40
 
from miro.plat.frontends.widgets import quicktime
41
 
from miro.plat.frontends.widgets import wrappermap
42
 
from miro.plat.frontends.widgets.base import Widget
43
 
 
44
 
###############################################################################
45
 
#### Dynamically link some specific Carbon functions which we need but     ####
46
 
###  which are not available in the default MacPython                      ####
47
 
###############################################################################
48
 
 
49
 
kUIModeNormal = 0
50
 
kUIModeAllHidden = 3
51
 
 
52
 
carbonPath = pathForFramework('/System/Library/Frameworks/Carbon.framework')
53
 
carbonBundle = NSBundle.bundleWithPath_(carbonPath)
54
 
loadBundleFunctions(carbonBundle, globals(), ((u'SetSystemUIMode', 'III'),))
55
 
 
56
 
OverallActivity = 0
57
 
 
58
 
coreServicesPath = pathForFramework('/System/Library/Frameworks/CoreServices.framework')
59
 
coreServicesBundle = NSBundle.bundleWithPath_(coreServicesPath)
60
 
loadBundleFunctions(coreServicesBundle, globals(), ((u'UpdateSystemActivity', 'IC'),))
61
 
 
62
 
###############################################################################
63
 
 
64
 
SUPPORTED_MEDIA_TYPES = mediatypes.VIDEO_MEDIA_TYPES
65
 
 
66
 
###############################################################################
67
 
 
68
 
class MiroMovieView (QTMovieView):
69
 
    
70
 
    def movieBounds(self):
71
 
        movie = self.movie()
72
 
        if movie is not None and app.playback_manager.presentation_mode != 'fit-to-bounds':
73
 
            my_bounds = self.bounds()
74
 
            natural_size = movie.attributeForKey_(QTMovieNaturalSizeAttribute).sizeValue()
75
 
 
76
 
            if app.playback_manager.presentation_mode == 'natural-size':
77
 
                movie_size = natural_size
78
 
            elif app.playback_manager.presentation_mode == 'half-size':
79
 
                movie_size = NSSize(natural_size.width/2, natural_size.height/2)
80
 
            elif app.playback_manager.presentation_mode == 'double-size':
81
 
                movie_size = NSSize(natural_size.width*2, natural_size.height*2)
82
 
            if movie_size.width > my_bounds.size.width or movie_size.height > my_bounds.size.height:
83
 
                return QTMovieView.movieBounds(self)
84
 
 
85
 
            movie_origin = NSPoint(int((my_bounds.size.width - movie_size.width) / 2), 
86
 
                                   int((my_bounds.size.height - movie_size.height) / 2))
87
 
            return NSRect(movie_origin, movie_size)
88
 
 
89
 
        return QTMovieView.movieBounds(self)
90
 
 
91
 
###############################################################################
92
 
 
93
 
class VideoPlayer (Widget, quicktime.Player):
94
 
 
95
 
    def __init__(self):
96
 
        quicktime.Player.__init__(self, SUPPORTED_MEDIA_TYPES)
97
 
        Widget.__init__(self)
98
 
 
99
 
        frame = ((0,0),(200,200))
100
 
 
101
 
        self.view = NSView.alloc().initWithFrame_(frame)
102
 
 
103
 
        self.video_view = MiroMovieView.alloc().initWithFrame_(frame)
104
 
        self.video_view.setFillColor_(NSColor.blackColor())
105
 
        self.video_view.setControllerVisible_(NO)
106
 
        self.video_view.setEditable_(NO)
107
 
        self.video_view.setPreservesAspectRatio_(YES)
108
 
 
109
 
        self.movie = None
110
 
        self.movie_notifications = None
111
 
        self.system_activity_updater_timer = None
112
 
        self.window_moved_handler = None
113
 
        self.item_changed_handler = None
114
 
 
115
 
    def calc_size_request(self):
116
 
        return (200,200)
117
 
 
118
 
    def viewport_created(self):
119
 
        self.video_window = VideoWindow.alloc().initWithContentRect_styleMask_backing_defer_(self.view.frame(), NSBorderlessWindowMask, NSBackingStoreBuffered, NO)
120
 
        self.video_window.setContentView_(self.video_view)
121
 
 
122
 
        self.adjust_video_frame()
123
 
        self.view.window().addChildWindow_ordered_(self.video_window, NSWindowAbove)
124
 
        self.video_window.orderFront_(nil)
125
 
        self.window_moved_handler = wrappermap.wrapper(self.view.window()).connect('did-move', self.on_window_moved)
126
 
        app.info_updater.item_changed_callbacks.add('manual', 'playback-list', self.on_items_changed)
127
 
 
128
 
    def place(self, rect, containing_view):
129
 
        Widget.place(self, rect, containing_view)
130
 
        self.adjust_video_frame()
131
 
 
132
 
    def on_window_moved(self, window):
133
 
        self.adjust_video_frame()
134
 
 
135
 
    def on_items_changed(self, message):
136
 
        if self.video_window is not None:
137
 
            self.video_window.on_items_changed(message.changed)
138
 
        
139
 
    def remove_viewport(self):
140
 
        app.info_updater.item_changed_callbacks.remove('manual', 'playback-list', self.on_items_changed)
141
 
        self.item_changed_handler = None
142
 
        self.prevent_system_sleep(False)
143
 
        self.detach_from_parent_window()
144
 
        self.video_window.close()
145
 
        self.video_window = None
146
 
        Widget.remove_viewport(self)
147
 
 
148
 
    def teardown(self):
149
 
        pass
150
 
 
151
 
    def detach_from_parent_window(self):
152
 
        window = self.view.window()
153
 
        window.removeChildWindow_(self.video_window)
154
 
        wrappermap.wrapper(window).disconnect(self.window_moved_handler)
155
 
        self.window_moved_handler = None
156
 
 
157
 
    def reset(self):
158
 
        self.video_view.setMovie_(nil)
159
 
        quicktime.Player.reset(self)
160
 
 
161
 
    def get_video_frame(self):
162
 
        frame = self.view.frame()
163
 
        frame.origin = self.view.convertPoint_toView_(NSZeroPoint, nil)
164
 
        frame.origin.x = 0
165
 
        frame.origin = self.view.window().convertBaseToScreen_(frame.origin)
166
 
        frame.size = NSSize(self.view.window().frame().size.width, frame.size.height)
167
 
        return frame
168
 
 
169
 
    def adjust_video_frame(self):
170
 
        frame = self.get_video_frame()
171
 
        self.video_window.setFrame_display_(frame, YES)
172
 
 
173
 
    def update_for_presentation_mode(self, mode):
174
 
        frame = self.video_view.frame()
175
 
        self.video_view.setFrame_(NSOffsetRect(frame, 1, 1))
176
 
        self.video_view.setFrame_(frame)
177
 
 
178
 
    def set_item(self, item_info, callback, errback, force_subtitles=False):
179
 
        def callback2():
180
 
            self.video_view.setMovie_(self.movie)
181
 
            self.video_view.setNeedsDisplay_(YES)
182
 
            self.video_window.setup(item_info, self)
183
 
            callback()
184
 
        quicktime.Player.set_item(self, item_info, callback2, errback, force_subtitles)
185
 
 
186
 
    def set_volume(self, volume):
187
 
        quicktime.Player.set_volume(self, volume)
188
 
        if self.video_window:
189
 
            self.video_window.palette.set_volume(volume)
190
 
 
191
 
    def play(self):
192
 
        threads.warn_if_not_on_main_thread('VideoPlayer.play')
193
 
        self.video_view.play_(nil)
194
 
        self.video_view.setNeedsDisplay_(YES)
195
 
        self.prevent_system_sleep(True)
196
 
 
197
 
    def pause(self):
198
 
        threads.warn_if_not_on_main_thread('VideoPlayer.pause')
199
 
        self.video_view.pause_(nil)
200
 
        self.prevent_system_sleep(True)
201
 
 
202
 
    def stop(self, will_play_another=False):
203
 
        threads.warn_if_not_on_main_thread('VideoPlayer.stop')
204
 
        self.prevent_system_sleep(True)
205
 
        self.video_view.pause_(nil)
206
 
        if self.video_window and not will_play_another:
207
 
            self.video_window.palette.remove()
208
 
        self.reset()
209
 
 
210
 
    def enter_fullscreen(self):
211
 
        self.video_window.enter_fullscreen()
212
 
 
213
 
    def exit_fullscreen(self):
214
 
        frame = self.get_video_frame()
215
 
        self.video_window.exit_fullscreen(frame)
216
 
 
217
 
    def prepare_switch_to_attached_playback(self):
218
 
        self.video_window.palette.remove()
219
 
        app.widgetapp.window.nswindow.makeKeyAndOrderFront_(nil)
220
 
 
221
 
    def prepare_switch_to_detached_playback(self):
222
 
        self.video_window.palette.remove()
223
 
 
224
 
    def prevent_system_sleep(self, prevent):
225
 
        if prevent and self.system_activity_updater_timer is None:
226
 
            logging.debug("Launching system activity updater timer")
227
 
            self.system_activity_updater_timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
228
 
                30,
229
 
                self.video_window,
230
 
                'updateSystemActivity:',
231
 
                nil,
232
 
                YES)
233
 
        elif not prevent and self.system_activity_updater_timer is not None:
234
 
            logging.debug("Stopping system activity updater timer")
235
 
            self.system_activity_updater_timer.invalidate()
236
 
            self.system_activity_updater_timer = None
237
 
 
238
 
###############################################################################
239
 
 
240
 
class VideoWindow (NSWindow):
241
 
 
242
 
    def initWithContentRect_styleMask_backing_defer_(self, rect, style, backing, defer):
243
 
        self = super(VideoWindow, self).initWithContentRect_styleMask_backing_defer_(rect,  style, backing, defer)
244
 
        self.setBackgroundColor_(NSColor.blackColor())
245
 
        self.setReleasedWhenClosed_(NO)
246
 
        self.setAcceptsMouseMovedEvents_(YES)
247
 
        self.palette = overlay.OverlayPalette.get_instance()
248
 
        self.is_fullscreen = False
249
 
        return self
250
 
 
251
 
    def close(self):
252
 
        self.palette.window().orderOut_(nil)
253
 
        self.palette = None
254
 
        super(VideoWindow, self).close()
255
 
 
256
 
    def on_items_changed(self, changed):
257
 
        if self.palette is not None:
258
 
            self.palette.on_items_changed(changed)
259
 
 
260
 
    def canBecomeMainWindow(self):
261
 
        return NO
262
 
 
263
 
    def canBecomeKeyWindow(self):
264
 
        return NO
265
 
 
266
 
    def setFrame_display_(self, frame, display):
267
 
        super(VideoWindow, self).setFrame_display_(frame, display)
268
 
        if self.palette.window().isVisible():
269
 
            self.palette.adjustPosition(self)
270
 
            if not self.palette.fit_in_video_window(self):
271
 
                self.palette.remove()
272
 
 
273
 
    def setup(self, item_info, renderer):
274
 
        self.palette.setup(item_info, renderer, self)
275
 
 
276
 
    def enter_fullscreen(self):
277
 
        NSCursor.setHiddenUntilMouseMoves_(YES)
278
 
        screens = NSScreen.screens()
279
 
        if len(screens) > 0:
280
 
            screenWithMenuBar = screens[0]
281
 
            if self.screen() == screenWithMenuBar:
282
 
                SetSystemUIMode(kUIModeAllHidden, 0)
283
 
        self.setFrame_display_animate_(self.screen().frame(), YES, YES)
284
 
        self.is_fullscreen = True
285
 
        self.palette.enter_fullscreen(self)
286
 
 
287
 
    def exit_fullscreen(self, frame):
288
 
        NSCursor.setHiddenUntilMouseMoves_(NO)
289
 
        self.is_fullscreen = False
290
 
        self.palette.exit_fullscreen(self)
291
 
        self.setFrame_display_animate_(frame, YES, YES)
292
 
        SetSystemUIMode(kUIModeNormal, 0)
293
 
 
294
 
    def updateSystemActivity_(self, timer):
295
 
        UpdateSystemActivity(OverallActivity)
296
 
 
297
 
    def sendEvent_(self, event):
298
 
        if self.parentWindow() is None:
299
 
            # We've been detached since the event was fired.  Just ignore it.
300
 
            return
301
 
 
302
 
        if event.type() == NSMouseMoved:
303
 
            if NSPointInRect(event.locationInWindow(), self.contentView().bounds()) and self.palette.fit_in_video_window(self):
304
 
                self.palette.reveal(self)
305
 
        elif event.type() == NSLeftMouseDown:
306
 
            if NSApplication.sharedApplication().isActive():
307
 
                if event.clickCount() > 1:
308
 
                    app.playback_manager.toggle_fullscreen()
309
 
                elif not self.parentWindow().isMainWindow():
310
 
                    self.parentWindow().makeKeyAndOrderFront_(nil)
311
 
            else:
312
 
                NSApplication.sharedApplication().activateIgnoringOtherApps_(YES)
313
 
        else:
314
 
            #super(VideoWindow, self).sendEvent_(event)
315
 
            self.parentWindow().sendEvent_(event)
316
 
 
317
 
###############################################################################