/* 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