OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/common/cursors/webcursor.h" | |
6 | |
7 #import <AppKit/AppKit.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/mac/mac_util.h" | |
11 #include "base/mac/scoped_cftyperef.h" | |
12 #include "grit/webkit_resources.h" | |
13 #include "skia/ext/skia_utils_mac.h" | |
14 #include "third_party/WebKit/public/platform/WebCursorInfo.h" | |
15 #include "third_party/WebKit/public/platform/WebSize.h" | |
16 #include "ui/base/resource/resource_bundle.h" | |
17 #include "ui/gfx/point_conversions.h" | |
18 #include "ui/gfx/size_conversions.h" | |
19 | |
20 | |
21 using blink::WebCursorInfo; | |
22 using blink::WebSize; | |
23 | |
24 // Declare symbols that are part of the 10.7 SDK. | |
25 #if !defined(MAC_OS_X_VERSION_10_7) || \ | |
26 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 | |
27 | |
28 @interface NSCursor (LionSDKDeclarations) | |
29 + (NSCursor*)IBeamCursorForVerticalLayout; | |
30 @end | |
31 | |
32 #endif // MAC_OS_X_VERSION_10_7 | |
33 | |
34 // Private interface to CoreCursor, as of Mac OS X 10.7. This is essentially the | |
35 // implementation of WKCursor in WebKitSystemInterface. | |
36 | |
37 enum { | |
38 kArrowCursor = 0, | |
39 kIBeamCursor = 1, | |
40 kMakeAliasCursor = 2, | |
41 kOperationNotAllowedCursor = 3, | |
42 kBusyButClickableCursor = 4, | |
43 kCopyCursor = 5, | |
44 kClosedHandCursor = 11, | |
45 kOpenHandCursor = 12, | |
46 kPointingHandCursor = 13, | |
47 kCountingUpHandCursor = 14, | |
48 kCountingDownHandCursor = 15, | |
49 kCountingUpAndDownHandCursor = 16, | |
50 kResizeLeftCursor = 17, | |
51 kResizeRightCursor = 18, | |
52 kResizeLeftRightCursor = 19, | |
53 kCrosshairCursor = 20, | |
54 kResizeUpCursor = 21, | |
55 kResizeDownCursor = 22, | |
56 kResizeUpDownCursor = 23, | |
57 kContextualMenuCursor = 24, | |
58 kDisappearingItemCursor = 25, | |
59 kVerticalIBeamCursor = 26, | |
60 kResizeEastCursor = 27, | |
61 kResizeEastWestCursor = 28, | |
62 kResizeNortheastCursor = 29, | |
63 kResizeNortheastSouthwestCursor = 30, | |
64 kResizeNorthCursor = 31, | |
65 kResizeNorthSouthCursor = 32, | |
66 kResizeNorthwestCursor = 33, | |
67 kResizeNorthwestSoutheastCursor = 34, | |
68 kResizeSoutheastCursor = 35, | |
69 kResizeSouthCursor = 36, | |
70 kResizeSouthwestCursor = 37, | |
71 kResizeWestCursor = 38, | |
72 kMoveCursor = 39, | |
73 kHelpCursor = 40, // Present on >= 10.7.3. | |
74 kCellCursor = 41, // Present on >= 10.7.3. | |
75 kZoomInCursor = 42, // Present on >= 10.7.3. | |
76 kZoomOutCursor = 43 // Present on >= 10.7.3. | |
77 }; | |
78 typedef long long CrCoreCursorType; | |
79 | |
80 @interface CrCoreCursor : NSCursor { | |
81 @private | |
82 CrCoreCursorType type_; | |
83 } | |
84 | |
85 + (id)cursorWithType:(CrCoreCursorType)type; | |
86 - (id)initWithType:(CrCoreCursorType)type; | |
87 - (CrCoreCursorType)_coreCursorType; | |
88 | |
89 @end | |
90 | |
91 @implementation CrCoreCursor | |
92 | |
93 + (id)cursorWithType:(CrCoreCursorType)type { | |
94 NSCursor* cursor = [[CrCoreCursor alloc] initWithType:type]; | |
95 if ([cursor image]) | |
96 return [cursor autorelease]; | |
97 | |
98 [cursor release]; | |
99 return nil; | |
100 } | |
101 | |
102 - (id)initWithType:(CrCoreCursorType)type { | |
103 if ((self = [super init])) { | |
104 type_ = type; | |
105 } | |
106 return self; | |
107 } | |
108 | |
109 - (CrCoreCursorType)_coreCursorType { | |
110 return type_; | |
111 } | |
112 | |
113 @end | |
114 | |
115 namespace { | |
116 | |
117 NSCursor* LoadCursor(int resource_id, int hotspot_x, int hotspot_y) { | |
118 const gfx::Image& cursor_image = | |
119 ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); | |
120 DCHECK(!cursor_image.IsEmpty()); | |
121 return [[[NSCursor alloc] initWithImage:cursor_image.ToNSImage() | |
122 hotSpot:NSMakePoint(hotspot_x, | |
123 hotspot_y)] autorelease]; | |
124 } | |
125 | |
126 // Gets a specified cursor from CoreCursor, falling back to loading it from the | |
127 // image cache if CoreCursor cannot provide it. | |
128 NSCursor* GetCoreCursorWithFallback(CrCoreCursorType type, | |
129 int resource_id, | |
130 int hotspot_x, | |
131 int hotspot_y) { | |
132 if (base::mac::IsOSLionOrLater()) { | |
133 NSCursor* cursor = [CrCoreCursor cursorWithType:type]; | |
134 if (cursor) | |
135 return cursor; | |
136 } | |
137 | |
138 return LoadCursor(resource_id, hotspot_x, hotspot_y); | |
139 } | |
140 | |
141 NSCursor* CreateCustomCursor(const std::vector<char>& custom_data, | |
142 const gfx::Size& custom_size, | |
143 float custom_scale, | |
144 const gfx::Point& hotspot) { | |
145 // If the data is missing, leave the backing transparent. | |
146 void* data = NULL; | |
147 size_t data_size = 0; | |
148 if (!custom_data.empty()) { | |
149 // This is safe since we're not going to draw into the context we're | |
150 // creating. | |
151 data = const_cast<char*>(&custom_data[0]); | |
152 data_size = custom_data.size(); | |
153 } | |
154 | |
155 // If the size is empty, use a 1x1 transparent image. | |
156 gfx::Size size = custom_size; | |
157 if (size.IsEmpty()) { | |
158 size.SetSize(1, 1); | |
159 data = NULL; | |
160 } | |
161 | |
162 SkBitmap bitmap; | |
163 bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); | |
164 bitmap.allocPixels(); | |
165 if (data) | |
166 memcpy(bitmap.getAddr32(0, 0), data, data_size); | |
167 else | |
168 bitmap.eraseARGB(0, 0, 0, 0); | |
169 | |
170 // Convert from pixels to view units. | |
171 if (custom_scale == 0) | |
172 custom_scale = 1; | |
173 NSSize dip_size = NSSizeFromCGSize(gfx::ToFlooredSize( | |
174 gfx::ScaleSize(custom_size, 1 / custom_scale)).ToCGSize()); | |
175 NSPoint dip_hotspot = NSPointFromCGPoint(gfx::ToFlooredPoint( | |
176 gfx::ScalePoint(hotspot, 1 / custom_scale)).ToCGPoint()); | |
177 | |
178 // Both the image and its representation need to have the same size for | |
179 // cursors to appear in high resolution on retina displays. Note that the | |
180 // size of a representation is not the same as pixelsWide or pixelsHigh. | |
181 NSImage* cursor_image = gfx::SkBitmapToNSImage(bitmap); | |
182 [cursor_image setSize:dip_size]; | |
183 [[[cursor_image representations] objectAtIndex:0] setSize:dip_size]; | |
184 | |
185 NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image | |
186 hotSpot:dip_hotspot]; | |
187 | |
188 return [cursor autorelease]; | |
189 } | |
190 | |
191 } // namespace | |
192 | |
193 // Match Safari's cursor choices; see platform/mac/CursorMac.mm . | |
194 gfx::NativeCursor WebCursor::GetNativeCursor() { | |
195 switch (type_) { | |
196 case WebCursorInfo::TypePointer: | |
197 return [NSCursor arrowCursor]; | |
198 case WebCursorInfo::TypeCross: | |
199 return [NSCursor crosshairCursor]; | |
200 case WebCursorInfo::TypeHand: | |
201 // If >= 10.7, the pointingHandCursor has a shadow so use it. Otherwise | |
202 // use the custom one. | |
203 if (base::mac::IsOSLionOrLater()) | |
204 return [NSCursor pointingHandCursor]; | |
205 else | |
206 return LoadCursor(IDR_LINK_CURSOR, 6, 1); | |
207 case WebCursorInfo::TypeIBeam: | |
208 return [NSCursor IBeamCursor]; | |
209 case WebCursorInfo::TypeWait: | |
210 return GetCoreCursorWithFallback(kBusyButClickableCursor, | |
211 IDR_WAIT_CURSOR, 7, 7); | |
212 case WebCursorInfo::TypeHelp: | |
213 return GetCoreCursorWithFallback(kHelpCursor, | |
214 IDR_HELP_CURSOR, 8, 8); | |
215 case WebCursorInfo::TypeEastResize: | |
216 case WebCursorInfo::TypeEastPanning: | |
217 return GetCoreCursorWithFallback(kResizeEastCursor, | |
218 IDR_EAST_RESIZE_CURSOR, 14, 7); | |
219 case WebCursorInfo::TypeNorthResize: | |
220 case WebCursorInfo::TypeNorthPanning: | |
221 return GetCoreCursorWithFallback(kResizeNorthCursor, | |
222 IDR_NORTH_RESIZE_CURSOR, 7, 1); | |
223 case WebCursorInfo::TypeNorthEastResize: | |
224 case WebCursorInfo::TypeNorthEastPanning: | |
225 return GetCoreCursorWithFallback(kResizeNortheastCursor, | |
226 IDR_NORTHEAST_RESIZE_CURSOR, 14, 1); | |
227 case WebCursorInfo::TypeNorthWestResize: | |
228 case WebCursorInfo::TypeNorthWestPanning: | |
229 return GetCoreCursorWithFallback(kResizeNorthwestCursor, | |
230 IDR_NORTHWEST_RESIZE_CURSOR, 0, 0); | |
231 case WebCursorInfo::TypeSouthResize: | |
232 case WebCursorInfo::TypeSouthPanning: | |
233 return GetCoreCursorWithFallback(kResizeSouthCursor, | |
234 IDR_SOUTH_RESIZE_CURSOR, 7, 14); | |
235 case WebCursorInfo::TypeSouthEastResize: | |
236 case WebCursorInfo::TypeSouthEastPanning: | |
237 return GetCoreCursorWithFallback(kResizeSoutheastCursor, | |
238 IDR_SOUTHEAST_RESIZE_CURSOR, 14, 14); | |
239 case WebCursorInfo::TypeSouthWestResize: | |
240 case WebCursorInfo::TypeSouthWestPanning: | |
241 return GetCoreCursorWithFallback(kResizeSouthwestCursor, | |
242 IDR_SOUTHWEST_RESIZE_CURSOR, 1, 14); | |
243 case WebCursorInfo::TypeWestResize: | |
244 case WebCursorInfo::TypeWestPanning: | |
245 return GetCoreCursorWithFallback(kResizeWestCursor, | |
246 IDR_WEST_RESIZE_CURSOR, 1, 7); | |
247 case WebCursorInfo::TypeNorthSouthResize: | |
248 return GetCoreCursorWithFallback(kResizeNorthSouthCursor, | |
249 IDR_NORTHSOUTH_RESIZE_CURSOR, 7, 7); | |
250 case WebCursorInfo::TypeEastWestResize: | |
251 return GetCoreCursorWithFallback(kResizeEastWestCursor, | |
252 IDR_EASTWEST_RESIZE_CURSOR, 7, 7); | |
253 case WebCursorInfo::TypeNorthEastSouthWestResize: | |
254 return GetCoreCursorWithFallback(kResizeNortheastSouthwestCursor, | |
255 IDR_NORTHEASTSOUTHWEST_RESIZE_CURSOR, | |
256 7, 7); | |
257 case WebCursorInfo::TypeNorthWestSouthEastResize: | |
258 return GetCoreCursorWithFallback(kResizeNorthwestSoutheastCursor, | |
259 IDR_NORTHWESTSOUTHEAST_RESIZE_CURSOR, | |
260 7, 7); | |
261 case WebCursorInfo::TypeColumnResize: | |
262 return [NSCursor resizeLeftRightCursor]; | |
263 case WebCursorInfo::TypeRowResize: | |
264 return [NSCursor resizeUpDownCursor]; | |
265 case WebCursorInfo::TypeMiddlePanning: | |
266 case WebCursorInfo::TypeMove: | |
267 return GetCoreCursorWithFallback(kMoveCursor, | |
268 IDR_MOVE_CURSOR, 7, 7); | |
269 case WebCursorInfo::TypeVerticalText: | |
270 // IBeamCursorForVerticalLayout is >= 10.7. | |
271 if ([NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)]) | |
272 return [NSCursor IBeamCursorForVerticalLayout]; | |
273 else | |
274 return LoadCursor(IDR_VERTICALTEXT_CURSOR, 7, 7); | |
275 case WebCursorInfo::TypeCell: | |
276 return GetCoreCursorWithFallback(kCellCursor, | |
277 IDR_CELL_CURSOR, 7, 7); | |
278 case WebCursorInfo::TypeContextMenu: | |
279 return [NSCursor contextualMenuCursor]; | |
280 case WebCursorInfo::TypeAlias: | |
281 return GetCoreCursorWithFallback(kMakeAliasCursor, | |
282 IDR_ALIAS_CURSOR, 11, 3); | |
283 case WebCursorInfo::TypeProgress: | |
284 return GetCoreCursorWithFallback(kBusyButClickableCursor, | |
285 IDR_PROGRESS_CURSOR, 3, 2); | |
286 case WebCursorInfo::TypeNoDrop: | |
287 case WebCursorInfo::TypeNotAllowed: | |
288 return [NSCursor operationNotAllowedCursor]; | |
289 case WebCursorInfo::TypeCopy: | |
290 return [NSCursor dragCopyCursor]; | |
291 case WebCursorInfo::TypeNone: | |
292 return LoadCursor(IDR_NONE_CURSOR, 7, 7); | |
293 case WebCursorInfo::TypeZoomIn: | |
294 return GetCoreCursorWithFallback(kZoomInCursor, | |
295 IDR_ZOOMIN_CURSOR, 7, 7); | |
296 case WebCursorInfo::TypeZoomOut: | |
297 return GetCoreCursorWithFallback(kZoomOutCursor, | |
298 IDR_ZOOMOUT_CURSOR, 7, 7); | |
299 case WebCursorInfo::TypeGrab: | |
300 return [NSCursor openHandCursor]; | |
301 case WebCursorInfo::TypeGrabbing: | |
302 return [NSCursor closedHandCursor]; | |
303 case WebCursorInfo::TypeCustom: | |
304 return CreateCustomCursor( | |
305 custom_data_, custom_size_, custom_scale_, hotspot_); | |
306 } | |
307 NOTREACHED(); | |
308 return nil; | |
309 } | |
310 | |
311 void WebCursor::InitFromNSCursor(NSCursor* cursor) { | |
312 CursorInfo cursor_info; | |
313 | |
314 if ([cursor isEqual:[NSCursor arrowCursor]]) { | |
315 cursor_info.type = WebCursorInfo::TypePointer; | |
316 } else if ([cursor isEqual:[NSCursor IBeamCursor]]) { | |
317 cursor_info.type = WebCursorInfo::TypeIBeam; | |
318 } else if ([cursor isEqual:[NSCursor crosshairCursor]]) { | |
319 cursor_info.type = WebCursorInfo::TypeCross; | |
320 } else if ([cursor isEqual:[NSCursor pointingHandCursor]]) { | |
321 cursor_info.type = WebCursorInfo::TypeHand; | |
322 } else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) { | |
323 cursor_info.type = WebCursorInfo::TypeWestResize; | |
324 } else if ([cursor isEqual:[NSCursor resizeRightCursor]]) { | |
325 cursor_info.type = WebCursorInfo::TypeEastResize; | |
326 } else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) { | |
327 cursor_info.type = WebCursorInfo::TypeEastWestResize; | |
328 } else if ([cursor isEqual:[NSCursor resizeUpCursor]]) { | |
329 cursor_info.type = WebCursorInfo::TypeNorthResize; | |
330 } else if ([cursor isEqual:[NSCursor resizeDownCursor]]) { | |
331 cursor_info.type = WebCursorInfo::TypeSouthResize; | |
332 } else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) { | |
333 cursor_info.type = WebCursorInfo::TypeNorthSouthResize; | |
334 } else if ([cursor isEqual:[NSCursor openHandCursor]]) { | |
335 cursor_info.type = WebCursorInfo::TypeGrab; | |
336 } else if ([cursor isEqual:[NSCursor closedHandCursor]]) { | |
337 cursor_info.type = WebCursorInfo::TypeGrabbing; | |
338 } else if ([cursor isEqual:[NSCursor operationNotAllowedCursor]]) { | |
339 cursor_info.type = WebCursorInfo::TypeNotAllowed; | |
340 } else if ([cursor isEqual:[NSCursor dragCopyCursor]]) { | |
341 cursor_info.type = WebCursorInfo::TypeCopy; | |
342 } else if ([cursor isEqual:[NSCursor contextualMenuCursor]]) { | |
343 cursor_info.type = WebCursorInfo::TypeContextMenu; | |
344 } else if ( | |
345 [NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] && | |
346 [cursor isEqual:[NSCursor IBeamCursorForVerticalLayout]]) { | |
347 cursor_info.type = WebCursorInfo::TypeVerticalText; | |
348 } else { | |
349 // Also handles the [NSCursor disappearingItemCursor] case. Quick-and-dirty | |
350 // image conversion; TODO(avi): do better. | |
351 CGImageRef cg_image = nil; | |
352 NSImage* image = [cursor image]; | |
353 for (id rep in [image representations]) { | |
354 if ([rep isKindOfClass:[NSBitmapImageRep class]]) { | |
355 cg_image = [rep CGImage]; | |
356 break; | |
357 } | |
358 } | |
359 | |
360 if (cg_image) { | |
361 cursor_info.type = WebCursorInfo::TypeCustom; | |
362 NSPoint hot_spot = [cursor hotSpot]; | |
363 cursor_info.hotspot = gfx::Point(hot_spot.x, hot_spot.y); | |
364 cursor_info.custom_image = gfx::CGImageToSkBitmap(cg_image); | |
365 } else { | |
366 cursor_info.type = WebCursorInfo::TypePointer; | |
367 } | |
368 } | |
369 | |
370 InitFromCursorInfo(cursor_info); | |
371 } | |
372 | |
373 void WebCursor::InitPlatformData() { | |
374 return; | |
375 } | |
376 | |
377 bool WebCursor::SerializePlatformData(Pickle* pickle) const { | |
378 return true; | |
379 } | |
380 | |
381 bool WebCursor::DeserializePlatformData(PickleIterator* iter) { | |
382 return true; | |
383 } | |
384 | |
385 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const { | |
386 return true; | |
387 } | |
388 | |
389 void WebCursor::CleanupPlatformData() { | |
390 return; | |
391 } | |
392 | |
393 void WebCursor::CopyPlatformData(const WebCursor& other) { | |
394 return; | |
395 } | |
OLD | NEW |