]> granicus.if.org Git - handbrake/commitdiff
MacGui: move some code out of HBPreviewController.m. Remember the windows size when...
authorDamiano Galassi <damiog@gmail.com>
Tue, 6 Oct 2015 17:52:42 +0000 (19:52 +0200)
committerDamiano Galassi <damiog@gmail.com>
Tue, 6 Oct 2015 17:52:42 +0000 (19:52 +0200)
macosx/English.lproj/PicturePreview.xib
macosx/HBPreviewController.m
macosx/HBPreviewView.h [new file with mode: 0644]
macosx/HBPreviewView.m [new file with mode: 0644]
macosx/HandBrake.xcodeproj/project.pbxproj
macosx/QTKit+HBQTMovieExtensions.h [new file with mode: 0644]
macosx/QTKit+HBQTMovieExtensions.m [new file with mode: 0644]

index 708b0d8b7df24352d8c4f65ccc2fd1f498444255..afb3b3dcb8fb54afd8cfd1dda5617df47420cb5a 100644 (file)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8164.2" systemVersion="15A225f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9052" systemVersion="15B22c" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
     <dependencies>
         <deployment identifier="macosx"/>
         <development version="6300" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8164.2"/>
-        <plugIn identifier="com.apple.QTKitIBPlugin" version="8164.2"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9052"/>
+        <plugIn identifier="com.apple.QTKitIBPlugin" version="9052"/>
     </dependencies>
     <objects>
         <customObject id="-2" userLabel="File's Owner" customClass="HBPreviewController">
                 <outlet property="fPreviewMovieStatusField" destination="223" id="225"/>
                 <outlet property="fScaleToScreenToggleButton" destination="275" id="yX0-fL-6J9"/>
                 <outlet property="fscaleInfoField" destination="280" id="282"/>
+                <outlet property="previewView" destination="ooo-9X-9Al" id="als-Lt-aVz"/>
                 <outlet property="window" destination="5" id="184"/>
             </connections>
         </customObject>
         <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
         <customObject id="-3" userLabel="Application" customClass="NSObject"/>
-        <window title="Preview" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="5" userLabel="PreviewPanel">
+        <window title="Preview" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="Preview" animationBehavior="default" id="5" userLabel="PreviewPanel">
             <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
-            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
-            <rect key="contentRect" x="221" y="837" width="490" height="360"/>
+            <rect key="contentRect" x="221" y="837" width="500" height="360"/>
             <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
+            <value key="minSize" type="size" width="480" height="360"/>
             <view key="contentView" id="6">
-                <rect key="frame" x="0.0" y="0.0" width="490" height="360"/>
+                <rect key="frame" x="0.0" y="0.0" width="500" height="360"/>
                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                 <subviews>
+                    <customView id="ooo-9X-9Al" customClass="HBPreviewView">
+                        <rect key="frame" x="0.0" y="0.0" width="500" height="360"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                    </customView>
                     <qtMovieView preservesAspectRatio="YES" id="207">
-                        <rect key="frame" x="0.0" y="0.0" width="490" height="360"/>
+                        <rect key="frame" x="0.0" y="0.0" width="500" height="360"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <animations/>
                         <color key="fillColor" red="0.80000000999999998" green="0.80000000999999998" blue="0.80000000999999998" alpha="1" colorSpace="calibratedRGB"/>
                     </qtMovieView>
                     <customView id="2me-4k-EDi" userLabel="Picture Controls" customClass="HBHUDView">
-                        <rect key="frame" x="15" y="136" width="460" height="100"/>
+                        <rect key="frame" x="20" y="136" width="460" height="100"/>
                         <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
                             <button toolTip="Encode And Play Back A Live Preview At Your Current Settings" verticalHuggingPriority="750" id="215">
                         <animations/>
                     </customView>
                     <customView hidden="YES" id="COi-Ia-2yt" userLabel="Playback Controls" customClass="HBHUDView">
-                        <rect key="frame" x="15" y="32" width="460" height="100"/>
+                        <rect key="frame" x="20" y="32" width="460" height="100"/>
                         <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
                             <slider verticalHuggingPriority="750" id="341">
                         <animations/>
                     </customView>
                     <customView hidden="YES" id="F8A-dU-Y1l" userLabel="Encoding Controls" customClass="HBHUDView">
-                        <rect key="frame" x="15" y="240" width="460" height="100"/>
+                        <rect key="frame" x="20" y="240" width="460" height="100"/>
                         <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
                             <textField verticalHuggingPriority="750" id="223">
                 </subviews>
                 <animations/>
             </view>
+            <connections>
+                <outlet property="delegate" destination="-2" id="7iq-HC-WuX"/>
+            </connections>
+            <point key="canvasLocation" x="-182" y="-40"/>
         </window>
     </objects>
     <resources>
index 9be3bae5bfeeea5cb03b3008b9252024019c6c7d..642661750298bf5a20a08100ee719b202077155c 100644 (file)
@@ -8,53 +8,17 @@
 #import "HBPreviewGenerator.h"
 #import "HBPictureController.h"
 
-#import <QTKit/QTKit.h>
-
-@implementation QTMovieView (HBQTMovieViewExtensions)
-
-- (void) mouseMoved: (NSEvent *) theEvent
-{
-    [super mouseMoved:theEvent];
-}
-
-@end
-
-@implementation QTMovie (HBQTMovieExtensions)
+#import "HBPreviewView.h"
 
-- (BOOL) isPlaying
-{
-    if ([self rate] > 0)
-        return YES;
-    else
-        return NO;
-}
-
-- (NSString *) timecode
-{
-    QTTime time = [self currentTime];
-    double timeInSeconds = (double)time.timeValue / time.timeScale;
-       UInt16 seconds = (UInt16)fmod(timeInSeconds, 60.0);
-       UInt16 minutes = (UInt16)fmod(timeInSeconds / 60.0, 60.0);
-       UInt16 hours = (UInt16)(timeInSeconds / (60.0 * 60.0));
-       UInt16 milliseconds = (UInt16)(timeInSeconds - (int) timeInSeconds) * 1000;
-       return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds];
-}
-
-- (void) setCurrentTimeDouble: (double) value
-{
-       long timeScale = [[self attributeForKey:QTMovieTimeScaleAttribute] longValue];
-       [self setCurrentTime:QTMakeTime((long long)value * timeScale, timeScale)];
-}
+#import <QTKit/QTKit.h>
+#import "QTKit+HBQTMovieExtensions.h"
 
-@end
+#define ANIMATION_DUR 0.15
 
-#define BORDER_SIZE 2.0
 // make min width and height of preview window large enough for hud
 #define MIN_WIDTH 480.0
 #define MIN_HEIGHT 360.0
 
-#define ANIMATION_DUR 0.15
-
 typedef enum ViewMode : NSUInteger {
     ViewModePicturePreview,
     ViewModeEncoding,
@@ -91,13 +55,8 @@ typedef enum ViewMode : NSUInteger {
 
 @property (nonatomic, readwrite) HBPictureController *pictureSettingsWindow;
 
-@property (nonatomic, strong) CALayer *backLayer;
-@property (nonatomic, strong) CALayer *pictureLayer;
-
-@property (nonatomic) CGFloat backingScaleFactor;
-
 @property (nonatomic) ViewMode currentViewMode;
-@property (nonatomic) BOOL scaleToScreen;
+@property (nonatomic) NSPoint windowCenterPoint;
 
 @property (nonatomic, strong) NSTimer *hudTimer;
 
@@ -106,21 +65,7 @@ typedef enum ViewMode : NSUInteger {
 @property (nonatomic, strong) QTMovie *movie;
 @property (nonatomic, strong) NSTimer *movieTimer;
 
-/* Pictures HUD actions */
-- (IBAction) previewDurationPopUpChanged: (id) sender;
-- (IBAction) pictureSliderChanged: (id) sender;
-- (IBAction) showPictureSettings:(id)sender;
-- (IBAction) toggleScaleToScreen:(id)sender;
-
-- (IBAction) cancelCreateMoviePreview: (id) sender;
-- (IBAction) createMoviePreview: (id) sender;
-
-/* Movie HUD actions */
-- (IBAction) showPicturesPreview: (id) sender;
-- (IBAction) toggleMoviePreviewPlayPause: (id) sender;
-- (IBAction) moviePlaybackGoToBeginning: (id) sender;
-- (IBAction) moviePlaybackGoToEnd: (id) sender;
-- (IBAction) previewScrubberChanged: (id) sender;
+@property (weak) IBOutlet HBPreviewView *previewView;
 
 @end
 
@@ -134,83 +79,49 @@ typedef enum ViewMode : NSUInteger {
 
 - (void)windowDidLoad
 {
-    [[self window] setDelegate:self];
-
-    if( ![[self window] setFrameUsingName:@"Preview"] )
-        [[self window] center];
-
-    [self setWindowFrameAutosaveName:@"Preview"];
-    [[self window] setExcludedFromWindowsMenu:YES];
-
-    /* lets set the preview window to accept mouse moved events */
-    [[self window] setAcceptsMouseMovedEvents:YES];
+    self.window.contentView.wantsLayer = YES;
 
-    /* we set the progress indicator to not use threaded animation
-     * as it causes a conflict with the qtmovieview's controllerbar
-     */
-    [fMovieCreationProgressIndicator setUsesThreadedAnimation:NO];
+    self.windowCenterPoint = [self centerPoint];
 
-       [fMovieView setHidden:YES];
-    [fMovieView setDelegate:self];
-    [fMovieView setControllerVisible:NO];
+    self.window.excludedFromWindowsMenu = YES;
+    self.window.acceptsMouseMovedEvents = YES;
 
-    /* we set the preview length popup in seconds */
+    // we set the preview length popup in seconds
     [fPreviewMovieLengthPopUp removeAllItems];
     [fPreviewMovieLengthPopUp addItemsWithTitles:@[@"15", @"30", @"45", @"60", @"90",
                                                    @"120", @"150", @"180", @"210", @"240"]];
 
     if ([[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"])
+    {
         [fPreviewMovieLengthPopUp selectItemWithTitle:[[NSUserDefaults standardUserDefaults]
                                                        objectForKey:@"PreviewLength"]];
-    if (![fPreviewMovieLengthPopUp selectedItem])
-        /* currently hard set default to 15 seconds */
+    }
+    if (!fPreviewMovieLengthPopUp.selectedItem)
+    {
+        // currently hard set default to 15 seconds
         [fPreviewMovieLengthPopUp selectItemAtIndex: 0];
+    }
 
-    /* Setup our layers for core animation */
-    [[[self window] contentView] setWantsLayer:YES];
-
-    self.backLayer = [CALayer layer];
-    [self.backLayer setBounds:CGRectMake(0.0, 0.0, MIN_WIDTH, MIN_HEIGHT)];
-    [self.backLayer setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
-                                            [[[self window] contentView] frame].size.height /2)];
-
-    [self.backLayer setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
-    CGColorRef white = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0);
-    [self.backLayer setBackgroundColor: white];
-    CFRelease(white);
-    [self.backLayer setShadowOpacity:0.5f];
-    [self.backLayer setShadowOffset:CGSizeMake(0, 0)];
-
-    self.pictureLayer = [CALayer layer];
-    [self.pictureLayer setBounds:CGRectMake(0.0, 0.0, MIN_WIDTH - (BORDER_SIZE * 2), MIN_HEIGHT - (BORDER_SIZE * 2))];
-    [self.pictureLayer setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
-                                               [[[self window] contentView] frame].size.height /2)];
-
-    [self.pictureLayer setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
-
-    // Disable fade on contents change
-    NSMutableDictionary *actions = [NSMutableDictionary
-                                    dictionaryWithDictionary:[self.pictureLayer actions]];
-
-    actions[@"contents"] = [NSNull null];
-    [self.pictureLayer setActions:actions];
+    // Relocate our hud origins.
+    NSPoint hudControlBoxOrigin = fMoviePlaybackControlBox.frame.origin;
+    fPictureControlBox.frameOrigin = hudControlBoxOrigin;
+    fEncodingControlBox.frameOrigin = hudControlBoxOrigin;
+    fMoviePlaybackControlBox.frameOrigin = hudControlBoxOrigin;
 
-    [[[[self window] contentView] layer] insertSublayer:self.backLayer below: [fMovieView layer]];
-    [[[[self window] contentView] layer] insertSublayer:self.pictureLayer below: [fMovieView layer]];
+    [self hideHud];
 
-    /* relocate our hud origins */
-    NSPoint hudControlBoxOrigin = [fMoviePlaybackControlBox frame].origin;
-    [fPictureControlBox setFrameOrigin:hudControlBoxOrigin];
-    [fEncodingControlBox setFrameOrigin:hudControlBoxOrigin];
-    [fMoviePlaybackControlBox setFrameOrigin:hudControlBoxOrigin];
+    fMovieView.hidden = YES;
+    fMovieView.delegate = self;
+    [fMovieView setControllerVisible:NO];
+}
 
-    [self hideHud];
+- (void)dealloc
+{
+    [self removeMovieObservers];
 
-    /* set the current scale factor */
-    if( [[self window] respondsToSelector:@selector( backingScaleFactor )] )
-        self.backingScaleFactor = [[self window] backingScaleFactor];
-    else
-        self.backingScaleFactor = 1.0;
+    [_hudTimer invalidate];
+    [_movieTimer invalidate];
+    [_generator cancel];
 }
 
 - (void)setGenerator:(HBPreviewGenerator *)generator
@@ -237,25 +148,25 @@ typedef enum ViewMode : NSUInteger {
         }
 
         [self switchViewToMode:ViewModePicturePreview];
-        [self displayPreview];
+        [self displayPreviewAtIndex:self.pictureIndex];
     }
     else
     {
-        [self.pictureLayer setContents:nil];
+        [self.previewView setImage:nil];
         self.window.title = NSLocalizedString(@"Preview", nil);
     }
 }
 
-- (void) reloadPreviews
+- (void)reloadPreviews
 {
     if (self.generator)
     {
         [self switchViewToMode:ViewModePicturePreview];
-        [self displayPreview];
+        [self displayPreviewAtIndex:self.pictureIndex];
     }
 }
 
-- (void) showWindow: (id) sender
+- (void)showWindow:(id)sender
 {
     [super showWindow:sender];
 
@@ -269,7 +180,7 @@ typedef enum ViewMode : NSUInteger {
     }
 }
 
-- (void) windowWillClose: (NSNotification *) aNotification
+- (void)windowWillClose:(NSNotification *)aNotification
 {
     if (self.currentViewMode == ViewModeEncoding)
     {
@@ -285,7 +196,7 @@ typedef enum ViewMode : NSUInteger {
     [self.generator purgeImageCache];
 }
 
-- (void) windowDidChangeBackingProperties: (NSNotification *) notification
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification
 {
     NSWindow *theWindow = (NSWindow *)[notification object];
 
@@ -297,55 +208,35 @@ typedef enum ViewMode : NSUInteger {
     {
         // Scale factor changed, update the preview window
         // to the new situation
-        self.backingScaleFactor = newBackingScaleFactor;
         if (self.generator)
+        {
             [self reloadPreviews];
+        }
     }
 }
 
+#pragma mark - Window sizing
+
 /**
- * Given the size of the preview image to be shown, returns the best possible
- * size for the view.
+ *  Calculates and returns the center point of the window
  */
-- (NSSize) optimalViewSizeForImageSize: (NSSize) imageSize
-{
-    CGFloat minWidth = MIN_WIDTH;
-    CGFloat minHeight = MIN_HEIGHT;
-
-    NSSize screenSize = [[[self window] screen] visibleFrame].size;
-    CGFloat maxWidth = screenSize.width;
-    CGFloat maxHeight = screenSize.height;
-
-    NSSize resultSize = imageSize;
-    CGFloat resultPar = resultSize.width / resultSize.height;
-
-    //note, a mbp 15" at 1440 x 900 is a 1.6 ar
-    CGFloat screenAspect = screenSize.width / screenSize.height;
+- (NSPoint)centerPoint {
+    NSPoint center = NSMakePoint(floor(self.window.frame.origin.x + self.window.frame.size.width / 2),
+                                 floor(self.window.frame.origin.y + self.window.frame.size.height / 2));
+    return center;
+}
 
-    if ( resultSize.width > maxWidth || resultSize.height > maxHeight )
+- (void)windowDidMove:(NSNotification *)notification
+{
+    if (self.previewView.fitToView == NO)
     {
-       // Source is larger than screen in one or more dimensions
-        if ( resultPar > screenAspect )
-        {
-            // Source aspect wider than screen aspect, snap to max width and vary height
-            resultSize.width = maxWidth;
-            resultSize.height = (maxWidth / resultPar);
-        }
-        else
-        {
-            // Source aspect narrower than screen aspect, snap to max height vary width
-            resultSize.height = maxHeight;
-            resultSize.width = (maxHeight * resultPar);
-        }
+        self.windowCenterPoint = [self centerPoint];
     }
+}
 
-    // If necessary, grow to minimum dimensions to ensure controls overlay is not obstructed
-    if ( resultSize.width < minWidth )
-        resultSize.width = minWidth;
-    if ( resultSize.height < minHeight )
-        resultSize.height = minHeight;
-
-    return resultSize;
+- (void)windowDidResize:(NSNotification *)notification
+{
+    [self updateSizeLabels];
 }
 
 /**
@@ -353,22 +244,20 @@ typedef enum ViewMode : NSUInteger {
  */
 - (void)resizeWindowForViewSize:(NSSize)viewSize animate:(BOOL)performAnimation
 {
-    NSSize currentSize = [[[self window] contentView] frame].size;
-    NSRect frame = [[self window] frame];
+    NSWindow *window = self.window;
+    NSSize currentSize = window.contentView.frame.size;
+    NSRect frame = window.frame;
 
     // Calculate border around content region of the frame
     int borderX = (int)(frame.size.width - currentSize.width);
     int borderY = (int)(frame.size.height - currentSize.height);
 
     // Make sure the frame is smaller than the screen
-    NSSize maxSize = [[[self window] screen] visibleFrame].size;
+    NSSize maxSize = window.screen.visibleFrame.size;
 
-    /* if we are not Scale To Screen, put an 10% of visible screen on the window */
-    if (self.scaleToScreen == NO)
-    {
-        maxSize.width = maxSize.width * 0.90;
-        maxSize.height = maxSize.height * 0.90;
-    }
+    // if we are not Scale To Screen, put an 10% of visible screen on the window
+    maxSize.width = maxSize.width * 0.90;
+    maxSize.height = maxSize.height * 0.90;
 
     // Set the new frame size
     // Add the border to the new frame size so that the content region
@@ -376,55 +265,75 @@ typedef enum ViewMode : NSUInteger {
     frame.size.width = viewSize.width + borderX;
     frame.size.height = viewSize.height + borderY;
 
-    /* compare frame to max size of screen */
-    if( frame.size.width > maxSize.width )
+    // compare frame to max size of screen
+    if (frame.size.width > maxSize.width)
     {
         frame.size.width = maxSize.width;
     }
-    if( frame.size.height > maxSize.height )
+    if (frame.size.height > maxSize.height)
     {
         frame.size.height = maxSize.height;
     }
 
-    /* Since upon launch we can open up the preview window if it was open
-     * the last time we quit (and at the size it was) we want to make
-     * sure that upon resize we do not have the window off the screen
-     * So check the origin against the screen origin and adjust if
-     * necessary.
-     */
-    NSSize screenSize = [[[self window] screen] visibleFrame].size;
-    NSPoint screenOrigin = [[[self window] screen] visibleFrame].origin;
+    // Since upon launch we can open up the preview window if it was open
+    // the last time we quit (and at the size it was) we want to make
+    // sure that upon resize we do not have the window off the screen
+    // So check the origin against the screen origin and adjust if
+    // necessary.
+    NSSize screenSize = window.screen.visibleFrame.size;
+    NSPoint screenOrigin = window.screen.visibleFrame.origin;
+
+    frame.origin.x = self.windowCenterPoint.x - floor(frame.size.width / 2);
+    frame.origin.y = self.windowCenterPoint.y - floor(frame.size.height / 2);
 
-    /* our origin is off the screen to the left*/
+    // our origin is off the screen to the left
     if (frame.origin.x < screenOrigin.x)
     {
-        /* so shift our origin to the right */
+        // so shift our origin to the right
         frame.origin.x = screenOrigin.x;
     }
     else if ((frame.origin.x + frame.size.width) > (screenOrigin.x + screenSize.width))
     {
-        /* the right side of the preview is off the screen, so shift to the left */
+        // the right side of the preview is off the screen, so shift to the left
         frame.origin.x = (screenOrigin.x + screenSize.width) - frame.size.width;
     }
 
-    if (self.scaleToScreen == YES)
+    [window setFrame:frame display:YES animate:performAnimation];
+}
+
+- (void)updateSizeLabels
+{
+    if (self.generator)
     {
-        /* our origin is off the screen to the top*/
-        if (frame.origin.y < screenOrigin.y)
+        CGFloat scale = self.previewView.scale;
+
+        NSMutableString *scaleString = [NSMutableString string];
+        if (scale * 100.0 != 100)
         {
-            /* so shift our origin to the bottom */
-            frame.origin.y = screenOrigin.y;
+            [scaleString appendFormat:NSLocalizedString(@"(%.0f%% actual size)", nil), scale * 100.0];
         }
-        else if ((frame.origin.y + frame.size.height) > (screenOrigin.y + screenSize.height))
+        else
         {
-            /* the top side of the preview is off the screen, so shift to the bottom */
-            frame.origin.y = (screenOrigin.y + screenSize.height) - frame.size.height;
+            [scaleString appendString:NSLocalizedString(@"(Actual size)", nil)];
+        }
+
+        if (self.previewView.fitToView == YES)
+        {
+            [scaleString appendString:NSLocalizedString(@" Scaled To Screen", nil)];
         }
-    }
 
-    [[self window] setFrame:frame display:YES animate:performAnimation];
+        // Set the info fields in the hud controller
+        fInfoField.stringValue = self.generator.info;
+        fscaleInfoField.stringValue = scaleString;
+
+        // Set the info field in the window title bar
+        self.window.title = [NSString stringWithFormat:NSLocalizedString(@"Preview - %@ %@", nil),
+                             self.generator.info, scaleString];
+    }
 }
 
+#pragma mark - Hud mode
+
 /**
  * Enable/Disable an arbitrary number of UI elements.
  * @param boxes an array of UI elements
@@ -469,7 +378,7 @@ typedef enum ViewMode : NSUInteger {
             }
             else if (self.currentViewMode == ViewModeMoviePreview)
             {
-                /* Stop playback and remove the observers */
+                // Stop playback and remove the observers
                 [fMovieView pause:self];
                 [self stopMovieTimer];
                 [self removeMovieObservers];
@@ -504,7 +413,7 @@ typedef enum ViewMode : NSUInteger {
             [self initPreviewScrubberForMovie];
             [self startMovieTimer];
 
-            /* Install movie notifications */
+            // Install movie notifications
             [self addMovieObservers];
         }
             break;
@@ -516,19 +425,10 @@ typedef enum ViewMode : NSUInteger {
     self.currentViewMode = mode;
 }
 
-- (void) dealloc
-{
-    [self removeMovieObservers];
-
-    [_hudTimer invalidate];
-    [_movieTimer invalidate];
-    [_generator cancel];
-}
-
 #pragma mark -
 #pragma mark Hud Control Overlay
 
-- (void) mouseMoved: (NSEvent *) theEvent
+- (void)mouseMoved:(NSEvent *)theEvent
 {
     [super mouseMoved:theEvent];
     NSPoint mouseLoc = [theEvent locationInWindow];
@@ -621,7 +521,7 @@ typedef enum ViewMode : NSUInteger {
     [fEncodingControlBox setHidden:YES];
 }
 
-- (void) startHudTimer
+- (void)startHudTimer
 {
        if (self.hudTimer)
     {
@@ -634,17 +534,16 @@ typedef enum ViewMode : NSUInteger {
     }
 }
 
-- (void) stopHudTimer
+- (void)stopHudTimer
 {
     [self.hudTimer invalidate];
     self.hudTimer = nil;
 }
 
-- (void) hudTimerFired: (NSTimer *)theTimer
+- (void)hudTimerFired: (NSTimer *)theTimer
 {
-    /* Regardless which control box is active, after the timer
-     * period we want either one to fade to hidden.
-     */
+    // Regardless which control box is active, after the timer
+    // period we want either one to fade to hidden.
     [self hideHudWithAnimation:fPictureControlBox];
     [self hideHudWithAnimation:fMoviePlaybackControlBox];
 
@@ -658,146 +557,58 @@ typedef enum ViewMode : NSUInteger {
  * Adjusts the window to draw the current picture (fPicture) adjusting its size as
  * necessary to display as much of the picture as possible.
  */
-- (void) displayPreview
+- (void)displayPreviewAtIndex:(NSUInteger)idx
 {
     if (self.window.isVisible)
     {
-        CGImageRef fPreviewImage = [self.generator copyImageAtIndex:self.pictureIndex shouldCache:YES];
-        [self.pictureLayer setContents:(__bridge id)(fPreviewImage)];
+        CGImageRef fPreviewImage = [self.generator copyImageAtIndex:idx shouldCache:YES];
+        [self.previewView setImage:fPreviewImage];
         CFRelease(fPreviewImage);
     }
 
-    // Set the picture size display fields below the Preview Picture
-    NSSize imageScaledSize = [self.generator imageSize];
-
-    if (self.backingScaleFactor != 1.0)
+    if (self.previewView.fitToView == NO && !(self.window.styleMask & NSFullScreenWindowMask))
     {
-        // HiDPI mode usually display everything
-        // with douple pixel count, but we don't
-        // want to double the size of the video
-        imageScaledSize.height /= self.backingScaleFactor;
-        imageScaledSize.width /= self.backingScaleFactor;
-    }
+        // Get the optimal view size for the image
+        NSSize imageScaledSize = [self.generator imageSize];
 
-    // Get the optimal view size for the image
-    NSSize viewSize = [self optimalViewSizeForImageSize:imageScaledSize];
-    viewSize.width += BORDER_SIZE * 2;
-    viewSize.height += BORDER_SIZE * 2;
-
-    NSSize windowSize;
-    if (self.scaleToScreen == YES)
-    {
-        // Scale the window to the max possible size
-        windowSize = [[[self window] screen] visibleFrame].size;
-    }
-    else
-    {
         // Scale the window to the image size
-        windowSize = viewSize;
+        NSSize windowSize = [self.previewView optimalViewSizeForImageSize:imageScaledSize minSize:NSMakeSize(MIN_WIDTH, MIN_HEIGHT)];
+        [self resizeWindowForViewSize:windowSize animate:self.window.isVisible];
     }
 
-    [self resizeWindowForViewSize:windowSize animate:self.window.isVisible];
-    NSSize areaSize = [[[self window] contentView] frame].size;
-    areaSize.width -= BORDER_SIZE * 2;
-    areaSize.height -= BORDER_SIZE * 2;
-
-    if (self.scaleToScreen == YES)
-    {
-        // We are in Scale To Screen mode so, we have to get the ratio for height and width against the window
-        // size so we can scale from there.
-        CGFloat pictureAspectRatio = imageScaledSize.width / imageScaledSize.height;
-        CGFloat areaAspectRatio = areaSize.width / areaSize.height;
-
-        if (pictureAspectRatio > areaAspectRatio)
-        {
-            viewSize.width = areaSize.width;
-            viewSize.height = viewSize.width / pictureAspectRatio;
-        }
-        else
-        {
-            viewSize.height = areaSize.height;
-            viewSize.width = viewSize.height * pictureAspectRatio;
-        }
-    }
-    else
-    {
-        // If the image is larger then the window, scale the image
-        viewSize = imageScaledSize;
-
-        if (imageScaledSize.width > areaSize.width || imageScaledSize.height > areaSize.height)
-        {
-            CGFloat pictureAspectRatio = imageScaledSize.width / imageScaledSize.height;
-            CGFloat areaAspectRatio = areaSize.width / areaSize.height;
-
-            if (pictureAspectRatio > areaAspectRatio)
-            {
-                viewSize.width = areaSize.width;
-                viewSize.height = viewSize.width / pictureAspectRatio;
-            }
-            else
-            {
-                viewSize.height = areaSize.height;
-                viewSize.width = viewSize.height * pictureAspectRatio;
-            }
-        }
-    }
-
-    // Resize the CALayers
-    [self.backLayer setBounds:CGRectMake(0, 0, viewSize.width + (BORDER_SIZE * 2), viewSize.height + (BORDER_SIZE * 2))];
-    [self.pictureLayer setBounds:CGRectMake(0, 0, viewSize.width, viewSize.height)];
-
-    CGFloat scale = self.pictureLayer.frame.size.width / imageScaledSize.width;
-    NSString *scaleString;
-    if (scale * 100.0 != 100)
-    {
-        scaleString = [NSString stringWithFormat:@" (%.0f%% actual size)", scale * 100.0];
-    }
-    else
-    {
-        scaleString = @"(Actual size)";
-    }
-
-    if (_scaleToScreen == YES)
-    {
-        scaleString = [scaleString stringByAppendingString:@" Scaled To Screen"];
-    }
-
-    // Set the info fields in the hud controller
-    [fInfoField setStringValue:self.generator.info];
-    [fscaleInfoField setStringValue:scaleString];
-
-    // Set the info field in the window title bar
-    [self.window setTitle:[NSString stringWithFormat:@"Preview - %@ %@", self.generator.info, scaleString]];
+    [self updateSizeLabels];
 }
 
-- (IBAction) previewDurationPopUpChanged: (id) sender
+- (IBAction)previewDurationPopUpChanged:(id)sender
 {
     [[NSUserDefaults standardUserDefaults] setObject:[fPreviewMovieLengthPopUp titleOfSelectedItem] forKey:@"PreviewLength"];
 }
 
-- (IBAction) pictureSliderChanged: (id) sender
+- (IBAction)pictureSliderChanged:(id)sender
 {
     if ((self.pictureIndex != [fPictureSlider intValue] || !sender) && self.generator) {
         self.pictureIndex = [fPictureSlider intValue];
-        [self displayPreview];
+        [self displayPreviewAtIndex:self.pictureIndex];
     }
 }
 
-- (IBAction) toggleScaleToScreen: (id) sender
+- (IBAction)toggleScaleToScreen:(id)sender
 {
-    if (self.scaleToScreen == YES)
+    if (self.previewView.fitToView == YES)
     {
-        self.scaleToScreen = NO;
-        /* make sure we are set to a still preview */
-        [self displayPreview];
-        [fScaleToScreenToggleButton setTitle:@"Scale To Screen"];
+        self.previewView.fitToView = NO;
+        fScaleToScreenToggleButton.title = NSLocalizedString(@"Scale To Screen", nil);
+
+        [self displayPreviewAtIndex:self.pictureIndex];
     }
     else
     {
-        self.scaleToScreen = YES;
-        /* make sure we are set to a still preview */
-        [self displayPreview];
-        [fScaleToScreenToggleButton setTitle:@"Actual Scale"];
+        self.previewView.fitToView = YES;
+        if (!(self.window.styleMask & NSFullScreenWindowMask))
+        {
+            [self.window setFrame:self.window.screen.visibleFrame display:YES animate:YES];
+        }
+        fScaleToScreenToggleButton.title = NSLocalizedString(@"Actual Scale", nil);
     }
 }
 
@@ -834,11 +645,11 @@ typedef enum ViewMode : NSUInteger {
     {
                NSError *outError;
                NSDictionary *movieAttributes = @{QTMovieURLAttribute: fileURL,
-                                          QTMovieAskUnresolvedDataRefsAttribute: @(NO),
-                                          @"QTMovieOpenForPlaybackAttribute": @(YES),
-                                          @"QTMovieOpenAsyncRequiredAttribute": @(NO),
-                                          @"QTMovieOpenAsyncOKAttribute": @(NO),
-                                          @"QTMovieIsSteppableAttribute": @(YES),
+                                          QTMovieAskUnresolvedDataRefsAttribute: @NO,
+                                          @"QTMovieOpenForPlaybackAttribute": @YES,
+                                          @"QTMovieOpenAsyncRequiredAttribute": @NO,
+                                          @"QTMovieOpenAsyncOKAttribute": @NO,
+                                          @"QTMovieIsSteppableAttribute": @YES,
                                           QTMovieApertureModeAttribute: QTMovieApertureModeClean};
 
         QTMovie *movie = [[QTMovie alloc] initWithAttributes:movieAttributes error:&outError];
@@ -860,29 +671,28 @@ typedef enum ViewMode : NSUInteger {
                }
         else
         {
-            /* Scale the fMovieView to the picture player size */
-            [fMovieView setFrameSize:[self.pictureLayer frame].size];
-            [fMovieView setFrameOrigin:[self.pictureLayer frame].origin];
+            // Scale the fMovieView to the picture player size
+            [fMovieView setFrame:self.previewView.pictureFrame];
 
             [fMovieView setMovie:movie];
             [movie setDelegate:self];
 
             // get and enable subtitles
-            NSArray *subtitlesArray = [movie tracksOfMediaType: @"sbtl"];
-            if (subtitlesArray && [subtitlesArray count])
+            NSArray *subtitlesArray = [movie tracksOfMediaType:QTMediaTypeSubtitle];
+            if (subtitlesArray.count)
             {
                 // enable the first tx3g subtitle track
-                [subtitlesArray[0] setEnabled: YES];
+                [subtitlesArray[0] setEnabled:YES];
             }
             else
             {
                 // Perian subtitles
                 subtitlesArray = [movie tracksOfMediaType: QTMediaTypeVideo];
-                if (subtitlesArray && ([subtitlesArray count] >= 2))
+                if (subtitlesArray.count >= 2)
                 {
                     // track 0 should be video, other video tracks should
                     // be subtitles; force-enable the first subs track
-                    [subtitlesArray[1] setEnabled: YES];
+                    [subtitlesArray[1] setEnabled:YES];
                 }
             }
 
@@ -915,7 +725,7 @@ typedef enum ViewMode : NSUInteger {
 
 - (IBAction) toggleMoviePreviewPlayPause: (id) sender
 {
-    /* make sure a movie is even loaded up */
+    // make sure a movie is even loaded up
     if (self.movie)
     {
         if ([self.movie isPlaying]) // we are playing
@@ -1005,7 +815,7 @@ typedef enum ViewMode : NSUInteger {
 
 - (void) addMovieObservers
 {
-    /* Notification for any time the movie rate changes */
+    // Notification for any time the movie rate changes
     [[NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(movieRateDidChange:)
                                                  name:@"QTMovieRateDidChangeNotification"
@@ -1014,7 +824,7 @@ typedef enum ViewMode : NSUInteger {
 
 - (void) removeMovieObservers
 {
-    /*Notification for any time the movie rate changes */
+    // Notification for any time the movie rate changes
     [[NSNotificationCenter defaultCenter] removeObserver:self
                                                     name:@"QTMovieRateDidChangeNotification"
                                                   object:self.movie];
@@ -1022,7 +832,7 @@ typedef enum ViewMode : NSUInteger {
 
 - (void) movieRateDidChange: (NSNotification *) notification
 {
-    if ([self.movie isPlaying])
+    if (self.movie.isPlaying)
         [fPlayPauseButton setState: NSOnState];
     else
         [fPlayPauseButton setState: NSOffState];
@@ -1092,16 +902,16 @@ typedef enum ViewMode : NSUInteger {
         [super keyDown:event];
 }
 
-- (void) scrollWheel: (NSEvent *) theEvent
+- (void)scrollWheel:(NSEvent *)theEvent
 {
     if (self.currentViewMode != ViewModeEncoding)
     {
-        if ([theEvent deltaY] < 0)
+        if (theEvent.deltaY < 0)
         {
             [fPictureSlider setIntegerValue:self.pictureIndex < [fPictureSlider maxValue] ? self.pictureIndex + 1 : self.pictureIndex];
             [self pictureSliderChanged:self];
         }
-        else if ([theEvent deltaY] > 0)
+        else if (theEvent.deltaY > 0)
         {
             [fPictureSlider setIntegerValue:self.pictureIndex > [fPictureSlider minValue] ? self.pictureIndex - 1 : self.pictureIndex];
             [self pictureSliderChanged:self];
diff --git a/macosx/HBPreviewView.h b/macosx/HBPreviewView.h
new file mode 100644 (file)
index 0000000..b590b7e
--- /dev/null
@@ -0,0 +1,51 @@
+/* HBPreviewView.h
+
+ 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 <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  A HBPreviewView is a sublcass of NSView that can be used to display an image
+ *  plus a border.
+ */
+@interface HBPreviewView : NSView
+
+/**
+ *  The image displayed by the view.
+ */
+@property (nonatomic, readwrite, nullable) CGImageRef image;
+
+/**
+ *  The scale at which the image is shown.
+ */
+@property (nonatomic, readonly) CGFloat scale;
+
+/**
+ *  The actual frame of the displayed image.
+ */
+@property (nonatomic, readonly) CGRect pictureFrame;
+
+/**
+ *  Wheters the image will be scaled to fill the view
+ *  or not.
+ */
+@property (nonatomic, readwrite) BOOL fitToView;
+
+/**
+ *  If enabled, the view will show a white border around the image.
+ */
+@property (nonatomic, readwrite) BOOL showBorder;
+
+/**
+ * Given the size of the preview image to be shown, returns the best possible
+ * size for the view.
+ */
+- (NSSize)optimalViewSizeForImageSize:(NSSize)imageSize minSize:(NSSize)minSize;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/macosx/HBPreviewView.m b/macosx/HBPreviewView.m
new file mode 100644 (file)
index 0000000..5ea95b3
--- /dev/null
@@ -0,0 +1,272 @@
+/* HBPreviewView.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 "HBPreviewView.h"
+
+// the white border around the preview image
+#define BORDER_SIZE 2.0
+
+@interface HBPreviewView ()
+
+@property (nonatomic) CALayer *backLayer;
+@property (nonatomic) CALayer *pictureLayer;
+
+@property (nonatomic, readwrite) CGFloat scale;
+@property (nonatomic, readwrite) NSRect pictureFrame;
+
+
+@end
+
+@implementation HBPreviewView
+
+- (instancetype)initWithFrame:(NSRect)frameRect
+{
+    self = [super initWithFrame:frameRect];
+
+    if (self)
+    {
+        [self setUp];
+    }
+
+    return self;
+}
+
+- (nullable instancetype)initWithCoder:(NSCoder *)coder
+{
+    self = [super initWithCoder:coder];
+    if (self)
+    {
+        [self setUp];
+    }
+
+    return self;
+}
+
+/**
+ *  Setups the sublayers,
+ *  called by every initializer.
+ */
+- (void)setUp
+{
+    // Make it a layer hosting view
+    self.layer = [CALayer new];
+    self.wantsLayer = YES;
+
+    _backLayer = [CALayer layer];
+    [_backLayer setBounds:CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height)];
+    CGColorRef white = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0);
+    [_backLayer setBackgroundColor: white];
+    CFRelease(white);
+    [_backLayer setShadowOpacity:0.5f];
+    [_backLayer setShadowOffset:CGSizeMake(0, 0)];
+    [_backLayer setAnchorPoint:CGPointMake(0, 0)];
+
+    _pictureLayer = [CALayer layer];
+    [_pictureLayer setBounds:CGRectMake(0.0, 0.0, self.frame.size.width - (BORDER_SIZE * 2), self.frame.size.height - (BORDER_SIZE * 2))];
+    [_pictureLayer setAnchorPoint:CGPointMake(0, 0)];
+
+    // Disable fade on contents change.
+    NSMutableDictionary *actions = [NSMutableDictionary dictionary];
+    if (_pictureLayer.actions)
+    {
+        [actions addEntriesFromDictionary:_pictureLayer.actions];
+    }
+
+    actions[@"contents"] = [NSNull null];
+    _pictureLayer.actions = actions;
+
+    [self.layer addSublayer:_backLayer];
+    [self.layer addSublayer:_pictureLayer];
+
+    _pictureLayer.hidden = YES;
+    _backLayer.hidden = YES;
+
+    _showBorder = YES;
+
+    _scale = 1;
+    _pictureFrame = _pictureLayer.frame;
+}
+
+- (void)setImage:(CGImageRef)image
+{
+    _image = image;
+    self.pictureLayer.contents = (__bridge id _Nullable)(image);
+
+    // Hide the layers if there is no image
+    BOOL hidden = _image == nil ? YES : NO;
+    self.pictureLayer.hidden = hidden ;
+    self.backLayer.hidden = hidden || !self.showBorder;
+
+    [self layout];
+}
+
+- (void)setFitToView:(BOOL)fitToView
+{
+    _fitToView = fitToView;
+    [self layout];
+}
+
+- (void)setShowBorder:(BOOL)showBorder
+{
+    _showBorder = showBorder;
+    self.backLayer.hidden = !showBorder;
+    [self layout];
+}
+
+- (void)setFrame:(NSRect)newRect {
+    // A change in size has required the view to be invalidated.
+    if ([self inLiveResize]) {
+        [super setFrame:newRect];
+    }
+    else {
+        [super setFrame:newRect];
+    }
+
+    [self layout];
+}
+
+- (NSSize)scaledSize:(NSSize)source toFit:(NSSize)destination
+{
+    NSSize result;
+    CGFloat sourceAspectRatio = source.width / source.height;
+    CGFloat destinationAspectRatio = destination.width / destination.height;
+
+    // Source is larger than screen in one or more dimensions
+    if (sourceAspectRatio > destinationAspectRatio)
+    {
+        // Source aspect wider than screen aspect, snap to max width and vary height
+        result.width = destination.width;
+        result.height = result.width / sourceAspectRatio;
+    }
+    else
+    {
+        // Source aspect narrower than screen aspect, snap to max height vary width
+        result.height = destination.height;
+        result.width = result.height * sourceAspectRatio;
+    }
+
+    return result;
+}
+
+/**
+ *  Updates the sublayers layout.
+ */
+- (void)layout
+{
+    // Set the picture size display fields below the Preview Picture
+    NSSize imageSize = NSMakeSize(CGImageGetWidth(self.image), CGImageGetHeight(self.image));
+    NSSize imageScaledSize = imageSize;
+
+    if (self.window.backingScaleFactor != 1.0)
+    {
+        // HiDPI mode usually display everything
+        // with douple pixel count, but we don't
+        // want to double the size of the video
+        imageScaledSize.height /= self.window.backingScaleFactor;
+        imageScaledSize.width /= self.window.backingScaleFactor;
+    }
+
+    NSSize frameSize = self.frame.size;
+
+    if (self.showBorder == YES)
+    {
+        frameSize.width -= BORDER_SIZE * 2;
+        frameSize.height -= BORDER_SIZE * 2;
+    }
+
+    if (self.fitToView == YES)
+    {
+        // We are in Fit to View mode so, we have to get the ratio for height and width against the window
+        // size so we can scale from there.
+        imageScaledSize = [self scaledSize:imageScaledSize toFit:frameSize];
+    }
+    else
+    {
+        // If the image is larger then the view, scale the image
+        if (imageScaledSize.width > frameSize.width || imageScaledSize.height > frameSize.height)
+        {
+            imageScaledSize = [self scaledSize:imageScaledSize toFit:frameSize];
+        }
+    }
+
+    [NSAnimationContext beginGrouping];
+    [[NSAnimationContext currentContext] setDuration:0];
+
+    // Resize the CALayers
+    CGRect backRect = CGRectMake(0, 0, imageScaledSize.width + (BORDER_SIZE * 2), imageScaledSize.height + (BORDER_SIZE * 2));
+    CGRect pictureRect = CGRectMake(0, 0, imageScaledSize.width, imageScaledSize.height);
+
+    backRect = CGRectIntegral(backRect);
+    pictureRect = CGRectIntegral(pictureRect);
+
+    self.backLayer.bounds = backRect;
+    self.pictureLayer.bounds = pictureRect;
+
+    // Position the CALayers
+    CGPoint anchor = CGPointMake(floor((self.frame.size.width - pictureRect.size.width) / 2),
+                                 floor((self.frame.size.height - pictureRect.size.height) / 2));
+    [self.pictureLayer setPosition:anchor];
+
+    CGPoint backAchor = CGPointMake(anchor.x - BORDER_SIZE, anchor.y - BORDER_SIZE);
+    [self.backLayer setPosition:backAchor];
+
+    [NSAnimationContext endGrouping];
+
+    // Update the proprierties
+    self.scale = self.pictureLayer.frame.size.width / imageSize.width;
+    self.pictureFrame = self.pictureLayer.frame;
+}
+
+/**
+ * Given the size of the preview image to be shown, returns the best possible
+ * size for the view.
+ */
+- (NSSize)optimalViewSizeForImageSize:(NSSize)imageSize minSize:(NSSize)minSize
+{
+    if (self.window.backingScaleFactor != 1.0)
+    {
+        // HiDPI mode usually display everything
+        // with douple pixel count, but we don't
+        // want to double the size of the video
+        imageSize.height /= self.window.backingScaleFactor;
+        imageSize.width /= self.window.backingScaleFactor;
+    }
+
+    NSSize screenSize = self.window.screen.visibleFrame.size;
+    CGFloat maxWidth = screenSize.width;
+    CGFloat maxHeight = screenSize.height;
+
+    NSSize resultSize = imageSize;
+
+    if (resultSize.width > maxWidth || resultSize.height > maxHeight)
+    {
+        resultSize = [self scaledSize:resultSize toFit:screenSize];
+    }
+
+    // If necessary, grow to minimum dimensions to ensure controls overlay is not obstructed
+    if (resultSize.width < minSize.width)
+    {
+        resultSize.width = minSize.width;
+    }
+    if (resultSize.height < minSize.height)
+    {
+        resultSize.height = minSize.height;
+    }
+
+    // Add the border
+    if (self.showBorder)
+    {
+        resultSize.width += BORDER_SIZE * 2;
+        resultSize.height += BORDER_SIZE * 2;
+    }
+
+    resultSize.width = floor(resultSize.width);
+    resultSize.height = floor(resultSize.height);
+
+    return resultSize;
+}
+
+@end
index 477b69b069d213e77da12933322d8b4de95ddeef..e4780d22bbec4ec691f9cc471408a1dbb844924a 100644 (file)
                6F0D69A91AD0683100A39DCA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 273F204014ADBC210021BE6D /* Foundation.framework */; };
                A90A0CAF1988D57200DA65CE /* HBAudioTrackPreset.m in Sources */ = {isa = PBXBuildFile; fileRef = A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */; };
                A91017B41A64440A00039BFB /* HBSubtitles.m in Sources */ = {isa = PBXBuildFile; fileRef = A91017B31A64440A00039BFB /* HBSubtitles.m */; };
+               A914BCB31BC441C700157917 /* HBPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = A914BCB21BC441C700157917 /* HBPreviewView.m */; };
+               A914BCB61BC441D100157917 /* QTKit+HBQTMovieExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = A914BCB51BC441D100157917 /* QTKit+HBQTMovieExtensions.m */; };
                A9160A351AE7A165009A7818 /* HBCodingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = A9160A341AE7A165009A7818 /* HBCodingUtilities.m */; };
                A91726E7197291BC00D1AFEF /* HBChapterTitlesController.m in Sources */ = {isa = PBXBuildFile; fileRef = A91726E6197291BC00D1AFEF /* HBChapterTitlesController.m */; };
                A91806711A4807B000FC9BED /* HBRange.m in Sources */ = {isa = PBXBuildFile; fileRef = A91806701A4807B000FC9BED /* HBRange.m */; };
                A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAudioTrackPreset.m; sourceTree = "<group>"; };
                A91017B21A64440A00039BFB /* HBSubtitles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBSubtitles.h; sourceTree = "<group>"; };
                A91017B31A64440A00039BFB /* HBSubtitles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBSubtitles.m; sourceTree = "<group>"; };
+               A914BCB11BC441C700157917 /* HBPreviewView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBPreviewView.h; sourceTree = "<group>"; };
+               A914BCB21BC441C700157917 /* HBPreviewView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBPreviewView.m; sourceTree = "<group>"; };
+               A914BCB41BC441D100157917 /* QTKit+HBQTMovieExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "QTKit+HBQTMovieExtensions.h"; sourceTree = "<group>"; };
+               A914BCB51BC441D100157917 /* QTKit+HBQTMovieExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "QTKit+HBQTMovieExtensions.m"; sourceTree = "<group>"; };
                A9160A331AE7A165009A7818 /* HBCodingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBCodingUtilities.h; sourceTree = "<group>"; };
                A9160A341AE7A165009A7818 /* HBCodingUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBCodingUtilities.m; sourceTree = "<group>"; };
                A91726E5197291BC00D1AFEF /* HBChapterTitlesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBChapterTitlesController.h; sourceTree = "<group>"; };
                                A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */,
                                A95121E41B5F7BE700FD773D /* NSArray+HBAdditions.h */,
                                A95121E51B5F7BE700FD773D /* NSArray+HBAdditions.m */,
+                               A914BCB41BC441D100157917 /* QTKit+HBQTMovieExtensions.h */,
+                               A914BCB51BC441D100157917 /* QTKit+HBQTMovieExtensions.m */,
                                A9B34D711976844500871B7D /* UI Views */,
                                273F20BD14ADC09F0021BE6D /* main.mm */,
                        );
                                A9BB0F2619A0ECE40079F1C1 /* HBHUDButtonCell.m */,
                                A9C9F88719A733FE00DC8923 /* HBHUDView.h */,
                                A9C9F88819A733FE00DC8923 /* HBHUDView.m */,
+                               A914BCB11BC441C700157917 /* HBPreviewView.h */,
+                               A914BCB21BC441C700157917 /* HBPreviewView.m */,
                        );
                        name = "UI Views";
                        sourceTree = "<group>";
                                A9AA447A1970664A00D7DEFC /* HBUtilities.m in Sources */,
                                A9BC24C91A69293E007DC41A /* HBAttributedStringAdditions.m in Sources */,
                                273F20AC14ADBE670021BE6D /* HBController.m in Sources */,
+                               A914BCB61BC441D100157917 /* QTKit+HBQTMovieExtensions.m in Sources */,
                                273F20AD14ADBE670021BE6D /* HBAdvancedController.m in Sources */,
                                273F20AE14ADBE670021BE6D /* HBAudioTrack.m in Sources */,
                                A9DEC87A1A23C89E00C79B48 /* HBPicture.m in Sources */,
                                A9F472891976B7F30009EC65 /* HBSubtitlesDefaultsController.m in Sources */,
                                A91AFD0C1A948827009BECED /* HBOutputFileWriter.m in Sources */,
                                A9906B2C1A710920001D82D5 /* HBQueueController.m in Sources */,
+                               A914BCB31BC441C700157917 /* HBPreviewView.m in Sources */,
                                A9CF25F41990D64E0023F727 /* HBPreset.m in Sources */,
                                A95121E61B5F7BE700FD773D /* NSArray+HBAdditions.m in Sources */,
                                A9DEC8741A23C87500C79B48 /* HBCore.m in Sources */,
diff --git a/macosx/QTKit+HBQTMovieExtensions.h b/macosx/QTKit+HBQTMovieExtensions.h
new file mode 100644 (file)
index 0000000..dadea3d
--- /dev/null
@@ -0,0 +1,22 @@
+/* QTKit+HBQTMovieExtensions.h
+
+ 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 <Cocoa/Cocoa.h>
+#import <QTKit/QTKit.h>
+
+@interface QTMovieView (HBQTMovieViewExtensions)
+
+- (void)mouseMoved:(NSEvent *)theEvent;
+
+@end
+
+@interface QTMovie (HBQTMovieExtensions)
+
+- (BOOL)isPlaying;
+- (NSString *)timecode;
+- (void)setCurrentTimeDouble:(double)value;
+
+@end
diff --git a/macosx/QTKit+HBQTMovieExtensions.m b/macosx/QTKit+HBQTMovieExtensions.m
new file mode 100644 (file)
index 0000000..903659e
--- /dev/null
@@ -0,0 +1,49 @@
+/* QTKit+HBQTMovieExtensions.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 "QTKit+HBQTMovieExtensions.h"
+
+@implementation QTMovieView (HBQTMovieViewExtensions)
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+    [super mouseMoved:theEvent];
+}
+
+@end
+
+@implementation QTMovie (HBQTMovieExtensions)
+
+- (BOOL)isPlaying
+{
+    if (self.rate > 0)
+    {
+        return YES;
+    }
+    else
+    {
+        return NO;
+    }
+}
+
+- (NSString *)timecode
+{
+    QTTime time = [self currentTime];
+    double timeInSeconds = (double)time.timeValue / time.timeScale;
+    UInt16 seconds = (UInt16)fmod(timeInSeconds, 60.0);
+    UInt16 minutes = (UInt16)fmod(timeInSeconds / 60.0, 60.0);
+    UInt16 hours = (UInt16)(timeInSeconds / (60.0 * 60.0));
+    UInt16 milliseconds = (UInt16)(timeInSeconds - (int) timeInSeconds) * 1000;
+    return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds];
+}
+
+- (void)setCurrentTimeDouble:(double)value
+{
+    long timeScale = [[self attributeForKey:QTMovieTimeScaleAttribute] longValue];
+    [self setCurrentTime:QTMakeTime((long long)value * timeScale, timeScale)];
+}
+
+@end
\ No newline at end of file