| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2009 Apple Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * 1. Redistributions of source code must retain the above copyright | |
| 8 * notice, this list of conditions and the following disclaimer. | |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer in the | |
| 11 * documentation and/or other materials provided with the distribution. | |
| 12 * | |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND AN
Y | |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR AN
Y | |
| 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND O
N | |
| 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 23 */ | |
| 24 | |
| 25 #import "config.h" | |
| 26 | |
| 27 #if ENABLE(VIDEO) | |
| 28 | |
| 29 #import "WebVideoFullscreenHUDWindowController.h" | |
| 30 | |
| 31 #import "ExceptionCodePlaceholder.h" | |
| 32 #import "FloatConversion.h" | |
| 33 #import <WebCoreSystemInterface.h> | |
| 34 #import <WebCore/HTMLMediaElement.h> | |
| 35 #import <wtf/RetainPtr.h> | |
| 36 #import <wtf/UnusedParam.h> | |
| 37 | |
| 38 using namespace WebCore; | |
| 39 using namespace std; | |
| 40 | |
| 41 static inline CGFloat webkit_CGFloor(CGFloat value) | |
| 42 { | |
| 43 if (sizeof(value) == sizeof(float)) | |
| 44 return floorf(value); | |
| 45 return floor(value); | |
| 46 } | |
| 47 | |
| 48 #define HAVE_MEDIA_CONTROL (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) | |
| 49 | |
| 50 @interface WebVideoFullscreenHUDWindowController (Private) | |
| 51 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 | |
| 52 <NSWindowDelegate> | |
| 53 #endif | |
| 54 | |
| 55 - (void)updateTime; | |
| 56 - (void)timelinePositionChanged:(id)sender; | |
| 57 - (float)currentTime; | |
| 58 - (void)setCurrentTime:(float)currentTime; | |
| 59 - (double)duration; | |
| 60 | |
| 61 - (void)volumeChanged:(id)sender; | |
| 62 - (float)maxVolume; | |
| 63 - (float)volume; | |
| 64 - (void)setVolume:(float)volume; | |
| 65 - (void)decrementVolume; | |
| 66 - (void)incrementVolume; | |
| 67 | |
| 68 - (void)updatePlayButton; | |
| 69 - (void)togglePlaying:(id)sender; | |
| 70 - (BOOL)playing; | |
| 71 - (void)setPlaying:(BOOL)playing; | |
| 72 | |
| 73 - (void)rewind:(id)sender; | |
| 74 - (void)fastForward:(id)sender; | |
| 75 | |
| 76 - (NSString *)remainingTimeText; | |
| 77 - (NSString *)elapsedTimeText; | |
| 78 | |
| 79 - (void)exitFullscreen:(id)sender; | |
| 80 @end | |
| 81 | |
| 82 @interface WebVideoFullscreenHUDWindow : NSWindow | |
| 83 @end | |
| 84 | |
| 85 @implementation WebVideoFullscreenHUDWindow | |
| 86 | |
| 87 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backi
ng:(NSBackingStoreType)bufferingType defer:(BOOL)flag | |
| 88 { | |
| 89 UNUSED_PARAM(aStyle); | |
| 90 self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMa
sk backing:bufferingType defer:flag]; | |
| 91 if (!self) | |
| 92 return nil; | |
| 93 | |
| 94 [self setOpaque:NO]; | |
| 95 [self setBackgroundColor:[NSColor clearColor]]; | |
| 96 [self setLevel:NSPopUpMenuWindowLevel]; | |
| 97 [self setAcceptsMouseMovedEvents:YES]; | |
| 98 [self setIgnoresMouseEvents:NO]; | |
| 99 [self setMovableByWindowBackground:YES]; | |
| 100 | |
| 101 return self; | |
| 102 } | |
| 103 | |
| 104 - (BOOL)canBecomeKeyWindow | |
| 105 { | |
| 106 return YES; | |
| 107 } | |
| 108 | |
| 109 - (void)cancelOperation:(id)sender | |
| 110 { | |
| 111 UNUSED_PARAM(sender); | |
| 112 [[self windowController] exitFullscreen:self]; | |
| 113 } | |
| 114 | |
| 115 - (void)center | |
| 116 { | |
| 117 NSRect hudFrame = [self frame]; | |
| 118 NSRect screenFrame = [[NSScreen mainScreen] frame]; | |
| 119 [self setFrameTopLeftPoint:NSMakePoint(screenFrame.origin.x + (screenFrame.s
ize.width - hudFrame.size.width) / 2, | |
| 120 screenFrame.origin.y + (screenFrame.s
ize.height - hudFrame.size.height) / 6)]; | |
| 121 } | |
| 122 | |
| 123 - (void)keyDown:(NSEvent *)event | |
| 124 { | |
| 125 [super keyDown:event]; | |
| 126 [[self windowController] fadeWindowIn]; | |
| 127 } | |
| 128 | |
| 129 - (BOOL)resignFirstResponder | |
| 130 { | |
| 131 return NO; | |
| 132 } | |
| 133 | |
| 134 - (BOOL)performKeyEquivalent:(NSEvent *)event | |
| 135 { | |
| 136 // Block all command key events while the fullscreen window is up. | |
| 137 if ([event type] != NSKeyDown) | |
| 138 return NO; | |
| 139 | |
| 140 if (!([event modifierFlags] & NSCommandKeyMask)) | |
| 141 return NO; | |
| 142 | |
| 143 return YES; | |
| 144 } | |
| 145 | |
| 146 @end | |
| 147 | |
| 148 static const CGFloat windowHeight = 59; | |
| 149 static const CGFloat windowWidth = 438; | |
| 150 | |
| 151 static const NSTimeInterval HUDWindowFadeOutDelay = 3; | |
| 152 | |
| 153 @implementation WebVideoFullscreenHUDWindowController | |
| 154 | |
| 155 - (id)init | |
| 156 { | |
| 157 NSWindow *window = [[WebVideoFullscreenHUDWindow alloc] initWithContentRect:
NSMakeRect(0, 0, windowWidth, windowHeight) | |
| 158 styleMask:NSBorderlessWindowMask backing:NSBackingSt
oreBuffered defer:NO]; | |
| 159 self = [super initWithWindow:window]; | |
| 160 [window setDelegate:self]; | |
| 161 [window release]; | |
| 162 if (!self) | |
| 163 return nil; | |
| 164 [self windowDidLoad]; | |
| 165 return self; | |
| 166 } | |
| 167 | |
| 168 - (void)dealloc | |
| 169 { | |
| 170 ASSERT(!_timelineUpdateTimer); | |
| 171 ASSERT(!_area); | |
| 172 ASSERT(!_isScrubbing); | |
| 173 [_timeline release]; | |
| 174 [_remainingTimeText release]; | |
| 175 [_elapsedTimeText release]; | |
| 176 [_volumeSlider release]; | |
| 177 [_playButton release]; | |
| 178 [super dealloc]; | |
| 179 } | |
| 180 | |
| 181 - (void)setArea:(NSTrackingArea *)area | |
| 182 { | |
| 183 if (area == _area) | |
| 184 return; | |
| 185 [_area release]; | |
| 186 _area = [area retain]; | |
| 187 } | |
| 188 | |
| 189 - (void)keyDown:(NSEvent *)event | |
| 190 { | |
| 191 NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers]; | |
| 192 if ([charactersIgnoringModifiers length] == 1) { | |
| 193 switch ([charactersIgnoringModifiers characterAtIndex:0]) { | |
| 194 case ' ': | |
| 195 [self togglePlaying:nil]; | |
| 196 return; | |
| 197 case NSUpArrowFunctionKey: | |
| 198 if ([event modifierFlags] & NSAlternateKeyMask) | |
| 199 [self setVolume:[self maxVolume]]; | |
| 200 else | |
| 201 [self incrementVolume]; | |
| 202 return; | |
| 203 case NSDownArrowFunctionKey: | |
| 204 if ([event modifierFlags] & NSAlternateKeyMask) | |
| 205 [self setVolume:0]; | |
| 206 else | |
| 207 [self decrementVolume]; | |
| 208 return; | |
| 209 default: | |
| 210 break; | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 [super keyDown:event]; | |
| 215 } | |
| 216 | |
| 217 - (id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate | |
| 218 { | |
| 219 return _delegate; | |
| 220 } | |
| 221 | |
| 222 - (void)setDelegate:(id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate | |
| 223 { | |
| 224 _delegate = delegate; | |
| 225 } | |
| 226 | |
| 227 - (void)scheduleTimeUpdate | |
| 228 { | |
| 229 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(un
scheduleTimeUpdate) object:self]; | |
| 230 | |
| 231 // First, update right away, then schedule future update | |
| 232 [self updateTime]; | |
| 233 [self updatePlayButton]; | |
| 234 | |
| 235 [_timelineUpdateTimer invalidate]; | |
| 236 [_timelineUpdateTimer release]; | |
| 237 | |
| 238 // Note that this creates a retain cycle between the window and us. | |
| 239 _timelineUpdateTimer = [[NSTimer timerWithTimeInterval:0.25 target:self sele
ctor:@selector(updateTime) userInfo:nil repeats:YES] retain]; | |
| 240 [[NSRunLoop currentRunLoop] addTimer:_timelineUpdateTimer forMode:NSRunLoopC
ommonModes]; | |
| 241 } | |
| 242 | |
| 243 - (void)unscheduleTimeUpdate | |
| 244 { | |
| 245 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(un
scheduleTimeUpdate) object:nil]; | |
| 246 | |
| 247 [_timelineUpdateTimer invalidate]; | |
| 248 [_timelineUpdateTimer release]; | |
| 249 _timelineUpdateTimer = nil; | |
| 250 } | |
| 251 | |
| 252 - (void)fadeWindowIn | |
| 253 { | |
| 254 NSWindow *window = [self window]; | |
| 255 if (![window isVisible]) | |
| 256 [window setAlphaValue:0]; | |
| 257 | |
| 258 [window makeKeyAndOrderFront:self]; | |
| 259 [[window animator] setAlphaValue:1]; | |
| 260 [self scheduleTimeUpdate]; | |
| 261 | |
| 262 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fa
deWindowOut) object:nil]; | |
| 263 if (!_mouseIsInHUD && [self playing]) // Don't fade out when paused. | |
| 264 [self performSelector:@selector(fadeWindowOut) withObject:nil afterDelay
:HUDWindowFadeOutDelay]; | |
| 265 } | |
| 266 | |
| 267 - (void)fadeWindowOut | |
| 268 { | |
| 269 [NSCursor setHiddenUntilMouseMoves:YES]; | |
| 270 [[[self window] animator] setAlphaValue:0]; | |
| 271 [self performSelector:@selector(unscheduleTimeUpdate) withObject:nil afterDe
lay:1]; | |
| 272 } | |
| 273 | |
| 274 - (void)closeWindow | |
| 275 { | |
| 276 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fa
deWindowOut) object:nil]; | |
| 277 [self unscheduleTimeUpdate]; | |
| 278 NSWindow *window = [self window]; | |
| 279 [[window contentView] removeTrackingArea:_area]; | |
| 280 [self setArea:nil]; | |
| 281 [window close]; | |
| 282 [window setDelegate:nil]; | |
| 283 [self setWindow:nil]; | |
| 284 } | |
| 285 | |
| 286 #ifndef HAVE_MEDIA_CONTROL | |
| 287 // FIXME: This code is never compiled, because HAVE_MEDIA_CONTROL is always defi
ned to something, even on Leopard. | |
| 288 // FIXME: Values in this enum have a different order than ones in WKMediaUIContr
olType. | |
| 289 enum { | |
| 290 WKMediaUIControlPlayPauseButton, | |
| 291 WKMediaUIControlRewindButton, | |
| 292 WKMediaUIControlFastForwardButton, | |
| 293 WKMediaUIControlExitFullscreenButton, | |
| 294 WKMediaUIControlVolumeDownButton, | |
| 295 WKMediaUIControlSlider, | |
| 296 WKMediaUIControlVolumeUpButton, | |
| 297 WKMediaUIControlTimeline | |
| 298 }; | |
| 299 #endif | |
| 300 | |
| 301 static NSControl *createControlWithMediaUIControlType(int controlType, NSRect fr
ame) | |
| 302 { | |
| 303 #ifdef HAVE_MEDIA_CONTROL | |
| 304 NSControl *control = wkCreateMediaUIControl(controlType); | |
| 305 [control setFrame:frame]; | |
| 306 return control; | |
| 307 #else | |
| 308 // FIXME: This code is never compiled, because HAVE_MEDIA_CONTROL is always
defined to something, even on Leopard. | |
| 309 if (controlType == wkMediaUIControlSlider) | |
| 310 return [[NSSlider alloc] initWithFrame:frame]; | |
| 311 return [[NSControl alloc] initWithFrame:frame]; | |
| 312 #endif | |
| 313 } | |
| 314 | |
| 315 static NSTextField *createTimeTextField(NSRect frame) | |
| 316 { | |
| 317 NSTextField *textField = [[NSTextField alloc] initWithFrame:frame]; | |
| 318 [textField setTextColor:[NSColor whiteColor]]; | |
| 319 [textField setBordered:NO]; | |
| 320 [textField setFont:[NSFont boldSystemFontOfSize:10]]; | |
| 321 [textField setDrawsBackground:NO]; | |
| 322 [textField setBezeled:NO]; | |
| 323 [textField setEditable:NO]; | |
| 324 [textField setSelectable:NO]; | |
| 325 return textField; | |
| 326 } | |
| 327 | |
| 328 - (void)windowDidLoad | |
| 329 { | |
| 330 static const CGFloat horizontalMargin = 10; | |
| 331 static const CGFloat playButtonWidth = 41; | |
| 332 static const CGFloat playButtonHeight = 35; | |
| 333 static const CGFloat playButtonTopMargin = 4; | |
| 334 static const CGFloat volumeSliderWidth = 50; | |
| 335 static const CGFloat volumeSliderHeight = 13; | |
| 336 static const CGFloat volumeButtonWidth = 18; | |
| 337 static const CGFloat volumeButtonHeight = 16; | |
| 338 static const CGFloat volumeUpButtonLeftMargin = 4; | |
| 339 static const CGFloat volumeControlsTopMargin = 13; | |
| 340 static const CGFloat exitFullscreenButtonWidth = 25; | |
| 341 static const CGFloat exitFullscreenButtonHeight = 21; | |
| 342 static const CGFloat exitFullscreenButtonTopMargin = 11; | |
| 343 static const CGFloat timelineWidth = 315; | |
| 344 static const CGFloat timelineHeight = 14; | |
| 345 static const CGFloat timelineBottomMargin = 7; | |
| 346 static const CGFloat timeTextFieldWidth = 54; | |
| 347 static const CGFloat timeTextFieldHeight = 13; | |
| 348 static const CGFloat timeTextFieldHorizontalMargin = 7; | |
| 349 | |
| 350 NSWindow *window = [self window]; | |
| 351 ASSERT(window); | |
| 352 | |
| 353 #ifdef HAVE_MEDIA_CONTROL | |
| 354 NSView *background = wkCreateMediaUIBackgroundView(); | |
| 355 #else | |
| 356 // FIXME: This code is never compiled, because HAVE_MEDIA_CONTROL is always
defined to something, even on Leopard. | |
| 357 NSView *background = [[NSView alloc] init]; | |
| 358 #endif | |
| 359 [window setContentView:background]; | |
| 360 _area = [[NSTrackingArea alloc] initWithRect:[background bounds] options:NST
rackingMouseEnteredAndExited | NSTrackingActiveAlways owner:self userInfo:nil]; | |
| 361 [background addTrackingArea:_area]; | |
| 362 [background release]; | |
| 363 | |
| 364 NSView *contentView = [window contentView]; | |
| 365 | |
| 366 CGFloat center = webkit_CGFloor((windowWidth - playButtonWidth) / 2); | |
| 367 _playButton = (NSButton *)createControlWithMediaUIControlType(wkMediaUIContr
olPlayPauseButton, NSMakeRect(center, windowHeight - playButtonTopMargin - playB
uttonHeight, playButtonWidth, playButtonHeight)); | |
| 368 ASSERT([_playButton isKindOfClass:[NSButton class]]); | |
| 369 [_playButton setTarget:self]; | |
| 370 [_playButton setAction:@selector(togglePlaying:)]; | |
| 371 [contentView addSubview:_playButton]; | |
| 372 | |
| 373 CGFloat closeToRight = windowWidth - horizontalMargin - exitFullscreenButton
Width; | |
| 374 NSControl *exitFullscreenButton = createControlWithMediaUIControlType(wkMedi
aUIControlExitFullscreenButton, NSMakeRect(closeToRight, windowHeight - exitFull
screenButtonTopMargin - exitFullscreenButtonHeight, exitFullscreenButtonWidth, e
xitFullscreenButtonHeight)); | |
| 375 [exitFullscreenButton setAction:@selector(exitFullscreen:)]; | |
| 376 [exitFullscreenButton setTarget:self]; | |
| 377 [contentView addSubview:exitFullscreenButton]; | |
| 378 [exitFullscreenButton release]; | |
| 379 | |
| 380 CGFloat volumeControlsBottom = windowHeight - volumeControlsTopMargin - volu
meButtonHeight; | |
| 381 CGFloat left = horizontalMargin; | |
| 382 NSControl *volumeDownButton = createControlWithMediaUIControlType(wkMediaUIC
ontrolVolumeDownButton, NSMakeRect(left, volumeControlsBottom, volumeButtonWidth
, volumeButtonHeight)); | |
| 383 [contentView addSubview:volumeDownButton]; | |
| 384 [volumeDownButton setTarget:self]; | |
| 385 [volumeDownButton setAction:@selector(setVolumeToZero:)]; | |
| 386 [volumeDownButton release]; | |
| 387 | |
| 388 left += volumeButtonWidth; | |
| 389 _volumeSlider = createControlWithMediaUIControlType(wkMediaUIControlSlider,
NSMakeRect(left, volumeControlsBottom + webkit_CGFloor((volumeButtonHeight - vol
umeSliderHeight) / 2), volumeSliderWidth, volumeSliderHeight)); | |
| 390 [_volumeSlider setValue:[NSNumber numberWithDouble:[self maxVolume]] forKey:
@"maxValue"]; | |
| 391 [_volumeSlider setTarget:self]; | |
| 392 [_volumeSlider setAction:@selector(volumeChanged:)]; | |
| 393 [contentView addSubview:_volumeSlider]; | |
| 394 | |
| 395 left += volumeSliderWidth + volumeUpButtonLeftMargin; | |
| 396 NSControl *volumeUpButton = createControlWithMediaUIControlType(wkMediaUICon
trolVolumeUpButton, NSMakeRect(left, volumeControlsBottom, volumeButtonWidth, vo
lumeButtonHeight)); | |
| 397 [volumeUpButton setTarget:self]; | |
| 398 [volumeUpButton setAction:@selector(setVolumeToMaximum:)]; | |
| 399 [contentView addSubview:volumeUpButton]; | |
| 400 [volumeUpButton release]; | |
| 401 | |
| 402 #ifdef HAVE_MEDIA_CONTROL | |
| 403 _timeline = wkCreateMediaUIControl(wkMediaUIControlTimeline); | |
| 404 #else | |
| 405 // FIXME: This code is never compiled, because HAVE_MEDIA_CONTROL is always
defined to something, even on Leopard. | |
| 406 _timeline = [[NSSlider alloc] init]; | |
| 407 #endif | |
| 408 [_timeline setTarget:self]; | |
| 409 [_timeline setAction:@selector(timelinePositionChanged:)]; | |
| 410 [_timeline setFrame:NSMakeRect(webkit_CGFloor((windowWidth - timelineWidth)
/ 2), timelineBottomMargin, timelineWidth, timelineHeight)]; | |
| 411 [contentView addSubview:_timeline]; | |
| 412 | |
| 413 _elapsedTimeText = createTimeTextField(NSMakeRect(timeTextFieldHorizontalMar
gin, timelineBottomMargin, timeTextFieldWidth, timeTextFieldHeight)); | |
| 414 [_elapsedTimeText setAlignment:NSLeftTextAlignment]; | |
| 415 [contentView addSubview:_elapsedTimeText]; | |
| 416 | |
| 417 _remainingTimeText = createTimeTextField(NSMakeRect(windowWidth - timeTextFi
eldHorizontalMargin - timeTextFieldWidth, timelineBottomMargin, timeTextFieldWid
th, timeTextFieldHeight)); | |
| 418 [_remainingTimeText setAlignment:NSRightTextAlignment]; | |
| 419 [contentView addSubview:_remainingTimeText]; | |
| 420 | |
| 421 [window recalculateKeyViewLoop]; | |
| 422 [window setInitialFirstResponder:_playButton]; | |
| 423 [window center]; | |
| 424 } | |
| 425 | |
| 426 - (void)updateVolume | |
| 427 { | |
| 428 [_volumeSlider setFloatValue:[self volume]]; | |
| 429 } | |
| 430 | |
| 431 - (void)updateTime | |
| 432 { | |
| 433 [self updateVolume]; | |
| 434 | |
| 435 [_timeline setFloatValue:[self currentTime]]; | |
| 436 [_timeline setValue:[NSNumber numberWithDouble:[self duration]] forKey:@"max
Value"]; | |
| 437 | |
| 438 [_remainingTimeText setStringValue:[self remainingTimeText]]; | |
| 439 [_elapsedTimeText setStringValue:[self elapsedTimeText]]; | |
| 440 } | |
| 441 | |
| 442 - (void)endScrubbing | |
| 443 { | |
| 444 ASSERT(_isScrubbing); | |
| 445 _isScrubbing = NO; | |
| 446 if (HTMLMediaElement* mediaElement = [_delegate mediaElement]) | |
| 447 mediaElement->endScrubbing(); | |
| 448 } | |
| 449 | |
| 450 - (void)timelinePositionChanged:(id)sender | |
| 451 { | |
| 452 UNUSED_PARAM(sender); | |
| 453 [self setCurrentTime:[_timeline floatValue]]; | |
| 454 if (!_isScrubbing) { | |
| 455 _isScrubbing = YES; | |
| 456 if (HTMLMediaElement* mediaElement = [_delegate mediaElement]) | |
| 457 mediaElement->beginScrubbing(); | |
| 458 static NSArray *endScrubbingModes = [[NSArray alloc] initWithObjects:NSD
efaultRunLoopMode, NSModalPanelRunLoopMode, nil]; | |
| 459 // Schedule -endScrubbing for when leaving mouse tracking mode. | |
| 460 [[NSRunLoop currentRunLoop] performSelector:@selector(endScrubbing) targ
et:self argument:nil order:0 modes:endScrubbingModes]; | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 - (float)currentTime | |
| 465 { | |
| 466 return [_delegate mediaElement] ? [_delegate mediaElement]->currentTime() :
0; | |
| 467 } | |
| 468 | |
| 469 - (void)setCurrentTime:(float)currentTime | |
| 470 { | |
| 471 if (![_delegate mediaElement]) | |
| 472 return; | |
| 473 [_delegate mediaElement]->setCurrentTime(currentTime, IGNORE_EXCEPTION); | |
| 474 [self updateTime]; | |
| 475 } | |
| 476 | |
| 477 - (double)duration | |
| 478 { | |
| 479 return [_delegate mediaElement] ? [_delegate mediaElement]->duration() : 0; | |
| 480 } | |
| 481 | |
| 482 - (float)maxVolume | |
| 483 { | |
| 484 // Set the volume slider resolution | |
| 485 return 100; | |
| 486 } | |
| 487 | |
| 488 - (void)volumeChanged:(id)sender | |
| 489 { | |
| 490 UNUSED_PARAM(sender); | |
| 491 [self setVolume:[_volumeSlider floatValue]]; | |
| 492 } | |
| 493 | |
| 494 - (void)setVolumeToZero:(id)sender | |
| 495 { | |
| 496 UNUSED_PARAM(sender); | |
| 497 [self setVolume:0]; | |
| 498 } | |
| 499 | |
| 500 - (void)setVolumeToMaximum:(id)sender | |
| 501 { | |
| 502 UNUSED_PARAM(sender); | |
| 503 [self setVolume:[self maxVolume]]; | |
| 504 } | |
| 505 | |
| 506 - (void)decrementVolume | |
| 507 { | |
| 508 if (![_delegate mediaElement]) | |
| 509 return; | |
| 510 | |
| 511 float volume = [self volume] - 10; | |
| 512 [self setVolume:MAX(volume, 0)]; | |
| 513 } | |
| 514 | |
| 515 - (void)incrementVolume | |
| 516 { | |
| 517 if (![_delegate mediaElement]) | |
| 518 return; | |
| 519 | |
| 520 float volume = [self volume] + 10; | |
| 521 [self setVolume:min(volume, [self maxVolume])]; | |
| 522 } | |
| 523 | |
| 524 - (float)volume | |
| 525 { | |
| 526 return [_delegate mediaElement] ? [_delegate mediaElement]->volume() * [self
maxVolume] : 0; | |
| 527 } | |
| 528 | |
| 529 - (void)setVolume:(float)volume | |
| 530 { | |
| 531 if (![_delegate mediaElement]) | |
| 532 return; | |
| 533 if ([_delegate mediaElement]->muted()) | |
| 534 [_delegate mediaElement]->setMuted(false); | |
| 535 [_delegate mediaElement]->setVolume(volume / [self maxVolume], IGNORE_EXCEPT
ION); | |
| 536 [self updateVolume]; | |
| 537 } | |
| 538 | |
| 539 - (void)updatePlayButton | |
| 540 { | |
| 541 [_playButton setIntValue:[self playing]]; | |
| 542 } | |
| 543 | |
| 544 - (void)updateRate | |
| 545 { | |
| 546 BOOL playing = [self playing]; | |
| 547 | |
| 548 // Keep the HUD visible when paused. | |
| 549 if (!playing) | |
| 550 [self fadeWindowIn]; | |
| 551 else if (!_mouseIsInHUD) { | |
| 552 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selecto
r(fadeWindowOut) object:nil]; | |
| 553 [self performSelector:@selector(fadeWindowOut) withObject:nil afterDelay
:HUDWindowFadeOutDelay]; | |
| 554 } | |
| 555 [self updatePlayButton]; | |
| 556 } | |
| 557 | |
| 558 - (void)togglePlaying:(id)sender | |
| 559 { | |
| 560 UNUSED_PARAM(sender); | |
| 561 [self setPlaying:![self playing]]; | |
| 562 } | |
| 563 | |
| 564 - (BOOL)playing | |
| 565 { | |
| 566 HTMLMediaElement* mediaElement = [_delegate mediaElement]; | |
| 567 if (!mediaElement) | |
| 568 return NO; | |
| 569 | |
| 570 return !mediaElement->canPlay(); | |
| 571 } | |
| 572 | |
| 573 - (void)setPlaying:(BOOL)playing | |
| 574 { | |
| 575 HTMLMediaElement* mediaElement = [_delegate mediaElement]; | |
| 576 | |
| 577 if (!mediaElement) | |
| 578 return; | |
| 579 | |
| 580 if (playing) | |
| 581 mediaElement->play(); | |
| 582 else | |
| 583 mediaElement->pause(); | |
| 584 } | |
| 585 | |
| 586 static NSString *timeToString(double time) | |
| 587 { | |
| 588 ASSERT_ARG(time, time >= 0); | |
| 589 | |
| 590 if (!std::isfinite(time)) | |
| 591 time = 0; | |
| 592 | |
| 593 int seconds = narrowPrecisionToFloat(abs(time)); | |
| 594 int hours = seconds / (60 * 60); | |
| 595 int minutes = (seconds / 60) % 60; | |
| 596 seconds %= 60; | |
| 597 | |
| 598 if (hours) | |
| 599 return [NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, secon
ds]; | |
| 600 | |
| 601 return [NSString stringWithFormat:@"%02d:%02d", minutes, seconds]; | |
| 602 } | |
| 603 | |
| 604 - (NSString *)remainingTimeText | |
| 605 { | |
| 606 HTMLMediaElement* mediaElement = [_delegate mediaElement]; | |
| 607 if (!mediaElement) | |
| 608 return @""; | |
| 609 | |
| 610 return [@"-" stringByAppendingString:timeToString(mediaElement->duration() -
mediaElement->currentTime())]; | |
| 611 } | |
| 612 | |
| 613 - (NSString *)elapsedTimeText | |
| 614 { | |
| 615 if (![_delegate mediaElement]) | |
| 616 return @""; | |
| 617 | |
| 618 return timeToString([_delegate mediaElement]->currentTime()); | |
| 619 } | |
| 620 | |
| 621 // MARK: NSResponder | |
| 622 | |
| 623 - (void)mouseEntered:(NSEvent *)theEvent | |
| 624 { | |
| 625 UNUSED_PARAM(theEvent); | |
| 626 // Make sure the HUD won't be hidden from now | |
| 627 _mouseIsInHUD = YES; | |
| 628 [self fadeWindowIn]; | |
| 629 } | |
| 630 | |
| 631 - (void)mouseExited:(NSEvent *)theEvent | |
| 632 { | |
| 633 UNUSED_PARAM(theEvent); | |
| 634 _mouseIsInHUD = NO; | |
| 635 [self fadeWindowIn]; | |
| 636 } | |
| 637 | |
| 638 - (void)rewind:(id)sender | |
| 639 { | |
| 640 UNUSED_PARAM(sender); | |
| 641 if (![_delegate mediaElement]) | |
| 642 return; | |
| 643 [_delegate mediaElement]->rewind(30); | |
| 644 } | |
| 645 | |
| 646 - (void)fastForward:(id)sender | |
| 647 { | |
| 648 UNUSED_PARAM(sender); | |
| 649 if (![_delegate mediaElement]) | |
| 650 return; | |
| 651 } | |
| 652 | |
| 653 - (void)exitFullscreen:(id)sender | |
| 654 { | |
| 655 UNUSED_PARAM(sender); | |
| 656 if (_isEndingFullscreen) | |
| 657 return; | |
| 658 _isEndingFullscreen = YES; | |
| 659 [_delegate requestExitFullscreen]; | |
| 660 } | |
| 661 | |
| 662 // MARK: NSWindowDelegate | |
| 663 | |
| 664 - (void)windowDidExpose:(NSNotification *)notification | |
| 665 { | |
| 666 UNUSED_PARAM(notification); | |
| 667 [self scheduleTimeUpdate]; | |
| 668 } | |
| 669 | |
| 670 - (void)windowDidClose:(NSNotification *)notification | |
| 671 { | |
| 672 UNUSED_PARAM(notification); | |
| 673 [self unscheduleTimeUpdate]; | |
| 674 } | |
| 675 | |
| 676 @end | |
| 677 | |
| 678 #endif | |
| OLD | NEW |