/* HBPlayerHUDController.m $
This file is part of the HandBrake source code.
Homepage: .
It may be used under the terms of the GNU General Public License. */
#import "HBPlayerHUDController.h"
@interface HBPlayerHUDController ()
@property (weak) IBOutlet NSButton *playButton;
@property (weak) IBOutlet NSSlider *slider;
@property (weak) IBOutlet NSSlider *volumeSlider;
@property (weak) IBOutlet NSTextField *currentTimeLabel;
@property (weak) IBOutlet NSTextField *remaingTimeLabel;
@property (weak) IBOutlet NSPopUpButton *tracksSelection;
@property (nonatomic, readonly) NSDictionary *monospacedAttr;
@property (nonatomic, readwrite) id rateObserver;
@property (nonatomic, readwrite) id periodicObserver;
@end
@implementation HBPlayerHUDController
- (NSString *)nibName
{
return @"HBPlayerHUDController";
}
- (void)loadView
{
[super loadView];
if (NSClassFromString(@"NSVisualEffectView") == NO)
{
self.currentTimeLabel.textColor = [NSColor whiteColor];
self.remaingTimeLabel.textColor = [NSColor whiteColor];
}
if ([[NSFont class] respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) {
_monospacedAttr = @{NSFontAttributeName: [NSFont monospacedDigitSystemFontOfSize:[NSFont smallSystemFontSize] weight:NSFontWeightRegular]};
}
else {
_monospacedAttr = @{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]};
}
}
- (BOOL)canBeHidden
{
return YES;
}
- (void)setPlayer:(id)player
{
if (_player)
{
[self.player removeRateObserver:self.rateObserver];
[self.player removeTimeObserver:self.periodicObserver];
self.rateObserver = nil;
self.periodicObserver = nil;
[self _clearTracksMenu];
}
_player = player;
if (player)
{
[self _buildTracksMenu];
// 10.7 does not supports weak NSViewController,
// so use self and disable the warning for now.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
self.periodicObserver = [self.player addPeriodicTimeObserverUsingBlock:^(NSTimeInterval time) {
[self _refreshUI];
}];
self.rateObserver = [self.player addRateObserverUsingBlock:^{
if (self.player.rate != 0.0)
{
self.playButton.image = [NSImage imageNamed:@"PauseTemplate"];
}
else
{
self.playButton.image = [NSImage imageNamed:@"PlayTemplate"];
}
}];
[self.slider setMinValue:0.0];
[self.slider setMaxValue:self.player.duration];
[self.slider setDoubleValue:0.0];
self.player.volume = self.volumeSlider.floatValue;
[self.player play];
}
}
- (void)dealloc
{
if (_rateObserver)
{
[_player removeRateObserver:_rateObserver];
_rateObserver = nil;
}
if (_periodicObserver)
{
[_player removeTimeObserver:_periodicObserver];
_periodicObserver = nil;
}
}
#pragma mark - Audio and subtitles selection menu
- (void)_buildTracksMenu
{
[self _clearTracksMenu];
NSArray *audioTracks = self.player.audioTracks;
if (audioTracks.count)
{
[self _addSectionTitle:NSLocalizedString(@"Audio", nil)];
[self _addTracksItemFromArray:audioTracks selector:@selector(enableAudioTrack:)];
}
NSArray *subtitlesTracks = self.player.subtitlesTracks;
if (subtitlesTracks.count)
{
if (audioTracks.count)
{
[self.tracksSelection.menu addItem:[NSMenuItem separatorItem]];
}
[self _addSectionTitle:NSLocalizedString(@"Subtitles", nil)];
[self _addTracksItemFromArray:subtitlesTracks selector:@selector(enableSubtitlesTrack:)];
}
}
- (void)_clearTracksMenu
{
for (NSMenuItem *item in [self.tracksSelection.menu.itemArray copy])
{
if (item.tag != 1)
{
[self.tracksSelection.menu removeItem:item];
}
}
}
- (void)_addSectionTitle:(NSString *)title
{
NSMenuItem *sectionTitle = [[NSMenuItem alloc] init];
sectionTitle.title = title;
sectionTitle.enabled = NO;
sectionTitle.indentationLevel = 0;
[self.tracksSelection.menu addItem:sectionTitle];
}
- (void)_addTracksItemFromArray:(NSArray *)tracks selector:(SEL)selector
{
for (HBPlayerTrack *track in tracks)
{
NSMenuItem *item = [[NSMenuItem alloc] init];
item.title = track.name;
item.enabled = YES;
item.indentationLevel = 1;
item.action = selector;
item.target = self;
item.state = track.enabled;
item.representedObject = track;
[self.tracksSelection.menu addItem:item];
}
}
- (void)_updateTracksMenuState
{
for (NSMenuItem *item in self.tracksSelection.menu.itemArray)
{
if (item.representedObject)
{
HBPlayerTrack *track = (HBPlayerTrack *)item.representedObject;
item.state = track.enabled;
}
}
}
- (IBAction)enableAudioTrack:(NSMenuItem *)sender
{
[self.player enableAudioTrack:sender.representedObject];
[self _updateTracksMenuState];
}
- (IBAction)enableSubtitlesTrack:(NSMenuItem *)sender
{
[self.player enableSubtitlesTrack:sender.representedObject];
[self _updateTracksMenuState];
}
- (NSString *)_timeToTimecode:(NSTimeInterval)timeInSeconds
{
UInt16 seconds = (UInt16)fmod(timeInSeconds, 60.0);
UInt16 minutes = (UInt16)fmod(timeInSeconds / 60.0, 60.0);
UInt16 milliseconds = (UInt16)((timeInSeconds - (int) timeInSeconds) * 1000);
return [NSString stringWithFormat:@"%02d:%02d.%03d", minutes, seconds, milliseconds];
}
- (NSAttributedString *)_monospacedString:(NSString *)string
{
return [[NSAttributedString alloc] initWithString:string attributes:self.monospacedAttr];
}
- (void)_refreshUI
{
if (self.player)
{
NSTimeInterval currentTime = self.player.currentTime;
NSTimeInterval duration = self.player.duration;
self.slider.doubleValue = currentTime;
self.currentTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToTimecode:currentTime]];
self.remaingTimeLabel.attributedStringValue = [self _monospacedString:[self _timeToTimecode:duration - currentTime]];
}
}
- (IBAction)playPauseToggle:(id)sender
{
if (self.player.rate != 0.0)
{
[self.player pause];
}
else
{
[self.player play];
}
}
- (IBAction)goToBeginning:(id)sender
{
[self.player gotoBeginning];
}
- (IBAction)goToEnd:(id)sender
{
[self.player gotoEnd];
}
- (IBAction)showPicturesPreview:(id)sender
{
[self.delegate stopPlayer];
}
- (IBAction)sliderChanged:(NSSlider *)sender
{
self.player.currentTime = sender.doubleValue;
}
- (IBAction)maxVolume:(id)sender
{
self.volumeSlider.doubleValue = 1;
self.player.volume = 1;
}
- (IBAction)mute:(id)sender
{
self.volumeSlider.doubleValue = 0;
self.player.volume = 0;
}
- (IBAction)volumeSliderChanged:(NSSlider *)sender
{
self.player.volume = sender.floatValue;
}
#pragma mark - Keyboard and mouse wheel control
- (BOOL)HB_keyDown:(NSEvent *)event
{
unichar key = [event.charactersIgnoringModifiers characterAtIndex:0];
if (self.player)
{
if (key == 32)
{
if (self.player.rate != 0.0)
[self.player pause];
else
[self.player play];
}
else if (key == 'k')
[self.player pause];
else if (key == 'l')
{
float rate = self.player.rate;
rate += 1.0f;
[self.player play];
self.player.rate = rate;
}
else if (key == 'j')
{
float rate = self.player.rate;
rate -= 1.0f;
[self.player play];
self.player.rate = rate;
}
else if (event.modifierFlags & NSAlternateKeyMask && key == NSLeftArrowFunctionKey)
{
[self.player gotoBeginning];
}
else if (event.modifierFlags & NSAlternateKeyMask && key == NSRightArrowFunctionKey)
{
[self.player gotoEnd];
}
else if (key == NSLeftArrowFunctionKey)
{
[self.player stepBackward];
}
else if (key == NSRightArrowFunctionKey)
{
[self.player stepForward];
}
else
{
return NO;
}
}
else
{
return NO;
}
return YES;
}
- (BOOL)HB_scrollWheel:(NSEvent *)theEvent;
{
if (theEvent.deltaY < 0)
{
self.player.currentTime = self.player.currentTime + 0.5;
}
else if (theEvent.deltaY > 0)
{
self.player.currentTime = self.player.currentTime - 0.5;
}
return YES;
}
@end