Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 "imgIContainer.h" |
michael@0 | 6 | #include "nsCocoaUtils.h" |
michael@0 | 7 | #include "nsCursorManager.h" |
michael@0 | 8 | #include "nsObjCExceptions.h" |
michael@0 | 9 | #include <math.h> |
michael@0 | 10 | |
michael@0 | 11 | static nsCursorManager *gInstance; |
michael@0 | 12 | static CGFloat sCursorScaleFactor = 0.0f; |
michael@0 | 13 | static imgIContainer *sCursorImgContainer = nullptr; |
michael@0 | 14 | static const nsCursor sCustomCursor = eCursorCount; |
michael@0 | 15 | |
michael@0 | 16 | /*! @category nsCursorManager(PrivateMethods) |
michael@0 | 17 | Private methods for the cursor manager class. |
michael@0 | 18 | */ |
michael@0 | 19 | @interface nsCursorManager(PrivateMethods) |
michael@0 | 20 | /*! @method getCursor: |
michael@0 | 21 | @abstract Get a reference to the native Mac representation of a cursor. |
michael@0 | 22 | @discussion Gets a reference to the Mac native implementation of a cursor. |
michael@0 | 23 | If the cursor has been requested before, it is retreived from the cursor cache, |
michael@0 | 24 | otherwise it is created and cached. |
michael@0 | 25 | @param aCursor the cursor to get |
michael@0 | 26 | @result the Mac native implementation of the cursor |
michael@0 | 27 | */ |
michael@0 | 28 | - (nsMacCursor *) getCursor: (nsCursor) aCursor; |
michael@0 | 29 | |
michael@0 | 30 | /*! @method setMacCursor: |
michael@0 | 31 | @abstract Set the current Mac native cursor |
michael@0 | 32 | @discussion Sets the current cursor - this routine is what actually causes the cursor to change. |
michael@0 | 33 | The argument is retained and the old cursor is released. |
michael@0 | 34 | @param aMacCursor the cursor to set |
michael@0 | 35 | @result NS_OK |
michael@0 | 36 | */ |
michael@0 | 37 | - (nsresult) setMacCursor: (nsMacCursor*) aMacCursor; |
michael@0 | 38 | |
michael@0 | 39 | /*! @method createCursor: |
michael@0 | 40 | @abstract Create a Mac native representation of a cursor. |
michael@0 | 41 | @discussion Creates a version of the Mac native representation of this cursor |
michael@0 | 42 | @param aCursor the cursor to create |
michael@0 | 43 | @result the Mac native implementation of the cursor |
michael@0 | 44 | */ |
michael@0 | 45 | + (nsMacCursor *) createCursor: (enum nsCursor) aCursor; |
michael@0 | 46 | |
michael@0 | 47 | @end |
michael@0 | 48 | |
michael@0 | 49 | @implementation nsCursorManager |
michael@0 | 50 | |
michael@0 | 51 | + (nsCursorManager *) sharedInstance |
michael@0 | 52 | { |
michael@0 | 53 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
michael@0 | 54 | |
michael@0 | 55 | if (!gInstance) { |
michael@0 | 56 | gInstance = [[nsCursorManager alloc] init]; |
michael@0 | 57 | } |
michael@0 | 58 | return gInstance; |
michael@0 | 59 | |
michael@0 | 60 | NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | + (void) dispose |
michael@0 | 64 | { |
michael@0 | 65 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
michael@0 | 66 | |
michael@0 | 67 | [gInstance release]; |
michael@0 | 68 | gInstance = nil; |
michael@0 | 69 | |
michael@0 | 70 | NS_OBJC_END_TRY_ABORT_BLOCK; |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | + (nsMacCursor *) createCursor: (enum nsCursor) aCursor |
michael@0 | 74 | { |
michael@0 | 75 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
michael@0 | 76 | |
michael@0 | 77 | switch(aCursor) |
michael@0 | 78 | { |
michael@0 | 79 | SEL cursorSelector; |
michael@0 | 80 | case eCursor_standard: |
michael@0 | 81 | return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; |
michael@0 | 82 | case eCursor_wait: |
michael@0 | 83 | case eCursor_spinning: |
michael@0 | 84 | { |
michael@0 | 85 | return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor]; |
michael@0 | 86 | } |
michael@0 | 87 | case eCursor_select: |
michael@0 | 88 | return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor]; |
michael@0 | 89 | case eCursor_hyperlink: |
michael@0 | 90 | return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor]; |
michael@0 | 91 | case eCursor_crosshair: |
michael@0 | 92 | return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor]; |
michael@0 | 93 | case eCursor_move: |
michael@0 | 94 | return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 95 | case eCursor_help: |
michael@0 | 96 | return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 97 | case eCursor_copy: |
michael@0 | 98 | cursorSelector = @selector(dragCopyCursor); |
michael@0 | 99 | return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? |
michael@0 | 100 | [NSCursor performSelector:cursorSelector] : |
michael@0 | 101 | [NSCursor arrowCursor] type:aCursor]; |
michael@0 | 102 | case eCursor_alias: |
michael@0 | 103 | cursorSelector = @selector(dragLinkCursor); |
michael@0 | 104 | return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? |
michael@0 | 105 | [NSCursor performSelector:cursorSelector] : |
michael@0 | 106 | [NSCursor arrowCursor] type:aCursor]; |
michael@0 | 107 | case eCursor_context_menu: |
michael@0 | 108 | cursorSelector = @selector(contextualMenuCursor); |
michael@0 | 109 | return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? |
michael@0 | 110 | [NSCursor performSelector:cursorSelector] : |
michael@0 | 111 | [NSCursor arrowCursor] type:aCursor]; |
michael@0 | 112 | case eCursor_cell: |
michael@0 | 113 | return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 114 | case eCursor_grab: |
michael@0 | 115 | return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; |
michael@0 | 116 | case eCursor_grabbing: |
michael@0 | 117 | return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor]; |
michael@0 | 118 | case eCursor_zoom_in: |
michael@0 | 119 | return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10,10) type:aCursor]; |
michael@0 | 120 | case eCursor_zoom_out: |
michael@0 | 121 | return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10,10) type:aCursor]; |
michael@0 | 122 | case eCursor_vertical_text: |
michael@0 | 123 | return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12,11) type:aCursor]; |
michael@0 | 124 | case eCursor_all_scroll: |
michael@0 | 125 | return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; |
michael@0 | 126 | case eCursor_not_allowed: |
michael@0 | 127 | case eCursor_no_drop: |
michael@0 | 128 | cursorSelector = @selector(operationNotAllowedCursor); |
michael@0 | 129 | return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? |
michael@0 | 130 | [NSCursor performSelector:cursorSelector] : |
michael@0 | 131 | [NSCursor arrowCursor] type:aCursor]; |
michael@0 | 132 | // Resize Cursors: |
michael@0 | 133 | // North |
michael@0 | 134 | case eCursor_n_resize: |
michael@0 | 135 | return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor]; |
michael@0 | 136 | // North East |
michael@0 | 137 | case eCursor_ne_resize: |
michael@0 | 138 | return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12,11) type:aCursor]; |
michael@0 | 139 | // East |
michael@0 | 140 | case eCursor_e_resize: |
michael@0 | 141 | return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor]; |
michael@0 | 142 | // South East |
michael@0 | 143 | case eCursor_se_resize: |
michael@0 | 144 | return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 145 | // South |
michael@0 | 146 | case eCursor_s_resize: |
michael@0 | 147 | return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor]; |
michael@0 | 148 | // South West |
michael@0 | 149 | case eCursor_sw_resize: |
michael@0 | 150 | return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10,12) type:aCursor]; |
michael@0 | 151 | // West |
michael@0 | 152 | case eCursor_w_resize: |
michael@0 | 153 | return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor]; |
michael@0 | 154 | // North West |
michael@0 | 155 | case eCursor_nw_resize: |
michael@0 | 156 | return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11,11) type:aCursor]; |
michael@0 | 157 | // North & South |
michael@0 | 158 | case eCursor_ns_resize: |
michael@0 | 159 | return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor]; |
michael@0 | 160 | // East & West |
michael@0 | 161 | case eCursor_ew_resize: |
michael@0 | 162 | return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor]; |
michael@0 | 163 | // North East & South West |
michael@0 | 164 | case eCursor_nesw_resize: |
michael@0 | 165 | return [nsMacCursor cursorWithImageNamed:@"sizeNESW" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 166 | // North West & South East |
michael@0 | 167 | case eCursor_nwse_resize: |
michael@0 | 168 | return [nsMacCursor cursorWithImageNamed:@"sizeNWSE" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 169 | // Column Resize |
michael@0 | 170 | case eCursor_col_resize: |
michael@0 | 171 | return [nsMacCursor cursorWithImageNamed:@"colResize" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 172 | // Row Resize |
michael@0 | 173 | case eCursor_row_resize: |
michael@0 | 174 | return [nsMacCursor cursorWithImageNamed:@"rowResize" hotSpot:NSMakePoint(12,12) type:aCursor]; |
michael@0 | 175 | default: |
michael@0 | 176 | return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | - (id) init |
michael@0 | 183 | { |
michael@0 | 184 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
michael@0 | 185 | |
michael@0 | 186 | if ((self = [super init])) { |
michael@0 | 187 | mCursors = [[NSMutableDictionary alloc] initWithCapacity:25]; |
michael@0 | 188 | } |
michael@0 | 189 | return self; |
michael@0 | 190 | |
michael@0 | 191 | NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | - (nsresult) setCursor: (enum nsCursor) aCursor |
michael@0 | 195 | { |
michael@0 | 196 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 197 | |
michael@0 | 198 | // Some plugins mess with our cursors and set a cursor that even |
michael@0 | 199 | // [NSCursor currentCursor] doesn't know about. In case that happens, just |
michael@0 | 200 | // reset the state. |
michael@0 | 201 | [[NSCursor currentCursor] set]; |
michael@0 | 202 | |
michael@0 | 203 | nsCursor oldType = [mCurrentMacCursor type]; |
michael@0 | 204 | if (oldType != aCursor) { |
michael@0 | 205 | if (aCursor == eCursor_none) { |
michael@0 | 206 | [NSCursor hide]; |
michael@0 | 207 | } else if (oldType == eCursor_none) { |
michael@0 | 208 | [NSCursor unhide]; |
michael@0 | 209 | } |
michael@0 | 210 | } |
michael@0 | 211 | [self setMacCursor:[self getCursor:aCursor]]; |
michael@0 | 212 | |
michael@0 | 213 | // if a custom cursor was previously set, release sCursorImgContainer |
michael@0 | 214 | if (oldType == sCustomCursor) { |
michael@0 | 215 | NS_IF_RELEASE(sCursorImgContainer); |
michael@0 | 216 | } |
michael@0 | 217 | return NS_OK; |
michael@0 | 218 | |
michael@0 | 219 | NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 220 | } |
michael@0 | 221 | |
michael@0 | 222 | - (nsresult) setMacCursor: (nsMacCursor*) aMacCursor |
michael@0 | 223 | { |
michael@0 | 224 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 225 | |
michael@0 | 226 | if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) { |
michael@0 | 227 | [aMacCursor retain]; |
michael@0 | 228 | [mCurrentMacCursor unset]; |
michael@0 | 229 | [aMacCursor set]; |
michael@0 | 230 | [mCurrentMacCursor release]; |
michael@0 | 231 | mCurrentMacCursor = aMacCursor; |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | return NS_OK; |
michael@0 | 235 | |
michael@0 | 236 | NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | - (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor |
michael@0 | 240 | { |
michael@0 | 241 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 242 | // As the user moves the mouse, this gets called repeatedly with the same aCursorImage |
michael@0 | 243 | if (sCursorImgContainer == aCursorImage && sCursorScaleFactor == scaleFactor && mCurrentMacCursor) { |
michael@0 | 244 | [self setMacCursor:mCurrentMacCursor]; |
michael@0 | 245 | return NS_OK; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | [[NSCursor currentCursor] set]; |
michael@0 | 249 | int32_t width = 0, height = 0; |
michael@0 | 250 | aCursorImage->GetWidth(&width); |
michael@0 | 251 | aCursorImage->GetHeight(&height); |
michael@0 | 252 | // prevent DoS attacks |
michael@0 | 253 | if (width > 128 || height > 128) { |
michael@0 | 254 | return NS_OK; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | NSImage *cursorImage; |
michael@0 | 258 | nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage, scaleFactor); |
michael@0 | 259 | if (NS_FAILED(rv) || !cursorImage) { |
michael@0 | 260 | return NS_ERROR_FAILURE; |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | // if the hotspot is nonsensical, make it 0,0 |
michael@0 | 264 | aHotspotX = (aHotspotX > (uint32_t)width - 1) ? 0 : aHotspotX; |
michael@0 | 265 | aHotspotY = (aHotspotY > (uint32_t)height - 1) ? 0 : aHotspotY; |
michael@0 | 266 | |
michael@0 | 267 | NSPoint hotSpot = ::NSMakePoint(aHotspotX, aHotspotY); |
michael@0 | 268 | [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage hotSpot:hotSpot] type:sCustomCursor]]; |
michael@0 | 269 | [cursorImage release]; |
michael@0 | 270 | |
michael@0 | 271 | NS_IF_RELEASE(sCursorImgContainer); |
michael@0 | 272 | sCursorImgContainer = aCursorImage; |
michael@0 | 273 | sCursorScaleFactor = scaleFactor; |
michael@0 | 274 | NS_ADDREF(sCursorImgContainer); |
michael@0 | 275 | |
michael@0 | 276 | return NS_OK; |
michael@0 | 277 | |
michael@0 | 278 | NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | - (nsMacCursor *) getCursor: (enum nsCursor) aCursor |
michael@0 | 282 | { |
michael@0 | 283 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
michael@0 | 284 | |
michael@0 | 285 | nsMacCursor * result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]]; |
michael@0 | 286 | if (!result) { |
michael@0 | 287 | result = [nsCursorManager createCursor:aCursor]; |
michael@0 | 288 | [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]]; |
michael@0 | 289 | } |
michael@0 | 290 | return result; |
michael@0 | 291 | |
michael@0 | 292 | NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | - (void) dealloc |
michael@0 | 296 | { |
michael@0 | 297 | NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
michael@0 | 298 | |
michael@0 | 299 | [mCurrentMacCursor unset]; |
michael@0 | 300 | [mCurrentMacCursor release]; |
michael@0 | 301 | [mCursors release]; |
michael@0 | 302 | NS_IF_RELEASE(sCursorImgContainer); |
michael@0 | 303 | [super dealloc]; |
michael@0 | 304 | |
michael@0 | 305 | NS_OBJC_END_TRY_ABORT_BLOCK; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | @end |