tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit ef40186945084fe56743f9a9e357e63d318eb15d
parent 56eaa2dbf1ffc390574b98398c51f64cc67ef9a7
Author: Markus Stange <mstange.moz@gmail.com>
Date:   Thu, 23 Oct 2025 04:49:50 +0000

Bug 1820168 - Stop using nsMacCursor. r=mac-reviewers,bradwerth

Happy code deletion day!

nsMacCursor was used so that animated cursors could be treated like non-animated cursors.
But we don't have animated cursors anymore! Nothing calls -[nsMacCursor cursorWithFrames:type:].

Depends on D172479

Differential Revision: https://phabricator.services.mozilla.com/D172480

Diffstat:
Mwidget/cocoa/moz.build | 1-
Mwidget/cocoa/nsCursorManager.h | 6+++---
Mwidget/cocoa/nsCursorManager.mm | 256+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Dwidget/cocoa/nsMacCursor.h | 129-------------------------------------------------------------------------------
Dwidget/cocoa/nsMacCursor.mm | 393-------------------------------------------------------------------------------
5 files changed, 143 insertions(+), 642 deletions(-)

diff --git a/widget/cocoa/moz.build b/widget/cocoa/moz.build @@ -48,7 +48,6 @@ UNIFIED_SOURCES += [ "nsDeviceContextSpecX.mm", "nsFilePicker.mm", "nsLookAndFeel.mm", - "nsMacCursor.mm", "nsMacDockSupport.mm", "nsMacFinderProgress.mm", "nsMacSharingService.mm", diff --git a/widget/cocoa/nsCursorManager.h b/widget/cocoa/nsCursorManager.h @@ -5,9 +5,8 @@ #ifndef nsCursorManager_h_ #define nsCursorManager_h_ -#import <Foundation/Foundation.h> +#import <Cocoa/Cocoa.h> #include "nsIWidget.h" -#include "nsMacCursor.h" /*! @class nsCursorManager @abstract Singleton service provides access to all cursors available in @@ -21,7 +20,8 @@ @interface nsCursorManager : NSObject { @private NSMutableDictionary* mCursors; - nsMacCursor* mCurrentMacCursor; + NSCursor* mCurrentCursor; + nsCursor mCurrentCursorType; } /*! @method setCursor: diff --git a/widget/cocoa/nsCursorManager.mm b/widget/cocoa/nsCursorManager.mm @@ -6,6 +6,8 @@ #include "nsCocoaUtils.h" #include "nsCursorManager.h" #include "nsObjCExceptions.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIFile.h" #include <math.h> static nsCursorManager* gInstance; @@ -25,7 +27,7 @@ static constexpr nsCursor kCustomCursor = eCursorCount; @param aCursor the cursor to get @result the Mac native implementation of the cursor */ -- (nsMacCursor*)getCursor:(nsCursor)aCursor; +- (NSCursor*)getCursor:(nsCursor)aCursor; /*! @method setMacCursor: @abstract Set the current Mac native cursor @@ -35,7 +37,7 @@ static constexpr nsCursor kCustomCursor = eCursorCount; @param aMacCursor the cursor to set @result NS_OK */ -- (nsresult)setMacCursor:(nsMacCursor*)aMacCursor; +- (nsresult)setMacCursor:(NSCursor*)aMacCursor; /*! @method createCursor: @abstract Create a Mac native representation of a cursor. @@ -44,10 +46,14 @@ static constexpr nsCursor kCustomCursor = eCursorCount; @param aCursor the cursor to create @result the Mac native implementation of the cursor */ -+ (nsMacCursor*)createCursor:(enum nsCursor)aCursor; ++ (NSCursor*)createCursor:(enum nsCursor)aCursor; @end +@interface NSCursor (CreateWithImageNamed) ++ (NSCursor*)cursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint; +@end + @implementation nsCursorManager + (nsCursorManager*)sharedInstance { @@ -62,190 +68,158 @@ static constexpr nsCursor kCustomCursor = eCursorCount; gInstance = nil; } -+ (nsMacCursor*)createCursor:(enum nsCursor)aCursor { ++ (NSCursor*)createCursor:(enum nsCursor)aCursor { switch (aCursor) { case eCursor_standard: - return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; + return [NSCursor arrowCursor]; case eCursor_wait: case eCursor_spinning: { - return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] - type:aCursor]; + return [NSCursor busyButClickableCursor]; } case eCursor_select: - return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor]; + return [NSCursor IBeamCursor]; case eCursor_hyperlink: - return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] - type:aCursor]; + return [NSCursor pointingHandCursor]; case eCursor_crosshair: - return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] - type:aCursor]; + return [NSCursor crosshairCursor]; case eCursor_move: - return [nsMacCursor cursorWithImageNamed:@"move" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"move" + hotSpot:NSMakePoint(12, 12)]; case eCursor_help: - return [nsMacCursor cursorWithImageNamed:@"help" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"help" + hotSpot:NSMakePoint(12, 12)]; case eCursor_copy: { SEL cursorSelector = @selector(dragCopyCursor); - return [nsMacCursor - cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] - ? [NSCursor performSelector:cursorSelector] - : [NSCursor arrowCursor] - type:aCursor]; + return [NSCursor respondsToSelector:cursorSelector] + ? [NSCursor performSelector:cursorSelector] + : [NSCursor arrowCursor]; } case eCursor_alias: { SEL cursorSelector = @selector(dragLinkCursor); - return [nsMacCursor - cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] - ? [NSCursor performSelector:cursorSelector] - : [NSCursor arrowCursor] - type:aCursor]; + return [NSCursor respondsToSelector:cursorSelector] + ? [NSCursor performSelector:cursorSelector] + : [NSCursor arrowCursor]; } case eCursor_context_menu: { SEL cursorSelector = @selector(contextualMenuCursor); - return [nsMacCursor - cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] - ? [NSCursor performSelector:cursorSelector] - : [NSCursor arrowCursor] - type:aCursor]; + return [NSCursor respondsToSelector:cursorSelector] + ? [NSCursor performSelector:cursorSelector] + : [NSCursor arrowCursor]; } case eCursor_cell: - return [nsMacCursor cursorWithImageNamed:@"cell" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"cell" + hotSpot:NSMakePoint(12, 12)]; case eCursor_grab: - return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] - type:aCursor]; + return [NSCursor openHandCursor]; case eCursor_grabbing: - return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] - type:aCursor]; + return [NSCursor closedHandCursor]; case eCursor_zoom_in: - return [nsMacCursor cursorWithImageNamed:@"zoomIn" - hotSpot:NSMakePoint(10, 10) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"zoomIn" + hotSpot:NSMakePoint(10, 10)]; case eCursor_zoom_out: - return [nsMacCursor cursorWithImageNamed:@"zoomOut" - hotSpot:NSMakePoint(10, 10) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"zoomOut" + hotSpot:NSMakePoint(10, 10)]; case eCursor_vertical_text: - return [nsMacCursor cursorWithImageNamed:@"vtIBeam" - hotSpot:NSMakePoint(12, 11) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"vtIBeam" + hotSpot:NSMakePoint(12, 11)]; case eCursor_all_scroll: - return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] - type:aCursor]; + return [NSCursor openHandCursor]; case eCursor_not_allowed: case eCursor_no_drop: { SEL cursorSelector = @selector(operationNotAllowedCursor); - return [nsMacCursor - cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] - ? [NSCursor performSelector:cursorSelector] - : [NSCursor arrowCursor] - type:aCursor]; + return [NSCursor respondsToSelector:cursorSelector] + ? [NSCursor performSelector:cursorSelector] + : [NSCursor arrowCursor]; } // Resize Cursors: // North case eCursor_n_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] - type:aCursor]; + return [NSCursor resizeUpCursor]; // North East case eCursor_ne_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNE" - hotSpot:NSMakePoint(12, 11) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeNE" + hotSpot:NSMakePoint(12, 11)]; // East case eCursor_e_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] - type:aCursor]; + return [NSCursor resizeRightCursor]; // South East case eCursor_se_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeSE" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeSE" + hotSpot:NSMakePoint(12, 12)]; // South case eCursor_s_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] - type:aCursor]; + return [NSCursor resizeDownCursor]; // South West case eCursor_sw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeSW" - hotSpot:NSMakePoint(10, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeSW" + hotSpot:NSMakePoint(10, 12)]; // West case eCursor_w_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] - type:aCursor]; + return [NSCursor resizeLeftCursor]; // North West case eCursor_nw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNW" - hotSpot:NSMakePoint(11, 11) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeNW" + hotSpot:NSMakePoint(11, 11)]; // North & South case eCursor_ns_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] - type:aCursor]; + return [NSCursor resizeUpDownCursor]; // East & West case eCursor_ew_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] - type:aCursor]; + return [NSCursor resizeLeftRightCursor]; // North East & South West case eCursor_nesw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNESW" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeNESW" + hotSpot:NSMakePoint(12, 12)]; // North West & South East case eCursor_nwse_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNWSE" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"sizeNWSE" + hotSpot:NSMakePoint(12, 12)]; // Column Resize case eCursor_col_resize: - return [nsMacCursor cursorWithImageNamed:@"colResize" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"colResize" + hotSpot:NSMakePoint(12, 12)]; // Row Resize case eCursor_row_resize: - return [nsMacCursor cursorWithImageNamed:@"rowResize" - hotSpot:NSMakePoint(12, 12) - type:aCursor]; + return [NSCursor cursorWithImageNamed:@"rowResize" + hotSpot:NSMakePoint(12, 12)]; default: - return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; + return [NSCursor arrowCursor]; } } - (id)init { if ((self = [super init])) { mCursors = [[NSMutableDictionary alloc] initWithCapacity:25]; + mCurrentCursor = [[NSCursor arrowCursor] retain]; + mCurrentCursorType = eCursor_standard; } return self; } - (nsresult)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor { - [self setMacCursor:[self getCursor:aCursor.mDefaultCursor]]; + [self setMacCursor:[self getCursor:aCursor.mDefaultCursor] + type:aCursor.mDefaultCursor]; sCurrentCursor = aCursor; return NS_OK; } -- (nsresult)setMacCursor:(nsMacCursor*)aMacCursor { - nsCursor oldType = [mCurrentMacCursor type]; - nsCursor newType = [aMacCursor type]; - if (oldType != newType) { - if (newType == eCursor_none) { +- (nsresult)setMacCursor:(NSCursor*)aMacCursor type:(nsCursor)aType { + if (mCurrentCursorType != aType) { + if (aType == eCursor_none) { [NSCursor hide]; - } else if (oldType == eCursor_none) { + } else if (mCurrentCursorType == eCursor_none) { [NSCursor unhide]; } + mCurrentCursorType = aType; } - if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) { + if (mCurrentCursor != aMacCursor || + [NSCursor currentCursor] != mCurrentCursor) { [aMacCursor retain]; - [mCurrentMacCursor unset]; [aMacCursor set]; - [mCurrentMacCursor release]; - mCurrentMacCursor = aMacCursor; + [mCurrentCursor release]; + mCurrentCursor = aMacCursor; } return NS_OK; @@ -257,10 +231,10 @@ static constexpr nsCursor kCustomCursor = eCursorCount; // As the user moves the mouse, this gets called repeatedly with the same // aCursorImage if (!aForceUpdate && sCurrentCursor == aCursor && - sCurrentCursorScaleFactor == scaleFactor && mCurrentMacCursor) { + sCurrentCursorScaleFactor == scaleFactor && mCurrentCursor) { // Native dragging can unset our cursor apparently (see bug 1739352). - if (MOZ_UNLIKELY(![mCurrentMacCursor isSet])) { - [mCurrentMacCursor set]; + if (MOZ_UNLIKELY([NSCursor currentCursor] != mCurrentCursor)) { + [mCurrentCursor set]; } return NS_OK; } @@ -296,17 +270,15 @@ static constexpr nsCursor kCustomCursor = eCursorCount; uint32_t hotspotY = aCursor.mHotspotY > (uint32_t(size.height) - 1) ? 0 : aCursor.mHotspotY; NSPoint hotSpot = ::NSMakePoint(hotspotX, hotspotY); - [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] - initWithImage:cursorImage - hotSpot:hotSpot] - type:kCustomCursor]]; + [self setMacCursor:[[NSCursor alloc] initWithImage:cursorImage + hotSpot:hotSpot] + type:kCustomCursor]; [cursorImage release]; return NS_OK; } -- (nsMacCursor*)getCursor:(enum nsCursor)aCursor { - nsMacCursor* result = - [mCursors objectForKey:[NSNumber numberWithInt:aCursor]]; +- (NSCursor*)getCursor:(enum nsCursor)aCursor { + NSCursor* result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]]; if (!result) { result = [nsCursorManager createCursor:aCursor]; [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]]; @@ -315,11 +287,63 @@ static constexpr nsCursor kCustomCursor = eCursorCount; } - (void)dealloc { - [mCurrentMacCursor unset]; - [mCurrentMacCursor release]; + [mCurrentCursor release]; [mCursors release]; sCurrentCursor = {}; [super dealloc]; } @end + +@implementation NSCursor (CreateWithImageName) + ++ (NSCursor*)cursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint { + nsCOMPtr<nsIFile> resDir; + nsAutoCString resPath; + NSString *pathToImage, *pathToHiDpiImage; + NSImage *cursorImage, *hiDpiCursorImage; + + nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir)); + if (NS_FAILED(rv)) goto INIT_FAILURE; + resDir->AppendNative("res"_ns); + resDir->AppendNative("cursors"_ns); + + rv = resDir->GetNativePath(resPath); + if (NS_FAILED(rv)) goto INIT_FAILURE; + + pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()]; + if (!pathToImage) goto INIT_FAILURE; + pathToImage = [pathToImage stringByAppendingPathComponent:imageName]; + pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"]; + // Add same extension to both image paths. + pathToImage = [pathToImage stringByAppendingPathExtension:@"png"]; + pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"]; + + cursorImage = + [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease]; + if (!cursorImage) goto INIT_FAILURE; + + // Note 1: There are a few different ways to get a hidpi image via + // initWithContentsOfFile. We let the OS handle this here: when the + // file basename ends in "@2x", it will be displayed at native resolution + // instead of being pixel-doubled. See bug 784909 comment 7 for alternate + // ways. + // + // Note 2: The OS is picky, and will ignore the hidpi representation + // unless it is exactly twice the size of the lowdpi image. + hiDpiCursorImage = + [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease]; + if (hiDpiCursorImage) { + NSImageRep* imageRep = [[hiDpiCursorImage representations] objectAtIndex:0]; + [cursorImage addRepresentation:imageRep]; + } + return [[[NSCursor alloc] initWithImage:cursorImage + hotSpot:aPoint] autorelease]; + +INIT_FAILURE: + NS_WARNING("Problem getting path to cursor image file!"); + [self release]; + return nil; +} + +@end diff --git a/widget/cocoa/nsMacCursor.h b/widget/cocoa/nsMacCursor.h @@ -1,129 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsMacCursor_h_ -#define nsMacCursor_h_ - -#import <Cocoa/Cocoa.h> -#import "nsIWidget.h" - -/*! @class nsMacCursor - @abstract Represents a native Mac cursor. - @discussion <code>nsMacCursor</code> provides a simple API for creating and - working with native Macintosh cursors. Cursors can be created - used without needing to be aware of the way different cursors - are implemented, in particular the details of managing an - animated cursor are hidden. -*/ -@interface nsMacCursor : NSObject { - @private - NSTimer* mTimer; - @protected - nsCursor mType; - int mFrameCounter; -} - -/*! @method cursorWithCursor: - @abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>. - @discussion Creates a cursor representing the given Cocoa built-in cursor. - @param aCursor the <code>NSCursor</code> to use - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> - representing the given <code>NSCursor</code> - */ -+ (nsMacCursor*)cursorWithCursor:(NSCursor*)aCursor type:(nsCursor)aType; - -/*! @method cursorWithImageNamed:hotSpot:type: - @abstract Create a cursor by specifying the name of an image resource to - use for the cursor and a hotspot. - @discussion Creates a cursor by loading the named image using the - <code>+[NSImage imageNamed:]</code> method. - <p>The image must be compatible with any restrictions laid down - by <code>NSCursor</code>. These vary by operating system - version.</p> - <p>The hotspot precisely determines the point where the user - clicks when using the cursor.</p> - @param aCursor the name of the image to use for the cursor - @param aPoint the point within the cursor to use as the hotspot - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> that uses - the given image and hotspot - */ -+ (nsMacCursor*)cursorWithImageNamed:(NSString*)aCursorImage - hotSpot:(NSPoint)aPoint - type:(nsCursor)aType; - -/*! @method cursorWithFrames:type: - @abstract Create an animated cursor by specifying the frames to use for - the animation. - @discussion Creates a cursor that will animate by cycling through the given - frames. Each element of the array must be an instance of - <code>NSCursor</code> - @param aCursorFrames an array of <code>NSCursor</code>, representing - the frames of an animated cursor, in the order they should be - played. - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> that will - animate the given cursor frames - */ -+ (nsMacCursor*)cursorWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType; - -/*! @method cocoaCursorWithImageNamed:hotSpot: - @abstract Create a Cocoa NSCursor object with a Gecko image resource name - and a hotspot point. - @discussion Create a Cocoa NSCursor object with a Gecko image resource name - and a hotspot point. - @param imageName the name of the gecko image resource, "tiff" - extension is assumed, do not append. - @param aPoint the point within the cursor to use as the hotspot - @result an autoreleased instance of <code>nsMacCursor</code> that will - animate the given cursor frames - */ -+ (NSCursor*)cocoaCursorWithImageNamed:(NSString*)imageName - hotSpot:(NSPoint)aPoint; - -/*! @method isSet - @abstract Determines whether this cursor is currently active. - @discussion This can be helpful when the Cocoa NSCursor state can be - influenced without going through nsCursorManager. - @result whether the cursor is currently set - */ -- (BOOL)isSet; - -/*! @method set - @abstract Set the cursor. - @discussion Makes this cursor the current cursor. If the cursor is - animated, the animation is started. - */ -- (void)set; - -/*! @method unset - @abstract Unset the cursor. The cursor will return to the default - (usually the arrow cursor). - @discussion Unsets the cursor. If the cursor is animated, the animation is - stopped. - */ -- (void)unset; - -/*! @method isAnimated - @abstract Tests whether this cursor is animated. - @discussion Use this method to determine whether a cursor is animated - @result YES if the cursor is animated (has more than one frame), NO if - it is a simple static cursor. - */ -- (BOOL)isAnimated; - -/** @method cursorType - @abstract Get the cursor type for this cursor - @discussion This method returns the <code>nsCursor</code> constant that - corresponds to this cursor, which is equivalent to the CSS - name for the cursor. - @result The nsCursor constant corresponding to this cursor, or - nsCursor's 'eCursorCount' if the cursor is a custom cursor - loaded from a URI - */ -- (nsCursor)type; -@end - -#endif // nsMacCursor_h_ diff --git a/widget/cocoa/nsMacCursor.mm b/widget/cocoa/nsMacCursor.mm @@ -1,393 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsMacCursor.h" -#include "nsObjCExceptions.h" -#include "nsDebug.h" -#include "nsDirectoryServiceDefs.h" -#include "nsCOMPtr.h" -#include "nsIFile.h" -#include "nsString.h" - -/*! @category nsMacCursor (PrivateMethods) - @abstract Private methods internal to the nsMacCursor class. - @discussion <code>nsMacCursor</code> is effectively an abstract class. It - does not define complete behaviour in and of itself, the subclasses defined - in this file provide the useful implementations. -*/ -@interface nsMacCursor (PrivateMethods) - -/*! @method getNextCursorFrame - @abstract get the index of the next cursor frame to display. - @discussion Increments and returns the frame counter of an animated cursor. - @result The index of the next frame to display in the cursor animation -*/ -- (int)getNextCursorFrame; - -/*! @method numFrames - @abstract Query the number of frames in this cursor's animation. - @discussion Returns the number of frames in this cursor's animation. Static - cursors return 1. -*/ -- (int)numFrames; - -/*! @method createTimer - @abstract Create a Timer to use to animate the cursor. - @discussion Creates an instance of <code>NSTimer</code> which is used to - drive the cursor animation. This method should only be called for cursors - that are animated. -*/ -- (void)createTimer; - -/*! @method destroyTimer - @abstract Destroy any timer instance associated with this cursor. - @discussion Invalidates and releases any <code>NSTimer</code> instance - associated with this cursor. - */ -- (void)destroyTimer; -/*! @method destroyTimer - @abstract Destroy any timer instance associated with this cursor. - @discussion Invalidates and releases any <code>NSTimer</code> instance - associated with this cursor. -*/ - -/*! @method advanceAnimatedCursor: - @abstract Method called by animation timer to perform animation. - @discussion Called by an animated cursor's associated timer to advance the - animation to the next frame. Determines which frame should occur next and - sets the cursor to that frame. - @param aTimer the timer causing the animation -*/ -- (void)advanceAnimatedCursor:(NSTimer*)aTimer; - -/*! @method setFrame: - @abstract Sets the current cursor, using an index to determine which frame - in the animation to display. - @discussion Sets the current cursor. The frame index determines which frame - is shown if the cursor is animated. Frames and numbered from <code>0</code> - to <code>-[nsMacCursor numFrames] - 1</code>. A static cursor has a single - frame, numbered 0. - @param aFrameIndex the index indicating which frame from the animation - to display -*/ -- (void)setFrame:(int)aFrameIndex; - -@end - -/*! @class nsCocoaCursor - @abstract Implementation of <code>nsMacCursor</code> that uses Cocoa - <code>NSCursor</code> instances. - @discussion Displays a static or animated cursor, using Cocoa - <code>NSCursor</code> instances. These can be either built-in - <code>NSCursor</code> instances, or custom <code>NSCursor</code>s created - from images. When more than one <code>NSCursor</code> is provided, the cursor - will use these as animation frames. -*/ -@interface nsCocoaCursor : nsMacCursor { - @private - NSArray* mFrames; - NSCursor* mLastSetCocoaCursor; -} - -/*! @method initWithFrames: - @abstract Create an animated cursor by specifying the frames to use for - the animation. - @discussion Creates a cursor that will animate by cycling through the given - frames. Each element of the array must be an instance of - <code>NSCursor</code> - @param aCursorFrames an array of <code>NSCursor</code>, representing - the frames of an animated cursor, in the order they should be played. - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> that will animate the - given cursor frames - */ -- (id)initWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType; - -/*! @method initWithCursor: - @abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>. - @discussion Creates a cursor representing the given Cocoa built-in cursor. - @param aCursor the <code>NSCursor</code> to use - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> representing the given - <code>NSCursor</code> -*/ -- (id)initWithCursor:(NSCursor*)aCursor type:(nsCursor)aType; - -/*! @method initWithImageNamed:hotSpot: - @abstract Create a cursor by specifying the name of an image resource to - use for the cursor and a hotspot. - @discussion Creates a cursor by loading the named image using the - <code>+[NSImage imageNamed:]</code> method. <p>The image must be compatible - with any restrictions laid down by <code>NSCursor</code>. These vary by - operating system version.</p> <p>The hotspot precisely determines the point - where the user clicks when using the cursor.</p> - @param aCursor the name of the image to use for the cursor - @param aPoint the point within the cursor to use as the hotspot - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> that uses the given - image and hotspot -*/ -- (id)initWithImageNamed:(NSString*)aCursorImage - hotSpot:(NSPoint)aPoint - type:(nsCursor)aType; - -@end - -@implementation nsMacCursor - -+ (nsMacCursor*)cursorWithCursor:(NSCursor*)aCursor type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return [[[nsCocoaCursor alloc] initWithCursor:aCursor - type:aType] autorelease]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -+ (nsMacCursor*)cursorWithImageNamed:(NSString*)aCursorImage - hotSpot:(NSPoint)aPoint - type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage - hotSpot:aPoint - type:aType] autorelease]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -+ (nsMacCursor*)cursorWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames - type:aType] autorelease]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -+ (NSCursor*)cocoaCursorWithImageNamed:(NSString*)imageName - hotSpot:(NSPoint)aPoint { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - nsCOMPtr<nsIFile> resDir; - nsAutoCString resPath; - NSString *pathToImage, *pathToHiDpiImage; - NSImage *cursorImage, *hiDpiCursorImage; - - nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir)); - if (NS_FAILED(rv)) goto INIT_FAILURE; - resDir->AppendNative("res"_ns); - resDir->AppendNative("cursors"_ns); - - rv = resDir->GetNativePath(resPath); - if (NS_FAILED(rv)) goto INIT_FAILURE; - - pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()]; - if (!pathToImage) goto INIT_FAILURE; - pathToImage = [pathToImage stringByAppendingPathComponent:imageName]; - pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"]; - // Add same extension to both image paths. - pathToImage = [pathToImage stringByAppendingPathExtension:@"png"]; - pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"]; - - cursorImage = - [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease]; - if (!cursorImage) goto INIT_FAILURE; - - // Note 1: There are a few different ways to get a hidpi image via - // initWithContentsOfFile. We let the OS handle this here: when the - // file basename ends in "@2x", it will be displayed at native resolution - // instead of being pixel-doubled. See bug 784909 comment 7 for alternates - // ways. - // - // Note 2: The OS is picky, and will ignore the hidpi representation - // unless it is exactly twice the size of the lowdpi image. - hiDpiCursorImage = - [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease]; - if (hiDpiCursorImage) { - NSImageRep* imageRep = [[hiDpiCursorImage representations] objectAtIndex:0]; - [cursorImage addRepresentation:imageRep]; - } - return [[[NSCursor alloc] initWithImage:cursorImage - hotSpot:aPoint] autorelease]; - -INIT_FAILURE: - NS_WARNING("Problem getting path to cursor image file!"); - [self release]; - return nil; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -- (BOOL)isSet { - // implemented by subclasses - return NO; -} - -- (void)set { - if ([self isAnimated]) { - [self createTimer]; - } - // if the cursor isn't animated or the timer creation fails for any reason... - if (!mTimer) { - [self setFrame:0]; - } -} - -- (void)unset { - [self destroyTimer]; -} - -- (BOOL)isAnimated { - return [self numFrames] > 1; -} - -- (int)numFrames { - // subclasses need to override this to support animation - return 1; -} - -- (int)getNextCursorFrame { - mFrameCounter = (mFrameCounter + 1) % [self numFrames]; - return mFrameCounter; -} - -- (void)createTimer { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - if (!mTimer) { - mTimer = [[NSTimer - scheduledTimerWithTimeInterval:0.25 - target:self - selector:@selector(advanceAnimatedCursor:) - userInfo:nil - repeats:YES] retain]; - } - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -- (void)destroyTimer { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - if (mTimer) { - [mTimer invalidate]; - [mTimer release]; - mTimer = nil; - } - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -- (void)advanceAnimatedCursor:(NSTimer*)aTimer { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - if ([aTimer isValid]) { - [self setFrame:[self getNextCursorFrame]]; - } - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -- (void)setFrame:(int)aFrameIndex { - // subclasses need to do something useful here -} - -- (nsCursor)type { - return mType; -} - -- (void)dealloc { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - [self destroyTimer]; - [super dealloc]; - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -@end - -@implementation nsCocoaCursor - -- (id)initWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - self = [super init]; - NSEnumerator* it = [aCursorFrames objectEnumerator]; - NSObject* frame = nil; - while ((frame = [it nextObject])) { - NS_ASSERTION([frame isKindOfClass:[NSCursor class]], - "Invalid argument: All frames must be of type NSCursor"); - } - mFrames = [aCursorFrames retain]; - mFrameCounter = 0; - mType = aType; - return self; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -- (id)initWithCursor:(NSCursor*)aCursor type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - NSArray* frame = [NSArray arrayWithObjects:aCursor, nil]; - return [self initWithFrames:frame type:aType]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -- (id)initWithImageNamed:(NSString*)aCursorImage - hotSpot:(NSPoint)aPoint - type:(nsCursor)aType { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return - [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage - hotSpot:aPoint] - type:aType]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -- (BOOL)isSet { - return [NSCursor currentCursor] == mLastSetCocoaCursor; -} - -- (void)setFrame:(int)aFrameIndex { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex]; - [newCursor set]; - mLastSetCocoaCursor = newCursor; - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -- (int)numFrames { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return [mFrames count]; - - NS_OBJC_END_TRY_BLOCK_RETURN(0); -} - -- (NSString*)description { - NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - - return [mFrames description]; - - NS_OBJC_END_TRY_BLOCK_RETURN(nil); -} - -- (void)dealloc { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - [mFrames release]; - [super dealloc]; - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - -@end