2 File: MovieControllerLayer.m
4 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
5 Inc. ("Apple") in consideration of your agreement to the following
6 terms, and your use, installation, modification or redistribution of
7 this Apple software constitutes acceptance of these terms. If you do
8 not agree with these terms, please do not use, install, modify or
9 redistribute this Apple software.
11 In consideration of your agreement to abide by the following terms, and
12 subject to these terms, Apple grants you a personal, non-exclusive
13 license, under Apple's copyrights in this original Apple software (the
14 "Apple Software"), to use, reproduce, modify and redistribute the Apple
15 Software, with or without modifications, in source and/or binary forms;
16 provided that if you redistribute the Apple Software in its entirety and
17 without modifications, you must retain this notice and the following
18 text and disclaimers in all such redistributions of the Apple Software.
19 Neither the name, trademarks, service marks or logos of Apple Inc. may
20 be used to endorse or promote products derived from the Apple Software
21 without specific prior written permission from Apple. Except as
22 expressly stated in this notice, no other rights or licenses, express or
23 implied, are granted by Apple herein, including but not limited to any
24 patent rights that may be infringed by your derivative works or by other
25 works in which the Apple Software may be incorporated.
27 The Apple Software is provided by Apple on an "AS IS" basis. APPLE
28 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
29 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
30 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
31 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
33 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
37 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
38 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
39 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
40 POSSIBILITY OF SUCH DAMAGE.
42 Copyright (C) 2009 Apple Inc. All Rights Reserved.
46 #import "MovieControllerLayer.h"
48 #import <QTKit/QTKit.h>
50 @interface MovieControllerLayer ()
52 - (NSTimeInterval)_currentTime;
53 - (NSTimeInterval)_duration;
56 @implementation MovieControllerLayer
58 static CGImageRef createImageNamed(NSString *name)
60 NSURL *url = [[NSBundle bundleForClass:[MovieControllerLayer class]] URLForResource:name withExtension:@"tiff"];
65 CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
69 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
70 CFRelease(imageSource);
77 if (self = [super init]) {
78 self.needsDisplayOnBoundsChange = YES;
79 self.frame = CGRectMake(0, 0, 0, 25);
80 self.autoresizingMask = kCALayerWidthSizable;
82 _playImage = createImageNamed(@"Play");
83 _pauseImage = createImageNamed(@"Pause");
84 _sliderTrackLeft = createImageNamed(@"SliderTrackLeft");
85 _sliderTrackRight = createImageNamed(@"SliderTrackRight");
86 _sliderTrackCenter = createImageNamed(@"SliderTrackCenter");
88 _thumb = createImageNamed(@"Thumb");
96 CGImageRelease(_playImage);
97 CGImageRelease(_pauseImage);
99 CGImageRelease(_sliderTrackLeft);
100 CGImageRelease(_sliderTrackRight);
101 CGImageRelease(_sliderTrackCenter);
103 CGImageRelease(_thumb);
106 [_updateTimeTimer invalidate];
113 - (CGRect)_playPauseButtonRect
115 return CGRectMake(0, 0, 25, 25);
118 - (CGRect)_sliderRect
120 CGFloat sliderYPosition = (self.bounds.size.height - CGImageGetHeight(_sliderTrackLeft)) / 2.0;
121 CGFloat playPauseButtonWidth = [self _playPauseButtonRect].size.width;
123 return CGRectMake(playPauseButtonWidth, sliderYPosition,
124 self.bounds.size.width - playPauseButtonWidth - 7, CGImageGetHeight(_sliderTrackLeft));
127 - (CGRect)_sliderThumbRect
129 CGRect sliderRect = [self _sliderRect];
131 CGFloat fraction = 0.0;
133 fraction = [self _currentTime] / [self _duration];
135 CGFloat x = fraction * (CGRectGetWidth(sliderRect) - CGImageGetWidth(_thumb));
137 return CGRectMake(CGRectGetMinX(sliderRect) + x, CGRectGetMinY(sliderRect) - 1,
138 CGImageGetWidth(_thumb), CGImageGetHeight(_thumb));
141 - (CGRect)_innerSliderRect
143 return CGRectInset([self _sliderRect], CGRectGetWidth([self _sliderThumbRect]) / 2, 0);
146 - (void)_drawPlayPauseButtonInContext:(CGContextRef)context
148 CGContextDrawImage(context, [self _playPauseButtonRect], [self _isPlaying] ? _pauseImage : _playImage);
151 - (void)_drawSliderInContext:(CGContextRef)context
154 CGRect sliderThumbRect = [self _sliderThumbRect];
155 CGContextDrawImage(context, sliderThumbRect, _thumb);
157 CGRect sliderRect = [self _sliderRect];
160 CGRect sliderLeftTrackRect = CGRectMake(CGRectGetMinX(sliderRect), CGRectGetMinY(sliderRect),
161 CGImageGetWidth(_sliderTrackLeft), CGImageGetHeight(_sliderTrackLeft));
162 CGContextDrawImage(context, sliderLeftTrackRect, _sliderTrackLeft);
165 CGRect sliderCenterTrackRect = CGRectInset(sliderRect, CGImageGetWidth(_sliderTrackLeft), 0);
166 CGContextDrawImage(context, sliderCenterTrackRect, _sliderTrackCenter);
169 CGRect sliderRightTrackRect = CGRectMake(CGRectGetMaxX(sliderCenterTrackRect), CGRectGetMinY(sliderRect),
170 CGImageGetWidth(_sliderTrackRight), CGImageGetHeight(_sliderTrackRight));
171 CGContextDrawImage(context, sliderRightTrackRect, _sliderTrackRight);
175 - (void)drawInContext:(CGContextRef)context
177 CGContextSaveGState(context);
178 CGContextSetFillColorWithColor(context, CGColorGetConstantColor(kCGColorBlack));
179 CGContextFillRect(context, self.bounds);
180 CGContextRestoreGState(context);
182 [self _drawPlayPauseButtonInContext:context];
183 [self _drawSliderInContext:context];
186 #pragma mark Movie handling
188 - (NSTimeInterval)_currentTime
193 QTTime time = [_movie currentTime];
194 NSTimeInterval timeInterval;
195 if (!QTGetTimeInterval(time, &timeInterval))
201 - (NSTimeInterval)_duration
206 QTTime time = [_movie duration];
207 NSTimeInterval timeInterval;
208 if (!QTGetTimeInterval(time, &timeInterval))
216 return [_movie rate] != 0.0;
219 - (void)_updateTime:(NSTimer *)timer
221 [self setNeedsDisplay];
224 - (void)_rateDidChange:(NSNotification *)notification
226 float rate = [[[notification userInfo] objectForKey:QTMovieRateDidChangeNotificationParameter] floatValue];
229 [_updateTimeTimer invalidate];
230 _updateTimeTimer = nil;
232 _updateTimeTimer = [NSTimer scheduledTimerWithTimeInterval:0.035 target:self selector:@selector(_updateTime:) userInfo:nil repeats:YES];
234 [self setNeedsDisplay];
237 - (void)_timeDidChange:(NSNotification *)notification
239 [self setNeedsDisplay];
242 - (id<CAAction>)actionForKey:(NSString *)key
244 // We don't want to animate the contents of the layer.
245 if ([key isEqualToString:@"contents"])
248 return [super actionForKey:key];
251 - (void)setMovie:(QTMovie *)movie
257 [[NSNotificationCenter defaultCenter] removeObserver:self
258 name:QTMovieRateDidChangeNotification
260 [[NSNotificationCenter defaultCenter] removeObserver:self
261 name:QTMovieTimeDidChangeNotification
266 _movie = [movie retain];
269 [[NSNotificationCenter defaultCenter] addObserver:self
270 selector:@selector(_rateDidChange:)
271 name:QTMovieRateDidChangeNotification
273 [[NSNotificationCenter defaultCenter] addObserver:self
274 selector:@selector(_timeDidChange:)
275 name:QTMovieTimeDidChangeNotification
277 [self setNeedsDisplay];
282 # pragma mark Event handling
284 - (void)_setNewTimeForThumbCenterX:(CGFloat)centerX
286 CGRect innerRect = [self _innerSliderRect];
288 CGFloat fraction = (centerX - CGRectGetMinX(innerRect)) / CGRectGetWidth(innerRect);
291 else if (fraction < 0.0)
294 NSTimeInterval newTime = fraction * [self _duration];
296 [_movie setCurrentTime:QTMakeTimeWithTimeInterval(newTime)];
297 [self setNeedsDisplay];
300 - (void)handleMouseDown:(CGPoint)point
305 if (CGRectContainsPoint([self _sliderRect], point)) {
306 _wasPlayingBeforeMouseDown = [self _isPlaying];
310 if (CGRectContainsPoint([self _sliderThumbRect], point))
311 _mouseDownXDelta = point.x - CGRectGetMidX([self _sliderThumbRect]);
313 [self _setNewTimeForThumbCenterX:point.x];
314 _mouseDownXDelta = 0;
319 - (void)handleMouseUp:(CGPoint)point
326 _mouseDownXDelta = 0;
328 if (_wasPlayingBeforeMouseDown)
333 if (CGRectContainsPoint([self _playPauseButtonRect], point)) {
334 if ([self _isPlaying])
342 - (void)handleMouseDragged:(CGPoint)point
350 point.x -= _mouseDownXDelta;
352 [self _setNewTimeForThumbCenterX:point.x];