widget/cocoa/nsMacCursor.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "nsMacCursor.h"
michael@0 6 #include "nsObjCExceptions.h"
michael@0 7 #include "nsDebug.h"
michael@0 8 #include "nsDirectoryServiceDefs.h"
michael@0 9 #include "nsCOMPtr.h"
michael@0 10 #include "nsIFile.h"
michael@0 11 #include "nsString.h"
michael@0 12
michael@0 13 /*! @category nsMacCursor (PrivateMethods)
michael@0 14 @abstract Private methods internal to the nsMacCursor class.
michael@0 15 @discussion <code>nsMacCursor</code> is effectively an abstract class. It does not define complete
michael@0 16 behaviour in and of itself, the subclasses defined in this file provide the useful implementations.
michael@0 17 */
michael@0 18 @interface nsMacCursor (PrivateMethods)
michael@0 19
michael@0 20 /*! @method getNextCursorFrame
michael@0 21 @abstract get the index of the next cursor frame to display.
michael@0 22 @discussion Increments and returns the frame counter of an animated cursor.
michael@0 23 @result The index of the next frame to display in the cursor animation
michael@0 24 */
michael@0 25 - (int) getNextCursorFrame;
michael@0 26
michael@0 27 /*! @method numFrames
michael@0 28 @abstract Query the number of frames in this cursor's animation.
michael@0 29 @discussion Returns the number of frames in this cursor's animation. Static cursors return 1.
michael@0 30 */
michael@0 31 - (int) numFrames;
michael@0 32
michael@0 33 /*! @method createTimer
michael@0 34 @abstract Create a Timer to use to animate the cursor.
michael@0 35 @discussion Creates an instance of <code>NSTimer</code> which is used to drive the cursor animation.
michael@0 36 This method should only be called for cursors that are animated.
michael@0 37 */
michael@0 38 - (void) createTimer;
michael@0 39
michael@0 40 /*! @method destroyTimer
michael@0 41 @abstract Destroy any timer instance associated with this cursor.
michael@0 42 @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor.
michael@0 43 */
michael@0 44 - (void) destroyTimer;
michael@0 45 /*! @method destroyTimer
michael@0 46 @abstract Destroy any timer instance associated with this cursor.
michael@0 47 @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor.
michael@0 48 */
michael@0 49
michael@0 50 /*! @method advanceAnimatedCursor:
michael@0 51 @abstract Method called by animation timer to perform animation.
michael@0 52 @discussion Called by an animated cursor's associated timer to advance the animation to the next frame.
michael@0 53 Determines which frame should occur next and sets the cursor to that frame.
michael@0 54 @param aTimer the timer causing the animation
michael@0 55 */
michael@0 56 - (void) advanceAnimatedCursor: (NSTimer *) aTimer;
michael@0 57
michael@0 58 /*! @method setFrame:
michael@0 59 @abstract Sets the current cursor, using an index to determine which frame in the animation to display.
michael@0 60 @discussion Sets the current cursor. The frame index determines which frame is shown if the cursor is animated.
michael@0 61 Frames and numbered from <code>0</code> to <code>-[nsMacCursor numFrames] - 1</code>. A static cursor
michael@0 62 has a single frame, numbered 0.
michael@0 63 @param aFrameIndex the index indicating which frame from the animation to display
michael@0 64 */
michael@0 65 - (void) setFrame: (int) aFrameIndex;
michael@0 66
michael@0 67 @end
michael@0 68
michael@0 69 /*! @class nsCocoaCursor
michael@0 70 @abstract Implementation of <code>nsMacCursor</code> that uses Cocoa <code>NSCursor</code> instances.
michael@0 71 @discussion Displays a static or animated cursor, using Cocoa <code>NSCursor</code> instances. These can be either
michael@0 72 built-in <code>NSCursor</code> instances, or custom <code>NSCursor</code>s created from images.
michael@0 73 When more than one <code>NSCursor</code> is provided, the cursor will use these as animation frames.
michael@0 74 */
michael@0 75 @interface nsCocoaCursor : nsMacCursor
michael@0 76 {
michael@0 77 @private
michael@0 78 NSArray *mFrames;
michael@0 79 NSCursor *mLastSetCocoaCursor;
michael@0 80 }
michael@0 81
michael@0 82 /*! @method initWithFrames:
michael@0 83 @abstract Create an animated cursor by specifying the frames to use for the animation.
michael@0 84 @discussion Creates a cursor that will animate by cycling through the given frames. Each element of the array
michael@0 85 must be an instance of <code>NSCursor</code>
michael@0 86 @param aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the
michael@0 87 order they should be played.
michael@0 88 @param aType the corresponding <code>nsCursor</code> constant
michael@0 89 @result an instance of <code>nsCocoaCursor</code> that will animate the given cursor frames
michael@0 90 */
michael@0 91 - (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType;
michael@0 92
michael@0 93 /*! @method initWithCursor:
michael@0 94 @abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>.
michael@0 95 @discussion Creates a cursor representing the given Cocoa built-in cursor.
michael@0 96 @param aCursor the <code>NSCursor</code> to use
michael@0 97 @param aType the corresponding <code>nsCursor</code> constant
michael@0 98 @result an instance of <code>nsCocoaCursor</code> representing the given <code>NSCursor</code>
michael@0 99 */
michael@0 100 - (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType;
michael@0 101
michael@0 102 /*! @method initWithImageNamed:hotSpot:
michael@0 103 @abstract Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot.
michael@0 104 @discussion Creates a cursor by loading the named image using the <code>+[NSImage imageNamed:]</code> method.
michael@0 105 <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary
michael@0 106 by operating system version.</p>
michael@0 107 <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p>
michael@0 108 @param aCursor the name of the image to use for the cursor
michael@0 109 @param aPoint the point within the cursor to use as the hotspot
michael@0 110 @param aType the corresponding <code>nsCursor</code> constant
michael@0 111 @result an instance of <code>nsCocoaCursor</code> that uses the given image and hotspot
michael@0 112 */
michael@0 113 - (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType;
michael@0 114
michael@0 115 @end
michael@0 116
michael@0 117 @implementation nsMacCursor
michael@0 118
michael@0 119 + (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType
michael@0 120 {
michael@0 121 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 122
michael@0 123 return [[[nsCocoaCursor alloc] initWithCursor:aCursor type:aType] autorelease];
michael@0 124
michael@0 125 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 126 }
michael@0 127
michael@0 128 + (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType
michael@0 129 {
michael@0 130 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 131
michael@0 132 return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage hotSpot:aPoint type:aType] autorelease];
michael@0 133
michael@0 134 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 135 }
michael@0 136
michael@0 137 + (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType
michael@0 138 {
michael@0 139 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 140
michael@0 141 return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames type:aType] autorelease];
michael@0 142
michael@0 143 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 144 }
michael@0 145
michael@0 146 + (NSCursor *) cocoaCursorWithImageNamed: (NSString *) imageName hotSpot: (NSPoint) aPoint
michael@0 147 {
michael@0 148 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 149
michael@0 150 nsCOMPtr<nsIFile> resDir;
michael@0 151 nsAutoCString resPath;
michael@0 152 NSString* pathToImage, *pathToHiDpiImage;
michael@0 153 NSImage* cursorImage, *hiDpiCursorImage;
michael@0 154
michael@0 155 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir));
michael@0 156 if (NS_FAILED(rv))
michael@0 157 goto INIT_FAILURE;
michael@0 158 resDir->AppendNative(NS_LITERAL_CSTRING("res"));
michael@0 159 resDir->AppendNative(NS_LITERAL_CSTRING("cursors"));
michael@0 160
michael@0 161 rv = resDir->GetNativePath(resPath);
michael@0 162 if (NS_FAILED(rv))
michael@0 163 goto INIT_FAILURE;
michael@0 164
michael@0 165 pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()];
michael@0 166 if (!pathToImage)
michael@0 167 goto INIT_FAILURE;
michael@0 168 pathToImage = [pathToImage stringByAppendingPathComponent:imageName];
michael@0 169 pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"];
michael@0 170 // Add same extension to both image paths.
michael@0 171 pathToImage = [pathToImage stringByAppendingPathExtension:@"png"];
michael@0 172 pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"];
michael@0 173
michael@0 174 cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease];
michael@0 175 if (!cursorImage)
michael@0 176 goto INIT_FAILURE;
michael@0 177
michael@0 178 // Note 1: There are a few different ways to get a hidpi image via
michael@0 179 // initWithContentsOfFile. We let the OS handle this here: when the
michael@0 180 // file basename ends in "@2x", it will be displayed at native resolution
michael@0 181 // instead of being pixel-doubled. See bug 784909 comment 7 for alternates ways.
michael@0 182 //
michael@0 183 // Note 2: The OS is picky, and will ignore the hidpi representation
michael@0 184 // unless it is exactly twice the size of the lowdpi image.
michael@0 185 hiDpiCursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease];
michael@0 186 if (hiDpiCursorImage) {
michael@0 187 NSImageRep *imageRep = [[hiDpiCursorImage representations] objectAtIndex:0];
michael@0 188 [cursorImage addRepresentation: imageRep];
michael@0 189 }
michael@0 190 return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease];
michael@0 191
michael@0 192 INIT_FAILURE:
michael@0 193 NS_WARNING("Problem getting path to cursor image file!");
michael@0 194 [self release];
michael@0 195 return nil;
michael@0 196
michael@0 197 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 198 }
michael@0 199
michael@0 200 - (BOOL) isSet
michael@0 201 {
michael@0 202 // implemented by subclasses
michael@0 203 return NO;
michael@0 204 }
michael@0 205
michael@0 206 - (void) set
michael@0 207 {
michael@0 208 if ([self isAnimated]) {
michael@0 209 [self createTimer];
michael@0 210 }
michael@0 211 // if the cursor isn't animated or the timer creation fails for any reason...
michael@0 212 if (!mTimer) {
michael@0 213 [self setFrame:0];
michael@0 214 }
michael@0 215 }
michael@0 216
michael@0 217 - (void) unset
michael@0 218 {
michael@0 219 [self destroyTimer];
michael@0 220 }
michael@0 221
michael@0 222 - (BOOL) isAnimated
michael@0 223 {
michael@0 224 return [self numFrames] > 1;
michael@0 225 }
michael@0 226
michael@0 227 - (int) numFrames
michael@0 228 {
michael@0 229 // subclasses need to override this to support animation
michael@0 230 return 1;
michael@0 231 }
michael@0 232
michael@0 233 - (int) getNextCursorFrame
michael@0 234 {
michael@0 235 mFrameCounter = (mFrameCounter + 1) % [self numFrames];
michael@0 236 return mFrameCounter;
michael@0 237 }
michael@0 238
michael@0 239 - (void) createTimer
michael@0 240 {
michael@0 241 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 242
michael@0 243 if (!mTimer) {
michael@0 244 mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.25
michael@0 245 target:self
michael@0 246 selector:@selector(advanceAnimatedCursor:)
michael@0 247 userInfo:nil
michael@0 248 repeats:YES] retain];
michael@0 249 }
michael@0 250
michael@0 251 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 252 }
michael@0 253
michael@0 254 - (void) destroyTimer
michael@0 255 {
michael@0 256 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 257
michael@0 258 if (mTimer) {
michael@0 259 [mTimer invalidate];
michael@0 260 [mTimer release];
michael@0 261 mTimer = nil;
michael@0 262 }
michael@0 263
michael@0 264 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 265 }
michael@0 266
michael@0 267 - (void) advanceAnimatedCursor: (NSTimer *) aTimer
michael@0 268 {
michael@0 269 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 270
michael@0 271 if ([aTimer isValid]) {
michael@0 272 [self setFrame:[self getNextCursorFrame]];
michael@0 273 }
michael@0 274
michael@0 275 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 276 }
michael@0 277
michael@0 278 - (void) setFrame: (int) aFrameIndex
michael@0 279 {
michael@0 280 // subclasses need to do something useful here
michael@0 281 }
michael@0 282
michael@0 283 - (nsCursor) type {
michael@0 284 return mType;
michael@0 285 }
michael@0 286
michael@0 287 - (void) dealloc
michael@0 288 {
michael@0 289 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 290
michael@0 291 [self destroyTimer];
michael@0 292 [super dealloc];
michael@0 293
michael@0 294 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 295 }
michael@0 296
michael@0 297 @end
michael@0 298
michael@0 299 @implementation nsCocoaCursor
michael@0 300
michael@0 301 - (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType
michael@0 302 {
michael@0 303 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 304
michael@0 305 self = [super init];
michael@0 306 NSEnumerator *it = [aCursorFrames objectEnumerator];
michael@0 307 NSObject *frame = nil;
michael@0 308 while ((frame = [it nextObject])) {
michael@0 309 NS_ASSERTION([frame isKindOfClass:[NSCursor class]], "Invalid argument: All frames must be of type NSCursor");
michael@0 310 }
michael@0 311 mFrames = [aCursorFrames retain];
michael@0 312 mFrameCounter = 0;
michael@0 313 mType = aType;
michael@0 314 return self;
michael@0 315
michael@0 316 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 317 }
michael@0 318
michael@0 319 - (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType
michael@0 320 {
michael@0 321 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 322
michael@0 323 NSArray *frame = [NSArray arrayWithObjects:aCursor, nil];
michael@0 324 return [self initWithFrames:frame type:aType];
michael@0 325
michael@0 326 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 327 }
michael@0 328
michael@0 329 - (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType
michael@0 330 {
michael@0 331 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 332
michael@0 333 return [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage hotSpot:aPoint] type:aType];
michael@0 334
michael@0 335 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 336 }
michael@0 337
michael@0 338 - (BOOL) isSet
michael@0 339 {
michael@0 340 return [NSCursor currentCursor] == mLastSetCocoaCursor;
michael@0 341 }
michael@0 342
michael@0 343 - (void) setFrame: (int) aFrameIndex
michael@0 344 {
michael@0 345 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 346
michael@0 347 NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex];
michael@0 348 [newCursor set];
michael@0 349 mLastSetCocoaCursor = newCursor;
michael@0 350
michael@0 351 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 352 }
michael@0 353
michael@0 354 - (int) numFrames
michael@0 355 {
michael@0 356 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 357
michael@0 358 return [mFrames count];
michael@0 359
michael@0 360 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
michael@0 361 }
michael@0 362
michael@0 363 - (NSString *) description
michael@0 364 {
michael@0 365 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 366
michael@0 367 return [mFrames description];
michael@0 368
michael@0 369 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 370 }
michael@0 371
michael@0 372 - (void) dealloc
michael@0 373 {
michael@0 374 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 375
michael@0 376 [mFrames release];
michael@0 377 [super dealloc];
michael@0 378
michael@0 379 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 380 }
michael@0 381
michael@0 382 @end

mercurial