summaryrefslogtreecommitdiffstats
path: root/macosx/HBPlayerHUDController.m
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/HBPlayerHUDController.m')
-rw-r--r--macosx/HBPlayerHUDController.m341
1 files changed, 341 insertions, 0 deletions
diff --git a/macosx/HBPlayerHUDController.m b/macosx/HBPlayerHUDController.m
new file mode 100644
index 000000000..03eed5a84
--- /dev/null
+++ b/macosx/HBPlayerHUDController.m
@@ -0,0 +1,341 @@
+/* HBPlayerHUDController.m $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ 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<HBPlayer>)player
+{
+ if (_player)
+ {
+ [self.player removeRateObserver:self.rateObserver];
+ [self.player removeTimeObserver:self.periodicObserver];
+ [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 play];
+ }
+}
+
+- (void)dealloc
+{
+ // Remove observers
+}
+
+#pragma mark - Audio and subtitles selection menu
+
+- (void)_buildTracksMenu
+{
+ [self _clearTracksMenu];
+
+ NSArray<HBPlayerTrack *> *audioTracks = self.player.audioTracks;
+ if (audioTracks.count)
+ {
+ [self _addSectionTitle:NSLocalizedString(@"Audio", nil)];
+ [self _addTracksItemFromArray:audioTracks selector:@selector(enableAudioTrack:)];
+ }
+
+ NSArray<HBPlayerTrack *> *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<HBPlayerTrack *> *)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