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