source: trunk/src/gui/kernel/qcursor_mac.mm@ 441

Last change on this file since 441 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 19.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qcursor_p.h>
43#include <private/qpixmap_mac_p.h>
44#include <qapplication.h>
45#include <qbitmap.h>
46#include <qcursor.h>
47#include <qevent.h>
48#include <string.h>
49#include <unistd.h>
50#include <AppKit/NSCursor.h>
51#include <qpainter.h>
52#include <private/qt_cocoa_helpers_mac_p.h>
53
54QT_BEGIN_NAMESPACE
55
56/*****************************************************************************
57 Externals
58 *****************************************************************************/
59extern QCursorData *qt_cursorTable[Qt::LastCursor + 1];
60extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
61extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp
62extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp
63
64/*****************************************************************************
65 Internal QCursorData class
66 *****************************************************************************/
67
68class QMacAnimateCursor : public QObject
69{
70 int timerId, step;
71 ThemeCursor curs;
72public:
73 QMacAnimateCursor() : QObject(), timerId(-1) { }
74 void start(ThemeCursor c) {
75 step = 1;
76 if(timerId != -1)
77 killTimer(timerId);
78 timerId = startTimer(300);
79 curs = c;
80 }
81 void stop() {
82 if(timerId != -1) {
83 killTimer(timerId);
84 timerId = -1;
85 }
86 }
87protected:
88 void timerEvent(QTimerEvent *e) {
89 if(e->timerId() == timerId) {
90 /*
91 if(SetAnimatedThemeCursor(curs, step++) == themeBadCursorIndexErr)
92 stop();
93 */
94 }
95 }
96};
97
98static QCursorData *currentCursor = 0; //current cursor
99void qt_mac_set_cursor(const QCursor *c, const QPoint &)
100{
101 if (!c) {
102 currentCursor = 0;
103 return;
104 }
105 c->handle(); //force the cursor to get loaded, if it's not
106
107 if(1 || currentCursor != c->d) {
108 if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor
109 && currentCursor->curs.tc.anim)
110 currentCursor->curs.tc.anim->stop();
111 QMacCocoaAutoReleasePool pool;
112 if(c->d->type == QCursorData::TYPE_ImageCursor) {
113 [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set];
114 } else if(c->d->type == QCursorData::TYPE_ThemeCursor) {
115#ifdef QT_MAC_USE_COCOA
116 if (c->d->curs.cp.nscursor == 0)
117 [[NSCursor arrowCursor] set];
118 [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set];
119#else
120 if(SetAnimatedThemeCursor(c->d->curs.tc.curs, 0) == themeBadCursorIndexErr) {
121 SetThemeCursor(c->d->curs.tc.curs);
122 } else {
123 if(!c->d->curs.tc.anim)
124 c->d->curs.tc.anim = new QMacAnimateCursor;
125 c->d->curs.tc.anim->start(c->d->curs.tc.curs);
126 }
127#endif
128 }
129 }
130 currentCursor = c->d;
131}
132
133void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos)
134{
135 QCursor cursor(Qt::ArrowCursor);
136 if (QApplication::overrideCursor()) {
137 cursor = *QApplication::overrideCursor();
138 } else {
139 for(QWidget *w = QApplication::widgetAt(globalPos); w; w = w->parentWidget()) {
140 if(w->testAttribute(Qt::WA_SetCursor)) {
141 cursor = w->cursor();
142 break;
143 }
144 }
145 }
146 qt_mac_set_cursor(&cursor, globalPos);
147}
148
149void qt_mac_update_cursor()
150{
151 qt_mac_update_cursor_at_global_pos(QCursor::pos());
152}
153
154static int nextCursorId = Qt::BitmapCursor;
155
156QCursorData::QCursorData(Qt::CursorShape s)
157 : cshape(s), bm(0), bmm(0), hx(-1), hy(-1), mId(s), type(TYPE_None)
158{
159 ref = 1;
160 memset(&curs, '\0', sizeof(curs));
161}
162
163QCursorData::~QCursorData()
164{
165 if (type == TYPE_ImageCursor) {
166 if (curs.cp.my_cursor) {
167 QMacCocoaAutoReleasePool pool;
168 [static_cast<NSCursor *>(curs.cp.nscursor) release];
169 }
170 } else if(type == TYPE_ThemeCursor) {
171 delete curs.tc.anim;
172 }
173 type = TYPE_None;
174
175 delete bm;
176 delete bmm;
177 if(currentCursor == this)
178 currentCursor = 0;
179}
180
181QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
182{
183 if (!QCursorData::initialized)
184 QCursorData::initialize();
185 if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
186 qWarning("Qt: QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
187 QCursorData *c = qt_cursorTable[0];
188 c->ref.ref();
189 return c;
190 }
191 // This is silly, but this is apparently called outside the constructor, so we have
192 // to be ready for that case.
193 QCursorData *x = new QCursorData;
194 x->ref = 1;
195 x->mId = ++nextCursorId;
196 x->bm = new QBitmap(bitmap);
197 x->bmm = new QBitmap(mask);
198 x->cshape = Qt::BitmapCursor;
199 x->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
200 x->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
201 return x;
202}
203
204Qt::HANDLE QCursor::handle() const
205{
206 if(!QCursorData::initialized)
207 QCursorData::initialize();
208 if(d->type == QCursorData::TYPE_None)
209 d->update();
210 return (Qt::HANDLE)d->mId;
211}
212
213QPoint QCursor::pos()
214{
215 return flipPoint([NSEvent mouseLocation]).toPoint();
216}
217
218void QCursor::setPos(int x, int y)
219{
220 CGWarpMouseCursorPosition(CGPointMake(x, y));
221
222 /* I'm not too keen on doing this, but this makes it a lot easier, so I just
223 send the event back through the event system and let it get propagated correctly
224 ideally this would not really need to be faked --Sam
225 */
226 QWidget *widget = 0;
227 if(QWidget *grb = QWidget::mouseGrabber())
228 widget = grb;
229 else
230 widget = QApplication::widgetAt(QPoint(x, y));
231 if(widget) {
232 QMouseEvent me(QMouseEvent::MouseMove, widget->mapFromGlobal(QPoint(x, y)), Qt::NoButton,
233 QApplication::mouseButtons(), QApplication::keyboardModifiers());
234 qt_sendSpontaneousEvent(widget, &me);
235 }
236}
237
238void QCursorData::initCursorFromBitmap()
239{
240 NSImage *nsimage;
241 QImage finalCursor(bm->size(), QImage::Format_ARGB32);
242 QImage bmi = bm->toImage().convertToFormat(QImage::Format_RGB32);
243 QImage bmmi = bmm->toImage().convertToFormat(QImage::Format_RGB32);
244 for (int row = 0; row < finalCursor.height(); ++row) {
245 QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row));
246 QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row));
247 QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row));
248 for (int col = 0; col < finalCursor.width(); ++col) {
249 if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
250 finalData[col] = 0xffffffff;
251 } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
252 finalData[col] = 0x7f000000;
253 } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) {
254 finalData[col] = 0x00000000;
255 } else {
256 finalData[col] = 0xff000000;
257 }
258 }
259 }
260 type = QCursorData::TYPE_ImageCursor;
261 curs.cp.my_cursor = true;
262 QPixmap bmCopy = QPixmap::fromImage(finalCursor);
263 NSPoint hotSpot = { hx, hy };
264 nsimage = static_cast<NSImage*>(qt_mac_create_nsimage(bmCopy));
265 curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
266 [nsimage release];
267}
268
269void QCursorData::initCursorFromPixmap()
270{
271 type = QCursorData::TYPE_ImageCursor;
272 curs.cp.my_cursor = true;
273 NSPoint hotSpot = { hx, hy };
274 NSImage *nsimage;
275 nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
276 curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
277 [nsimage release];
278}
279
280void QCursorData::update()
281{
282 if(!QCursorData::initialized)
283 QCursorData::initialize();
284 if(type != QCursorData::TYPE_None)
285 return;
286
287 /* Note to self... ***
288 * mask x data
289 * 0xFF x 0x00 == fully opaque white
290 * 0x00 x 0xFF == xor'd black
291 * 0xFF x 0xFF == fully opaque black
292 * 0x00 x 0x00 == fully transparent
293 */
294
295 if (hx < 0)
296 hx = 0;
297 if (hy < 0)
298 hy = 0;
299
300#define QT_USE_APPROXIMATE_CURSORS
301#ifdef QT_USE_APPROXIMATE_CURSORS
302 static const uchar cur_ver_bits[] = {
303 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
304 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0,
305 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 };
306 static const uchar mcur_ver_bits[] = {
307 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8,
308 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8,
309 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 };
310
311 static const uchar cur_hor_bits[] = {
312 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30,
313 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20,
314 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
315 static const uchar mcur_hor_bits[] = {
316 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78,
317 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78,
318 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 };
319
320 static const uchar cur_fdiag_bits[] = {
321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78,
322 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00,
323 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
324 static const uchar mcur_fdiag_bits[] = {
325 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc,
326 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00,
327 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 };
328
329 static const uchar cur_bdiag_bits[] = {
330 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00,
331 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8,
332 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
333 static const uchar mcur_bdiag_bits[] = {
334 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04,
335 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc,
336 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 };
337
338 static const unsigned char cur_up_arrow_bits[] = {
339 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10,
340 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
341 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 };
342 static const unsigned char mcur_up_arrow_bits[] = {
343 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0,
344 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
345 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 };
346#endif
347 const uchar *cursorData = 0;
348 const uchar *cursorMaskData = 0;
349#ifdef QT_MAC_USE_COCOA
350 switch (cshape) { // map Q cursor to MAC cursor
351 case Qt::BitmapCursor: {
352 if (pixmap.isNull())
353 initCursorFromBitmap();
354 else
355 initCursorFromPixmap();
356 break; }
357 case Qt::BlankCursor: {
358 pixmap = QPixmap(16, 16);
359 pixmap.fill(Qt::transparent);
360 initCursorFromPixmap();
361 break; }
362 case Qt::ArrowCursor: {
363 type = QCursorData::TYPE_ThemeCursor;
364 curs.cp.nscursor = [NSCursor arrowCursor];
365 break; }
366 case Qt::CrossCursor: {
367 type = QCursorData::TYPE_ThemeCursor;
368 curs.cp.nscursor = [NSCursor crosshairCursor];
369 break; }
370 case Qt::WaitCursor: {
371 pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/spincursor.png"));
372 initCursorFromPixmap();
373 break; }
374 case Qt::IBeamCursor: {
375 type = QCursorData::TYPE_ThemeCursor;
376 curs.cp.nscursor = [NSCursor IBeamCursor];
377 break; }
378 case Qt::SizeAllCursor: {
379 pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/pluscursor.png"));
380 initCursorFromPixmap();
381 break; }
382 case Qt::WhatsThisCursor: { //for now just use the pointing hand