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