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