CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / cocoa / nsMacCursor.mm
blob4ac48bd79a07629f36a3988e713e63c6b26fd841
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Camino Cursor code.
15  *
16  * The Initial Developer of the Original Code is 
17  * Andrew Thompson.
18  * Portions created by the Andrew Thompson are Copyright (C) 2004
19  * Andrew Thompson. All Rights Reserved.
20  * 
21  * Contributor(s):
22  *    Josh Aas <josh@mozilla.com>
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
38 #include "nsMacCursor.h"
39 #include "nsObjCExceptions.h"
40 #include "nsDebug.h"
41 #include "nsDirectoryServiceDefs.h"
42 #include "nsCOMPtr.h"
43 #include "nsIFile.h"
44 #include "nsString.h"
46 /*! @category   nsMacCursor (PrivateMethods)
47     @abstract   Private methods internal to the nsMacCursor class.
48     @discussion <code>nsMacCursor</code> is effectively an abstract class. It does not define complete 
49                 behaviour in and of itself, the subclasses defined in this file provide the useful implementations.
51 @interface nsMacCursor (PrivateMethods)
53 /*! @method     getNextCursorFrame
54     @abstract   get the index of the next cursor frame to display.
55     @discussion Increments and returns the frame counter of an animated cursor.
56     @result     The index of the next frame to display in the cursor animation
58 - (int) getNextCursorFrame;
60 /*! @method     numFrames
61     @abstract   Query the number of frames in this cursor's animation.
62     @discussion Returns the number of frames in this cursor's animation. Static cursors return 1.
64 - (int) numFrames;
66 /*! @method     createTimer
67     @abstract   Create a Timer to use to animate the cursor.
68     @discussion Creates an instance of <code>NSTimer</code> which is used to drive the cursor animation.
69                 This method should only be called for cursors that are animated.
71 - (void) createTimer;
73 /*! @method     destroyTimer
74     @abstract   Destroy any timer instance associated with this cursor.
75     @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor.
76  */
77 - (void) destroyTimer;
78 /*! @method     destroyTimer
79     @abstract   Destroy any timer instance associated with this cursor.
80     @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor.
83 /*! @method     advanceAnimatedCursor:
84     @abstract   Method called by animation timer to perform animation.
85     @discussion Called by an animated cursor's associated timer to advance the animation to the next frame.
86                 Determines which frame should occur next and sets the cursor to that frame.
87     @param      aTimer the timer causing the animation
89 - (void) advanceAnimatedCursor: (NSTimer *) aTimer;
91 /*! @method     setFrame:
92     @abstract   Sets the current cursor, using an index to determine which frame in the animation to display.
93     @discussion Sets the current cursor. The frame index determines which frame is shown if the cursor is animated.
94                 Frames and numbered from <code>0</code> to <code>-[nsMacCursor numFrames] - 1</code>. A static cursor
95                 has a single frame, numbered 0.
96     @param      aFrameIndex the index indicating which frame from the animation to display
98 - (void) setFrame: (int) aFrameIndex;
100 @end
102 /*! @class      nsCocoaCursor
103     @abstract   Implementation of <code>nsMacCursor</code> that uses Cocoa <code>NSCursor</code> instances.
104     @discussion Displays a static or animated cursor, using Cocoa <code>NSCursor</code> instances. These can be either
105                 built-in <code>NSCursor</code> instances, or custom <code>NSCursor</code>s created from images.
106                 When more than one <code>NSCursor</code> is provided, the cursor will use these as animation frames.
108 @interface nsCocoaCursor : nsMacCursor
110   @private
111   NSArray *mFrames;
112   NSCursor *mLastSetCocoaCursor;
115 /*! @method     initWithFrames:
116     @abstract   Create an animated cursor by specifying the frames to use for the animation.
117     @discussion Creates a cursor that will animate by cycling through the given frames. Each element of the array
118                 must be an instance of <code>NSCursor</code>
119     @param      aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the
120                 order they should be played.
121     @param      aType the corresponding <code>nsCursor</code> constant
122     @result     an instance of <code>nsCocoaCursor</code> that will animate the given cursor frames
123  */
124 - (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType;
126 /*! @method     initWithCursor:
127     @abstract   Create a cursor by specifying a Cocoa <code>NSCursor</code>.
128     @discussion Creates a cursor representing the given Cocoa built-in cursor.
129     @param      aCursor the <code>NSCursor</code> to use
130     @param      aType the corresponding <code>nsCursor</code> constant
131     @result     an instance of <code>nsCocoaCursor</code> representing the given <code>NSCursor</code>
133 - (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType;
135 /*! @method     initWithImageNamed:hotSpot:
136     @abstract   Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot.
137     @discussion Creates a cursor by loading the named image using the <code>+[NSImage imageNamed:]</code> method.
138                 <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary
139                 by operating system version.</p>
140                 <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p>
141     @param      aCursor the name of the image to use for the cursor
142     @param      aPoint the point within the cursor to use as the hotspot
143     @param      aType the corresponding <code>nsCursor</code> constant
144     @result     an instance of <code>nsCocoaCursor</code> that uses the given image and hotspot
146 - (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType;
148 @end
150 @implementation nsMacCursor
152 + (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType
154   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
156   return [[[nsCocoaCursor alloc] initWithCursor:aCursor type:aType] autorelease];
158   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
161 + (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType
163   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
165   return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage hotSpot:aPoint type:aType] autorelease];
167   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
170 + (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType
172   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
174   return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames type:aType] autorelease];
176   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
179 + (NSCursor *) cocoaCursorWithImageNamed: (NSString *) imageName hotSpot: (NSPoint) aPoint
181   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
183   nsCOMPtr<nsIFile> resDir;
184   nsCAutoString resPath;
185   NSString* pathToImage;
186   NSImage* cursorImage;
188   nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir));
189   if (NS_FAILED(rv))
190     goto INIT_FAILURE;
191   resDir->AppendNative(NS_LITERAL_CSTRING("res"));
192   resDir->AppendNative(NS_LITERAL_CSTRING("cursors"));
194   rv = resDir->GetNativePath(resPath);
195   if (NS_FAILED(rv))
196     goto INIT_FAILURE;
198   pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()];
199   if (!pathToImage)
200     goto INIT_FAILURE;
201   pathToImage = [pathToImage stringByAppendingPathComponent:imageName];
202   pathToImage = [pathToImage stringByAppendingPathExtension:@"tiff"];
204   cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease];
205   if (!cursorImage)
206     goto INIT_FAILURE;
207   return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease];
209 INIT_FAILURE:
210   NS_WARNING("Problem getting path to cursor image file!");
211   [self release];
212   return nil;
214   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
217 - (BOOL) isSet
219   // implemented by subclasses
220   return NO;
223 - (void) set
225   if ([self isAnimated]) {
226     [self createTimer];
227   }
228   // if the cursor isn't animated or the timer creation fails for any reason...
229   if (!mTimer) {
230     [self setFrame:0];
231   }
234 - (void) unset
236   [self destroyTimer];    
239 - (BOOL) isAnimated
241   return [self numFrames] > 1;
244 - (int) numFrames
246   // subclasses need to override this to support animation
247   return 1;
250 - (int) getNextCursorFrame
252   mFrameCounter = (mFrameCounter + 1) % [self numFrames];
253   return mFrameCounter;
256 - (void) createTimer
258   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
260   if (!mTimer) {
261     mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.25
262                                                target:self
263                                              selector:@selector(advanceAnimatedCursor:)
264                                              userInfo:nil
265                                               repeats:YES] retain];
266   }
268   NS_OBJC_END_TRY_ABORT_BLOCK;
271 - (void) destroyTimer
273   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
275   if (mTimer) {
276       [mTimer invalidate];
277       [mTimer release];
278       mTimer = nil;
279   }
281   NS_OBJC_END_TRY_ABORT_BLOCK;
284 - (void) advanceAnimatedCursor: (NSTimer *) aTimer
286   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
288   if ([aTimer isValid]) {
289     [self setFrame:[self getNextCursorFrame]];
290   }
292   NS_OBJC_END_TRY_ABORT_BLOCK;
295 - (void) setFrame: (int) aFrameIndex
297   // subclasses need to do something useful here
300 - (nsCursor) type {
301   return mType;
304 - (void) dealloc
306   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
308   [self destroyTimer];
309   [super dealloc];
311   NS_OBJC_END_TRY_ABORT_BLOCK;
314 @end
316 @implementation nsCocoaCursor
318 - (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType
320   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
322   self = [super init];
323   NSEnumerator *it = [aCursorFrames objectEnumerator];
324   NSObject *frame = nil;
325   while ((frame = [it nextObject])) {
326     NS_ASSERTION([frame isKindOfClass:[NSCursor class]], "Invalid argument: All frames must be of type NSCursor");
327   }
328   mFrames = [aCursorFrames retain];
329   mFrameCounter = 0;
330   mType = aType;
331   return self;
333   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
336 - (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType
338   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
340   NSArray *frame = [NSArray arrayWithObjects:aCursor, nil];
341   return [self initWithFrames:frame type:aType];
343   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
346 - (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType
348   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
350   return [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage hotSpot:aPoint] type:aType];
352   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
355 - (BOOL) isSet
357   return [NSCursor currentCursor] == mLastSetCocoaCursor;
360 - (void) setFrame: (int) aFrameIndex
362   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
364   NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex];
365   [newCursor set];
366   mLastSetCocoaCursor = newCursor;
368   NS_OBJC_END_TRY_ABORT_BLOCK;
371 - (int) numFrames
373   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
375   return [mFrames count];
377   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
380 - (NSString *) description
382   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
384   return [mFrames description];
386   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
389 - (void) dealloc
391   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
393   [mFrames release];
394   [super dealloc];
396   NS_OBJC_END_TRY_ABORT_BLOCK;
399 @end