animated cursors for cocoa widget, like we did for carbon (bug 208717)

This commit is contained in:
pinkerton%aol.net 2004-04-29 03:14:21 +00:00
Родитель 1d237e8381
Коммит 28f2eb8765
6 изменённых файлов: 858 добавлений и 55 удалений

Просмотреть файл

@ -112,6 +112,8 @@ CMMSRCS = \
nsChildView.mm \
nsWidgetFactory.mm \
nsNativeScrollbar.mm \
nsCursorManager.mm \
nsMacCursor.mm \
$(NULL)
XPIDLSRCS += \

Просмотреть файл

@ -64,7 +64,7 @@
#endif
#include <unistd.h>
#include "nsCursorManager.h"
@interface ChildView(Private)
@ -116,7 +116,6 @@ nsIWidget * gRollupWidget = nsnull;
static NMRec gNMRec;
static Boolean gNotificationInstalled = false;
#pragma mark -
//#define PAINT_DEBUGGING // flash areas as they are painted
@ -904,59 +903,7 @@ nsIMenuBar* nsChildView::GetMenuBar()
NS_METHOD nsChildView::SetCursor(nsCursor aCursor)
{
nsBaseWidget::SetCursor(aCursor);
short cursor = -1;
switch (aCursor)
{
case eCursor_standard: cursor = kThemeArrowCursor; break;
case eCursor_wait: cursor = kThemeWatchCursor; break;
case eCursor_select: cursor = kThemeIBeamCursor; break;
case eCursor_hyperlink: cursor = kThemePointingHandCursor; break;
case eCursor_sizeWE: cursor = kThemeResizeLeftRightCursor; break;
case eCursor_sizeNS: cursor = 129; break;
case eCursor_sizeNW: cursor = 130; break;
case eCursor_sizeSE: cursor = 131; break;
case eCursor_sizeNE: cursor = 132; break;
case eCursor_sizeSW: cursor = 133; break;
case eCursor_arrow_north: cursor = 134; break;
case eCursor_arrow_north_plus:cursor = 135; break;
case eCursor_arrow_south: cursor = 136; break;
case eCursor_arrow_south_plus:cursor = 137; break;
case eCursor_arrow_west: cursor = 138; break;
case eCursor_arrow_west_plus: cursor = 139; break;
case eCursor_arrow_east: cursor = 140; break;
case eCursor_arrow_east_plus: cursor = 141; break;
case eCursor_crosshair: cursor = kThemeCrossCursor; break;
case eCursor_move: cursor = kThemeOpenHandCursor; break;
case eCursor_help: cursor = 143; break;
case eCursor_copy: cursor = 144; break; // CSS3
case eCursor_alias: cursor = 145; break;
case eCursor_context_menu: cursor = 146; break;
case eCursor_cell: cursor = kThemePlusCursor; break;
case eCursor_grab: cursor = kThemeOpenHandCursor; break;
case eCursor_grabbing: cursor = kThemeClosedHandCursor; break;
case eCursor_spinning: cursor = 200; break; // better than kThemeSpinningCursor
case eCursor_count_up: cursor = kThemeCountingUpHandCursor; break;
case eCursor_count_down: cursor = kThemeCountingDownHandCursor; break;
case eCursor_count_up_down: cursor = kThemeCountingUpAndDownHandCursor; break;
case eCursor_zoom_in: cursor = 149; break;
case eCursor_zoom_out: cursor = 150; break;
}
if (cursor >= 0)
{
if (cursor >= 128)
{
nsMacResources::OpenLocalResourceFile();
CursHandle cursHandle = ::GetCursor(cursor);
NS_ASSERTION ( cursHandle, "Can't load cursor, is the resource file installed correctly?" );
if ( cursHandle )
::SetCursor(*cursHandle);
nsMacResources::CloseLocalResourceFile();
}
else
::SetThemeCursor(cursor);
}
[[nsCursorManager sharedInstance] setCursor: aCursor];
return NS_OK;
} // nsChildView::SetCursor
@ -1338,6 +1285,7 @@ NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
if (!mView || !mVisible)
return NS_OK;
printf(". invalidate full widget\n");
if (aIsSynchronous)
[mView display];
else
@ -1359,6 +1307,7 @@ NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous
NSRect r;
ConvertGeckoToCocoaRect ( aRect, r );
printf(". invalidate rect\n");
if (aIsSynchronous)
[mView displayRect:r];
else
@ -1375,6 +1324,7 @@ NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous
//-------------------------------------------------------------------------
NS_IMETHODIMP nsChildView::Validate()
{
printf(". validate full widget\n");
[mView setNeedsDisplay:NO];
return NS_OK;
}
@ -1398,6 +1348,7 @@ NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIs
region->GetBoundingBox ( &bounds.x, &bounds.y, &bounds.width, &bounds.height );
ConvertGeckoToCocoaRect(bounds, r);
printf(". invalidate region\n");
if ( aIsSynchronous )
[mView displayRect:r];
else
@ -2476,6 +2427,7 @@ nsChildView::Idle()
}
// tell gecko to paint.
printf("- drawRect in nsChildView\n");
nsRect r;
ConvertCocoaToGeckoRect(aRect, r);
nsCOMPtr<nsIRenderingContext> rendContext = getter_AddRefs(mGeckoChild->GetRenderingContext());

Просмотреть файл

@ -0,0 +1,73 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is nsCursors.
*
* The Initial Developer of the Original Code is Andrew Thompson.
* Portions created by the Andrew Thompson are Copyright (C) 2004
* Andrew Thompson. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import <Foundation/Foundation.h>
#import "nsIWidget.h"
#import "nsMacCursor.h"
/*! @class nsCursorManager
@abstract Singleton service provides access to all cursors available in the application.
@discussion Use <code>nsCusorManager</code> to set the current cursor using an XP <code>nsCusor</code> enum value.
<code>nsCursorManager</code> encapsulates the details of setting different types of cursors, animating
cursors and cleaning up cursors when they are no longer in use.
*/
@interface nsCursorManager : NSObject
{
@private
NSMutableDictionary *mCursors;
nsCursor mCurrentCursor;
}
/*! @method setCursor:
@abstract Sets the current cursor.
@discussion Sets the current cursor to the cursor indicated by the XP cursor constant given as an argument.
Resources associated with the previous cursor are cleaned up.
@param aCursor the cursor to use
*/
- (void) setCursor: (nsCursor) aCursor;
/*! @method sharedInstance
@abstract Get the Singleton instance of the cursor manager.
@discussion Use this method to obtain a reference to the cursor manager.
@result a reference to the cursor manager
*/
+ (nsCursorManager *) sharedInstance;
/*! @method dispose
@abstract Releases the shared instance of the cursor manager.
@discussion Use dispose to clean up the cursor manager and associated cursors.
*/
+ (void) dispose;
@end

Просмотреть файл

@ -0,0 +1,242 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is nsCursors.
*
* The Initial Developer of the Original Code is Andrew Thompson.
* Portions created by the Andrew Thompson are Copyright (C) 2004
* Andrew Thompson. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import "nsCursorManager.h"
#import "math.h"
static nsCursorManager *gInstance;
/*! @category nsCursorManager(PrivateMethods)
Private methods for the cursor manager class.
*/
@interface nsCursorManager(PrivateMethods)
/*! @method getCursor:
@abstract Get a reference to the native Mac representation of a cursor.
@discussion Gets a reference to the Mac native implementation of a cursor.
If the cursor has been requested before, it is retreived from the cursor cache, otherwise is it created
and cached.
@param aCursor the cursor to get
@result the Mac native implementation of the cursor
*/
- (nsMacCursor *) getCursor: (nsCursor) aCursor;
/*! @method createCursor:
@abstract Create a Mac native representation of a cursor.
@discussion Creates a version of the Mac native representation of this cursor suitable for use on this version of
Mac OS X
@param aCursor the cursor to create
@result the Mac native implementation of the cursor
*/
+ (nsMacCursor *) createCursor: (enum nsCursor) aCursor;
/*! @method createNSCursor:orThemeCursor:
@abstract Creates the appropriate cursor implementation from the arguments.
@discussion Creates a native Mac cursor, using NSCursor if the cursor is available on this version of Mac OS X,
otherwise falls back to creating a traditional Carbon AppearanceManager ThemeCursor.
@param aPantherCursor selector indicating the NSCursor cursor to create if on Panther or later
@param aJaguarCursor the ThemeCursor to use as an alternative if running on Jaguar
@result the Mac native implementation of the cursor
*/
+ (nsMacCursor *) createNSCursor: (SEL) aPantherCursor orThemeCursor: (ThemeCursor) aJaguarCursor;
/*! @method createNSCursor:orImageCursor:withHotspot:
@abstract Creates the appropriate cursor implementation from the arguments.
@discussion Creates a native Mac cursor, using NSCursor if the cursor is available on this version of Mac OS X,
otherwise falls back to creating an NSCursor instance from a custom image and hotspot.
@param aPantherCursor selector indicating the NSCursor cursor to create if on Panther or later
@param aImageName the name of the image to use for the cursor as the cursor on Jagua
@param aPoint the hotspot to use with the image to form a cursor on Jaguar
@result the Mac native implementation of the cursor
*/
+ (nsMacCursor *) createNSCursor: (SEL) aPantherCursor orImageCursor: (NSString *) aImageName withHotspot: (NSPoint) aPoint;
@end
/*! @function isPantherOrLater
@abstract Determine whether we are running on Panther (Mac OS X 10.3) or later
@result YES if the current operating system version is 10.3 or later, else NO
*/
static BOOL isPantherOrLater();
static BOOL isPantherOrLater()
{
static PRBool gInitVer = PR_FALSE;
static PRBool gOnPantherOrLater = PR_FALSE;
if(!gInitVer)
{
long version;
OSErr err = ::Gestalt(gestaltSystemVersion, &version);
gOnPantherOrLater = (err == noErr && version >= 0x00001030);
gInitVer = PR_TRUE;
}
return gOnPantherOrLater;
}
@implementation nsCursorManager
+ (nsCursorManager *) sharedInstance
{
if (gInstance == nil)
{
gInstance = [[nsCursorManager alloc] init];
}
return gInstance;
}
+ (void) dispose
{
[gInstance release];
gInstance = nil;
}
+ (nsMacCursor *) createCursor: (enum nsCursor) aCursor
{
switch(aCursor)
{
case eCursor_standard:
return [nsMacCursor cursorWithCursor: [NSCursor arrowCursor]];
case eCursor_wait:
return [nsMacCursor cursorWithThemeCursor: kThemeWatchCursor];
case eCursor_select:
return [nsMacCursor cursorWithCursor: [NSCursor IBeamCursor]];
case eCursor_hyperlink:
return [nsCursorManager createNSCursor: @selector(pointingHandCursor) orThemeCursor: kThemePointingHandCursor];
case eCursor_sizeWE:
return [nsCursorManager createNSCursor: @selector(resizeLeftRightCursor) orThemeCursor: kThemeResizeLeftRightCursor];
case eCursor_sizeNS:
return [nsCursorManager createNSCursor: @selector(resizeUpDownCursor) orImageCursor: @"sizeNS" withHotspot: NSMakePoint(7,7)];
case eCursor_sizeNW:
return [nsMacCursor cursorWithImageNamed: @"sizeNW" hotSpot: NSMakePoint(7,7)];
case eCursor_sizeSE:
return [nsMacCursor cursorWithImageNamed: @"sizeSE" hotSpot: NSMakePoint(8,8)];
case eCursor_sizeNE:
return [nsMacCursor cursorWithImageNamed: @"sizeNE" hotSpot: NSMakePoint(8,7)];
case eCursor_sizeSW:
return [nsMacCursor cursorWithImageNamed: @"sizeSW" hotSpot: NSMakePoint(6,8)];
case eCursor_arrow_north:
case eCursor_arrow_north_plus:
return [nsCursorManager createNSCursor: @selector(resizeUpCursor) orImageCursor: @"arrowN" withHotspot: NSMakePoint(7,7)];
case eCursor_arrow_south:
case eCursor_arrow_south_plus:
return [nsCursorManager createNSCursor: @selector(resizeDownCursor) orImageCursor: @"arrowS" withHotspot: NSMakePoint(7,7)];
case eCursor_arrow_west:
case eCursor_arrow_west_plus:
return [nsCursorManager createNSCursor: @selector(resizeLeftCursor) orThemeCursor: kThemeResizeLeftCursor];
case eCursor_arrow_east:
case eCursor_arrow_east_plus:
return [nsCursorManager createNSCursor: @selector(resizeRightCursor) orThemeCursor: kThemeResizeRightCursor];
case eCursor_crosshair:
return [nsCursorManager createNSCursor: @selector(crosshairCursor) orThemeCursor: kThemeCrossCursor];
case eCursor_move:
return [nsCursorManager createNSCursor: @selector(openHandCursor) orThemeCursor: kThemeOpenHandCursor];
case eCursor_help:
return [nsMacCursor cursorWithImageNamed: @"help" hotSpot: NSMakePoint(1,1)];
case eCursor_copy:
return [nsMacCursor cursorWithThemeCursor: kThemeCopyArrowCursor];
case eCursor_alias:
return [nsMacCursor cursorWithThemeCursor: kThemeAliasArrowCursor];
case eCursor_context_menu:
return [nsMacCursor cursorWithImageNamed: @"contextMenu" hotSpot: NSMakePoint(1,1)];
case eCursor_cell:
return [nsMacCursor cursorWithThemeCursor: kThemePlusCursor];
case eCursor_grab:
return [nsCursorManager createNSCursor: @selector(openHandCursor) orThemeCursor: kThemeOpenHandCursor];
case eCursor_grabbing:
return [nsCursorManager createNSCursor: @selector(closedHandCursor) orThemeCursor: kThemeClosedHandCursor];
case eCursor_spinning:
return [nsMacCursor cursorWithResources: 200 lastFrame: 203]; // better than kThemeSpinningCursor
case eCursor_count_up:
return [nsMacCursor cursorWithThemeCursor: kThemeCountingUpHandCursor];
case eCursor_count_down:
return [nsMacCursor cursorWithThemeCursor: kThemeCountingDownHandCursor];
case eCursor_count_up_down:
return [nsMacCursor cursorWithThemeCursor: kThemeCountingUpAndDownHandCursor];
case eCursor_zoom_in:
return [nsMacCursor cursorWithImageNamed: @"zoomIn" hotSpot: NSMakePoint(6,6)];
case eCursor_zoom_out:
return [nsMacCursor cursorWithImageNamed: @"zoomOut" hotSpot: NSMakePoint(6,6)];
default:
return [nsMacCursor cursorWithCursor: [NSCursor arrowCursor]];
}
}
+ (nsMacCursor *) createNSCursor: (SEL) aPantherCursor orThemeCursor: (ThemeCursor) aJaguarCursor
{
return isPantherOrLater() ? [nsMacCursor cursorWithCursor: [NSCursor performSelector: aPantherCursor]] :
[nsMacCursor cursorWithThemeCursor: aJaguarCursor];
}
+ (nsMacCursor *) createNSCursor: (SEL) aPantherCursor orImageCursor: (NSString *) aImageName withHotspot: (NSPoint) aPoint
{
return isPantherOrLater() ? [nsMacCursor cursorWithCursor: [NSCursor performSelector: aPantherCursor]] :
[nsMacCursor cursorWithImageNamed: aImageName hotSpot: aPoint];
}
- (id) init
{
if ( (self = [super init]) )
{
mCursors = [[NSMutableDictionary alloc] initWithCapacity: 25];
}
return self;
}
- (void) setCursor: (enum nsCursor) aCursor
{
if (aCursor != mCurrentCursor)
{
[[self getCursor: mCurrentCursor] unset];
[[self getCursor: aCursor] set];
mCurrentCursor = aCursor;
}
}
- (nsMacCursor *) getCursor: (enum nsCursor) aCursor
{
nsMacCursor * result = [mCursors objectForKey: [NSNumber numberWithInt: aCursor]];
if ( result == nil )
{
result = [nsCursorManager createCursor: aCursor];
[mCursors setObject: result forKey: [NSNumber numberWithInt: aCursor]];
}
return result;
}
- (void) dealloc
{
[[self getCursor: mCurrentCursor] unset];
[mCursors release];
[super dealloc];
}
@end

Просмотреть файл

@ -0,0 +1,121 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is nsCursors.
*
* The Initial Developer of the Original Code is Andrew Thompson.
* Portions created by the Andrew Thompson are Copyright (C) 2004
* Andrew Thompson. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.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;
int mFrameCounter;
}
/*! @method cursorWithThemeCursor:
@abstract Create a cursor by specifying a Carbon Apperance Manager <code>ThemeCursor</code> constant.
@discussion Creates a cursor representing the given Appearance Manager built in cursor.
@param aCursor the <code>ThemeCursor</code> to use
@result an autoreleased instance of <code>nsMacCursor</code> representing the given <code>ThemeCursor</code>
*/
+ (nsMacCursor *) cursorWithThemeCursor: (ThemeCursor) aCursor;
/*! @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
@result an autoreleased instance of <code>nsMacCursor</code> representing the given <code>NSCursor</code>
*/
+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor;
/*! @method cursorWithImageNamed: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. eg, Jaguar has a smaller maximum size than Panther.</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
@result an autoreleased instance of <code>nsMacCursor</code> that uses the given image and hotspot
*/
+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint;
/*! @method cursorWithFrames:
@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.
@result an autoreleased instance of <code>nsMacCursor</code> that will animate the given cursor frames
*/
+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames;
/*! @method cursorWithResources:lastFrame:
@abstract Create an animated cursor by specifying a range of <code>CURS</code> resources to load and animate.
@discussion Creates a cursor that will animate by cycling through the given range of cursor resource ids. Each
resource in the range must be the next frame in the animation.
<p>To create a static cursor, simply pass the same resource id for both parameters.</p>
<p>The frames are loaded from the compiled version of the resource file nsMacWidget.r.</p>
@param aFirstFrame the resource id for the first frame of the animation. Must be 128 or greated
@param aLastFrame the resource id for the last frame of the animation. Must be 128 or greater, and greater than
or equal to <code>aFirstFrame</code>
@result an autoreleased instance of <code>nsMacCursor</code> that will animate the given cursor resources
*/
+ (nsMacCursor *) cursorWithResources: (int) aFirstFrame lastFrame: (int) aLastFrame;
/*! @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;
@end

Просмотреть файл

@ -0,0 +1,413 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Camino Cursor Code.
*
* The Initial Developer of the Original Code is Andrew Thompson.
* Portions created by the Andrew Thompson are Copyright (C) 2004
* Andrew Thompson. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import "nsMacCursor.h"
#import "nsMacResources.h"
#import "nsDebug.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 spinCursor:
@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) spinCursor: (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 nsThemeCursor
@abstract Implementation of <code>nsMacCursor</code> that uses Carbon Appearance Manager cursors.
@discussion Displays a static or animated <code>ThemeCursor</code> using Carbon Appearance Manager functions.
Understands how many frames exist in each of the built-in <code>ThemeCursor</code>s.
*/
@interface nsThemeCursor : nsMacCursor
{
@private
ThemeCursor mCursor;
}
/*! @method initWithThemeCursor:
@abstract Create a cursor by specifying a Carbon Apperance Manager <code>ThemeCursor</code> constant.
@discussion Creates a cursor representing the given Appearance Manager built in cursor.
@param aCursor the <code>ThemeCursor</code> to use
@result an instance of <code>nsThemeCursor</code> representing the given <code>ThemeCursor</code>
*/
- (id) initWithThemeCursor: (ThemeCursor) aCursor;
@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;
}
/*! @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.
@result an instance of <code>nsCocoaCursor</code> that will animate the given cursor frames
*/
- (id) initWithFrames: (NSArray *) aCursorFrames;
/*! @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
@result an instance of <code>nsCocoaCursor</code> representing the given <code>NSCursor</code>
*/
- (id) initWithCursor: (NSCursor *) aCursor;
/*! @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. eg, Jaguar has a smaller maximum size than Panther.</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
@result an instance of <code>nsCocoaCursor</code> that uses the given image and hotspot
*/
- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint;
@end
/*! @class nsResourceCursor
@abstract Implementation of <code>nsMacCursor</code> that uses Carbon <code>CURS</code> resources.
@discussion Displays a static or animated cursor, using Carbon <code>CURS</code> resources.
<p>Animated cursors are produced by cycling through a range of cursor resource ids.</p>
<p>The frames are loaded from the compiled version of the resource file nsMacWidget.r.</p>
*/
@interface nsResourceCursor : nsMacCursor
{
@private
int mFirstFrame;
int mLastFrame;
}
/*! @method initWithResources:lastFrame:
@abstract Create an animated cursor by specifying a range of <code>CURS</code> resources to load and animate.
@discussion Creates a cursor that will animate by cycling through the given range of cursor resource ids. Each
resource in the range must be the next frame in the animation.
<p>To create a static cursor, simply pass the same resource id for both parameters.</p>
<p>The frames are loaded from the compiled version of the resource file nsMacWidget.r.</p>
@param aFirstFrame the resource id for the first frame of the animation. Must be 128 or greated
@param aLastFrame the resource id for the last frame of the animation. Must be 128 or greater, and greater than
or equal to <code>aFirstFrame</code>
@result an instance of <code>nsResourceCursor</code> that will animate the given cursor resources
*/
- (id) initWithFirstFrame: (int) aFirstFrame lastFrame: (int) aLastFrame;
@end
@implementation nsMacCursor
+ (nsMacCursor *) cursorWithThemeCursor: (ThemeCursor) aCursor
{
return [[[nsThemeCursor alloc] initWithThemeCursor: aCursor] autorelease];
}
+ (nsMacCursor *) cursorWithResources: (int) aFirstFrame lastFrame: (int) aLastFrame
{
return [[[nsResourceCursor alloc] initWithFirstFrame: aFirstFrame lastFrame: aLastFrame] autorelease];
}
+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor
{
return [[[nsCocoaCursor alloc] initWithCursor: aCursor] autorelease];
}
+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint
{
return [[[nsCocoaCursor alloc] initWithImageNamed: aCursorImage hotSpot: aPoint] autorelease];
}
+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames
{
return [[[nsCocoaCursor alloc] initWithFrames: aCursorFrames] autorelease];
}
- (void) set
{
if ( [self isAnimated])
{
[self createTimer];
}
//if the cursor isn't animated or the timer creation fails for any reason...
if (mTimer == nil)
{
[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
{
if ( mTimer == nil)
{
mTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.25
target: self
selector: @selector(spinCursor:)
userInfo: nil
repeats: YES] retain];
}
}
- (void) destroyTimer
{
if ( mTimer != nil)
{
[mTimer invalidate];
[mTimer release];
mTimer = nil;
}
}
- (void) spinCursor: (NSTimer *) aTimer
{
if ( [aTimer isValid] )
{
[self setFrame: [self getNextCursorFrame]];
}
}
- (void) setFrame: (int) aFrameIndex
{
//subclasses need to do something useful here
}
- (void) dealloc
{
[self destroyTimer];
[super dealloc];
}
@end
@implementation nsThemeCursor
- (id) initWithThemeCursor: (ThemeCursor) aCursor
{
self = [super init];
//Appearance Manager cursors all fall into the range 0..127. Custom application CURS resources begin at id 128.
NS_ASSERTION(mCursor >= 0 && mCursor < 128, "Theme cursors must be in the range 0 <= num < 128");
mCursor = aCursor;
return self;
}
- (void) setFrame: (int) aFrameIndex
{
if ( [self isAnimated] )
{
//if the cursor is animated try to draw the appropriate frame
OSStatus err = ::SetAnimatedThemeCursor(mCursor, aFrameIndex);
if ( err != noErr )
{
//in the event of any kind of problem, just try to show the first frame
::SetThemeCursor(mCursor);
}
}
else
{
::SetThemeCursor(mCursor);
}
}
- (int) numFrames
{
//These don't appear to be documented. Trial and Error...
switch (mCursor)
{
case kThemeWatchCursor:
case kThemeSpinningCursor:
return 8;
case kThemeCountingUpHandCursor:
case kThemeCountingDownHandCursor:
return 6;
case kThemeCountingUpAndDownHandCursor:
return 11;
default:
return 1;
}
}
@end
@implementation nsCocoaCursor
- (id) initWithFrames: (NSArray *) aCursorFrames
{
self = [super init];
NSEnumerator *it = [aCursorFrames objectEnumerator];
NSObject *frame = nil;
while ((frame = [it nextObject]) != nil)
{
NS_ASSERTION([frame isKindOfClass: [NSCursor class]], "Invalid argument: All frames must be of type NSCursor");
}
mFrames = [aCursorFrames retain];
mFrameCounter = 0;
return self;
}
- (id) initWithCursor: (NSCursor *) aCursor
{
NSArray *frame = [NSArray arrayWithObjects: aCursor, nil];
return [self initWithFrames: frame];
}
- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint
{
return [self initWithCursor: [[NSCursor alloc] initWithImage: [NSImage imageNamed: aCursorImage] hotSpot: aPoint]];
}
- (void) setFrame: (int) aFrameIndex
{
[[mFrames objectAtIndex: aFrameIndex] performSelectorOnMainThread: @selector(set) withObject: nil waitUntilDone: NO];
}
- (int) numFrames
{
return [mFrames count];
}
- (NSString *) description
{
return [mFrames description];
}
- (void) dealloc
{
[mFrames release];
[super dealloc];
}
@end
@implementation nsResourceCursor
-(id) initWithFirstFrame: (int) aFirstFrame lastFrame: (int) aLastFrame
{
self= [super init];
//Appearance Manager cursors all fall into the range 0..127. Custom application CURS resources begin at id 128.
NS_ASSERTION(aFirstFrame >= 128 && aLastFrame >= 128 && aLastFrame >= aFirstFrame, "Nonsensical frame indicies");
mFirstFrame = aFirstFrame;
mLastFrame = aLastFrame;
return self;
}
- (void) setFrame: (int) aFrameIndex
{
nsMacResources::OpenLocalResourceFile();
CursHandle cursHandle = ::GetCursor(mFirstFrame + aFrameIndex);
NS_ASSERTION(cursHandle, "Can't load cursor, is the resource file installed correctly?");
if (cursHandle)
{
::SetCursor(*cursHandle);
}
nsMacResources::CloseLocalResourceFile();
}
- (int) numFrames
{
return (mLastFrame - mFirstFrame) + 1;
}
@end