1/*
2 * Copyright (C) 2004, 2006, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "Cursor.h"
28
29#import "BlockExceptions.h"
30#import "WebCoreSystemInterface.h"
31#import <wtf/StdLibExtras.h>
32
33@interface WebCoreCursorBundle : NSObject { }
34@end
35
36@implementation WebCoreCursorBundle
37@end
38
39namespace WebCore {
40
41// Simple NSCursor calls shouldn't need protection,
42// but creating a cursor with a bad image might throw.
43
44static RetainPtr<NSCursor> createCustomCursor(Image* image, const IntPoint& hotSpot)
45{
46    // FIXME: The cursor won't animate.  Not sure if that's a big deal.
47    NSImage* nsImage = image->getNSImage();
48    if (!nsImage)
49        return 0;
50    BEGIN_BLOCK_OBJC_EXCEPTIONS;
51    return adoptNS([[NSCursor alloc] initWithImage:nsImage hotSpot:hotSpot]);
52    END_BLOCK_OBJC_EXCEPTIONS;
53    return 0;
54}
55
56static RetainPtr<NSCursor> createNamedCursor(const char* name, int x, int y)
57{
58    BEGIN_BLOCK_OBJC_EXCEPTIONS;
59    RetainPtr<NSString> resourceName = adoptNS([[NSString alloc] initWithUTF8String:name]);
60    RetainPtr<NSImage> cursorImage = adoptNS([[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreCursorBundle class]] pathForResource:resourceName.get() ofType:@"png"]]);
61
62    RetainPtr<NSCursor> cursor;
63
64    if (cursorImage)
65        cursor = adoptNS([[NSCursor alloc] initWithImage:cursorImage.get() hotSpot:NSMakePoint(x, y)]);
66
67    return cursor;
68    END_BLOCK_OBJC_EXCEPTIONS;
69    return nil;
70}
71
72void Cursor::ensurePlatformCursor() const
73{
74    if (m_platformCursor)
75        return;
76
77    switch (m_type) {
78    case Cursor::Pointer:
79        m_platformCursor = [NSCursor arrowCursor];
80        break;
81
82    case Cursor::Cross:
83        m_platformCursor = [NSCursor crosshairCursor];
84        break;
85
86    case Cursor::Hand:
87#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
88        m_platformCursor = [NSCursor pointingHandCursor];
89#else
90        // The pointingHandCursor from NSCursor does not have a shadow on
91        // older versions of OS X, so use our own custom cursor.
92        m_platformCursor = createNamedCursor("linkCursor", 6, 1);
93#endif
94        break;
95
96    case Cursor::IBeam:
97        m_platformCursor = [NSCursor IBeamCursor];
98        break;
99
100    case Cursor::Wait:
101#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
102        m_platformCursor = wkCursor("BusyButClickable");
103#else
104        m_platformCursor = createNamedCursor("waitCursor", 7, 7);
105#endif
106        break;
107
108    case Cursor::Help:
109#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
110        m_platformCursor = wkCursor("Help");
111        if (m_platformCursor)
112            break;
113#endif
114        m_platformCursor = createNamedCursor("helpCursor", 8, 8);
115        break;
116
117    case Cursor::Move:
118    case Cursor::MiddlePanning:
119#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
120        m_platformCursor = wkCursor("Move");
121#else
122        m_platformCursor = createNamedCursor("moveCursor", 7, 7);
123#endif
124        break;
125
126    case Cursor::EastResize:
127    case Cursor::EastPanning:
128#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
129        m_platformCursor = wkCursor("ResizeEast");
130#else
131        m_platformCursor = createNamedCursor("eastResizeCursor", 14, 7);
132#endif
133        break;
134
135    case Cursor::NorthResize:
136    case Cursor::NorthPanning:
137#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
138        m_platformCursor = wkCursor("ResizeNorth");
139#else
140        m_platformCursor = createNamedCursor("northResizeCursor", 7, 1);
141#endif
142        break;
143
144    case Cursor::NorthEastResize:
145    case Cursor::NorthEastPanning:
146#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
147        m_platformCursor = wkCursor("ResizeNortheast");
148#else
149        m_platformCursor = createNamedCursor("northEastResizeCursor", 14, 1);
150#endif
151        break;
152
153    case Cursor::NorthWestResize:
154    case Cursor::NorthWestPanning:
155#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
156        m_platformCursor = wkCursor("ResizeNorthwest");
157#else
158        m_platformCursor = createNamedCursor("northWestResizeCursor", 0, 0);
159#endif
160        break;
161
162    case Cursor::SouthResize:
163    case Cursor::SouthPanning:
164#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
165        m_platformCursor = wkCursor("ResizeSouth");
166#else
167        m_platformCursor = createNamedCursor("southResizeCursor", 7, 14);
168#endif
169        break;
170
171    case Cursor::SouthEastResize:
172    case Cursor::SouthEastPanning:
173#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
174        m_platformCursor = wkCursor("ResizeSoutheast");
175#else
176        m_platformCursor = createNamedCursor("southEastResizeCursor", 14, 14);
177#endif
178        break;
179
180    case Cursor::SouthWestResize:
181    case Cursor::SouthWestPanning:
182#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
183        m_platformCursor = wkCursor("ResizeSouthwest");
184#else
185        m_platformCursor = createNamedCursor("southWestResizeCursor", 1, 14);
186#endif
187        break;
188
189    case Cursor::WestResize:
190    case Cursor::WestPanning:
191#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
192        m_platformCursor = wkCursor("ResizeWest");
193#else
194        m_platformCursor = createNamedCursor("westResizeCursor", 1, 7);
195#endif
196        break;
197
198    case Cursor::NorthSouthResize:
199#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
200        m_platformCursor = wkCursor("ResizeNorthSouth");
201#else
202        m_platformCursor = createNamedCursor("northSouthResizeCursor", 7, 7);
203#endif
204        break;
205
206    case Cursor::EastWestResize:
207#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
208        m_platformCursor = wkCursor("ResizeEastWest");
209#else
210        m_platformCursor = createNamedCursor("eastWestResizeCursor", 7, 7);
211#endif
212        break;
213
214    case Cursor::NorthEastSouthWestResize:
215#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
216        m_platformCursor = wkCursor("ResizeNortheastSouthwest");
217#else
218        m_platformCursor = createNamedCursor("northEastSouthWestResizeCursor", 7, 7);
219#endif
220        break;
221
222    case Cursor::NorthWestSouthEastResize:
223#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
224        m_platformCursor = wkCursor("ResizeNorthwestSoutheast");
225#else
226        m_platformCursor = createNamedCursor("northWestSouthEastResizeCursor", 7, 7);
227#endif
228        break;
229
230    case Cursor::ColumnResize:
231        m_platformCursor = [NSCursor resizeLeftRightCursor];
232        break;
233
234    case Cursor::RowResize:
235        m_platformCursor = [NSCursor resizeUpDownCursor];
236        break;
237
238    case Cursor::VerticalText:
239#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
240        m_platformCursor = [NSCursor IBeamCursorForVerticalLayout];
241#else
242        m_platformCursor = createNamedCursor("verticalTextCursor", 7, 7);
243#endif
244        break;
245
246    case Cursor::Cell:
247#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
248        m_platformCursor = wkCursor("Cell");
249        if (m_platformCursor)
250            break;
251#endif
252        m_platformCursor = createNamedCursor("cellCursor", 7, 7);
253        break;
254
255    case Cursor::ContextMenu:
256        m_platformCursor = [NSCursor contextualMenuCursor];
257        break;
258
259    case Cursor::Alias:
260#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
261        m_platformCursor = wkCursor("MakeAlias");
262#else
263        m_platformCursor = createNamedCursor("aliasCursor", 11, 3);
264#endif
265        break;
266
267    case Cursor::Progress:
268#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
269        m_platformCursor = wkCursor("BusyButClickable");
270#else
271        m_platformCursor = createNamedCursor("progressCursor", 3, 2);
272#endif
273        break;
274
275    case Cursor::NoDrop:
276        m_platformCursor = [NSCursor operationNotAllowedCursor];
277        break;
278
279    case Cursor::Copy:
280        m_platformCursor = [NSCursor dragCopyCursor];
281        break;
282
283    case Cursor::None:
284        m_platformCursor = createNamedCursor("noneCursor", 7, 7);
285        break;
286
287    case Cursor::NotAllowed:
288        m_platformCursor = [NSCursor operationNotAllowedCursor];
289        break;
290
291    case Cursor::ZoomIn:
292#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
293        m_platformCursor = wkCursor("ZoomIn");
294        if (m_platformCursor)
295            break;
296#endif
297        m_platformCursor = createNamedCursor("zoomInCursor", 7, 7);
298        break;
299
300    case Cursor::ZoomOut:
301#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
302        m_platformCursor = wkCursor("ZoomOut");
303        if (m_platformCursor)
304            break;
305#endif
306        m_platformCursor = createNamedCursor("zoomOutCursor", 7, 7);
307        break;
308
309    case Cursor::Grab:
310        m_platformCursor = [NSCursor openHandCursor];
311        break;
312
313    case Cursor::Grabbing:
314        m_platformCursor = [NSCursor closedHandCursor];
315        break;
316
317    case Cursor::Custom:
318        m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot);
319        break;
320    }
321}
322
323Cursor::Cursor(const Cursor& other)
324    : m_type(other.m_type)
325    , m_image(other.m_image)
326    , m_hotSpot(other.m_hotSpot)
327    , m_imageScaleFactor(other.m_imageScaleFactor)
328    , m_platformCursor(other.m_platformCursor)
329{
330}
331
332Cursor& Cursor::operator=(const Cursor& other)
333{
334    m_type = other.m_type;
335    m_image = other.m_image;
336    m_hotSpot = other.m_hotSpot;
337    m_imageScaleFactor = other.m_imageScaleFactor;
338    m_platformCursor = other.m_platformCursor;
339    return *this;
340}
341
342Cursor::~Cursor()
343{
344}
345
346NSCursor *Cursor::platformCursor() const
347{
348    ensurePlatformCursor();
349    return m_platformCursor.get();
350}
351
352} // namespace WebCore
353