~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/plugins/platforms/cocoa/qnsview.mm

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the plugins of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include <QtCore/qglobal.h>
 
43
 
 
44
#include <Carbon/Carbon.h>
 
45
 
 
46
#include "qnsview.h"
 
47
#include "qcocoawindow.h"
 
48
#include "qcocoahelpers.h"
 
49
#include "qcocoaautoreleasepool.h"
 
50
#include "qmultitouch_mac_p.h"
 
51
#include "qcocoadrag.h"
 
52
#include <qpa/qplatformintegration.h>
 
53
 
 
54
#include <qpa/qwindowsysteminterface.h>
 
55
#include <QtGui/QTextFormat>
 
56
#include <QtCore/QDebug>
 
57
#include <private/qguiapplication_p.h>
 
58
#include "qcocoabackingstore.h"
 
59
#include "qcocoaglcontext.h"
 
60
 
 
61
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
 
62
#include <accessibilityinspector.h>
 
63
#endif
 
64
 
 
65
static QTouchDevice *touchDevice = 0;
 
66
 
 
67
@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
 
68
  - (CGFloat)deviceDeltaX;
 
69
  - (CGFloat)deviceDeltaY;
 
70
  - (CGFloat)deviceDeltaZ;
 
71
@end
 
72
 
 
73
@implementation QNSView
 
74
 
 
75
- (id) init
 
76
{
 
77
    self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
 
78
    if (self) {
 
79
        m_backingStore = 0;
 
80
        m_maskImage = 0;
 
81
        m_maskData = 0;
 
82
        m_window = 0;
 
83
        m_buttons = Qt::NoButton;
 
84
        m_sendKeyEvent = false;
 
85
        m_subscribesForGlobalFrameNotifications = false;
 
86
        currentCustomDragTypes = 0;
 
87
        m_sendUpAsRightButton = false;
 
88
 
 
89
        if (!touchDevice) {
 
90
            touchDevice = new QTouchDevice;
 
91
            touchDevice->setType(QTouchDevice::TouchPad);
 
92
            touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
 
93
            QWindowSystemInterface::registerTouchDevice(touchDevice);
 
94
        }
 
95
    }
 
96
    return self;
 
97
}
 
98
 
 
99
- (void)dealloc
 
100
{
 
101
    CGImageRelease(m_maskImage);
 
102
    m_maskImage = 0;
 
103
    delete[] m_maskData;
 
104
    m_maskData = 0;
 
105
    m_window = 0;
 
106
    if (m_subscribesForGlobalFrameNotifications) {
 
107
        m_subscribesForGlobalFrameNotifications = false;
 
108
        [[NSNotificationCenter defaultCenter] removeObserver:self
 
109
             name:NSViewGlobalFrameDidChangeNotification
 
110
             object:self];
 
111
}
 
112
    [super dealloc];
 
113
}
 
114
 
 
115
- (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
 
116
{
 
117
    self = [self init];
 
118
    if (!self)
 
119
        return 0;
 
120
 
 
121
    m_window = window;
 
122
    m_platformWindow = platformWindow;
 
123
    m_accessibleRoot = 0;
 
124
    m_sendKeyEvent = false;
 
125
 
 
126
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
 
127
    // prevent rift in space-time continuum, disable
 
128
    // accessibility for the accessibility inspector's windows.
 
129
    static bool skipAccessibilityForInspectorWindows = false;
 
130
    if (!skipAccessibilityForInspectorWindows) {
 
131
 
 
132
        m_accessibleRoot = window->accessibleRoot();
 
133
 
 
134
        AccessibilityInspector *inspector = new AccessibilityInspector(window);
 
135
        skipAccessibilityForInspectorWindows = true;
 
136
        inspector->inspectWindow(window);
 
137
        skipAccessibilityForInspectorWindows = false;
 
138
    }
 
139
#else
 
140
    m_accessibleRoot = window->accessibleRoot();
 
141
#endif
 
142
 
 
143
    [self registerDragTypes];
 
144
    [self setPostsFrameChangedNotifications : YES];
 
145
    [[NSNotificationCenter defaultCenter] addObserver:self
 
146
                                          selector:@selector(updateGeometry)
 
147
                                          name:NSViewFrameDidChangeNotification
 
148
                                          object:self];
 
149
 
 
150
    return self;
 
151
}
 
152
 
 
153
- (void) setQCocoaGLContext:(QCocoaGLContext *)context
 
154
{
 
155
    [context->nsOpenGLContext() setView:self];
 
156
    if (!m_subscribesForGlobalFrameNotifications) {
 
157
        // NSOpenGLContext expects us to repaint (or update) the view when
 
158
        // it changes position on screen. Since this happens unnoticed for
 
159
        // the view when the parent view moves, we need to register a special
 
160
        // notification that lets us handle this case:
 
161
        m_subscribesForGlobalFrameNotifications = true;
 
162
        [[NSNotificationCenter defaultCenter] addObserver:self
 
163
            selector:@selector(globalFrameChanged:)
 
164
            name:NSViewGlobalFrameDidChangeNotification
 
165
            object:self];
 
166
    }
 
167
}
 
168
 
 
169
- (void) globalFrameChanged:(NSNotification*)notification
 
170
{
 
171
    Q_UNUSED(notification);
 
172
    QWindowSystemInterface::handleExposeEvent(m_window, m_window->geometry());
 
173
}
 
174
 
 
175
- (void)updateGeometry
 
176
{
 
177
    QRect geometry;
 
178
    if (m_platformWindow->m_nsWindow) {
 
179
        // top level window, get window rect and flip y.
 
180
        NSRect rect = [self frame];
 
181
        NSRect windowRect = [[self window] frame];
 
182
        geometry = QRect(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height);
 
183
    } else {
 
184
        // child window, use the frame rect
 
185
        geometry = qt_mac_toQRect([self frame]);
 
186
    }
 
187
 
 
188
    if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry())
 
189
        return;
 
190
 
 
191
#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
 
192
    qDebug() << "QNSView::udpateGeometry" << m_platformWindow << geometry;
 
193
#endif
 
194
 
 
195
    // Call setGeometry on QPlatformWindow. (not on QCocoaWindow,
 
196
    // doing that will initiate a geometry change it and possibly create
 
197
    // an infinite loop when this notification is triggered again.)
 
198
    m_platformWindow->QPlatformWindow::setGeometry(geometry);
 
199
 
 
200
    // Send a geometry change event to Qt, if it's ready to handle events
 
201
    if (!m_platformWindow->m_inConstructor) {
 
202
        QWindowSystemInterface::handleGeometryChange(m_window, geometry);
 
203
        QWindowSystemInterface::flushWindowSystemEvents();
 
204
    }
 
205
}
 
206
 
 
207
- (void)windowNotification : (NSNotification *) windowNotification
 
208
{
 
209
    //qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
 
210
 
 
211
    NSString *notificationName = [windowNotification name];
 
212
    if (notificationName == NSWindowDidBecomeKeyNotification) {
 
213
        if (!m_platformWindow->windowIsPopupType())
 
214
            QWindowSystemInterface::handleWindowActivated(m_window);
 
215
    } else if (notificationName == NSWindowDidResignKeyNotification) {
 
216
        // key window will be non-nil if another window became key... do not
 
217
        // set the active window to zero here, the new key window's
 
218
        // NSWindowDidBecomeKeyNotification hander will change the active window
 
219
        NSWindow *keyWindow = [NSApp keyWindow];
 
220
        if (!keyWindow) {
 
221
            // no new key window, go ahead and set the active window to zero
 
222
            if (!m_platformWindow->windowIsPopupType())
 
223
                QWindowSystemInterface::handleWindowActivated(0);
 
224
        }
 
225
    } else if (notificationName == NSWindowDidMiniaturizeNotification) {
 
226
        QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowMinimized);
 
227
    } else if (notificationName == NSWindowDidDeminiaturizeNotification) {
 
228
        QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
 
229
        // Qt expects an expose event after restore/deminiaturize. This also needs
 
230
        // to be a non-synchronous event to make sure it gets processed after
 
231
        // the state change event sent above.
 
232
        QWindowSystemInterface::handleExposeEvent(m_window, QRegion(m_window->geometry()));
 
233
    } else {
 
234
 
 
235
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
 
236
    if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
 
237
        if (notificationName == NSWindowDidEnterFullScreenNotification) {
 
238
            QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowFullScreen);
 
239
        } else if (notificationName == NSWindowDidExitFullScreenNotification) {
 
240
            QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
 
241
        }
 
242
    }
 
243
#endif
 
244
 
 
245
    }
 
246
}
 
247
 
 
248
- (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset
 
249
{
 
250
    m_backingStore = backingStore;
 
251
    m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio();
 
252
    QRect br = region.boundingRect();
 
253
    [self setNeedsDisplayInRect:NSMakeRect(br.x(), br.y(), br.width(), br.height())];
 
254
}
 
255
 
 
256
- (void) setMaskRegion:(const QRegion *)region
 
257
{
 
258
    if (m_maskImage)
 
259
        CGImageRelease(m_maskImage);
 
260
    if (region->isEmpty()) {
 
261
        m_maskImage = 0;
 
262
    }
 
263
 
 
264
    const QRect &rect = qt_mac_toQRect([self frame]);
 
265
    QImage maskImage(rect.size(), QImage::Format_RGB888);
 
266
    maskImage.fill(Qt::white);
 
267
    QPainter p(&maskImage);
 
268
    p.setRenderHint(QPainter::Antialiasing);
 
269
    p.setClipRegion(*region);
 
270
    p.fillRect(rect, QBrush(Qt::black));
 
271
    p.end();
 
272
 
 
273
    maskImage = maskImage.convertToFormat(QImage::Format_Indexed8);
 
274
    m_maskImage = qt_mac_toCGImage(maskImage, true, &m_maskData);
 
275
}
 
276
 
 
277
- (void) drawRect:(NSRect)dirtyRect
 
278
{
 
279
    if (!m_backingStore)
 
280
        return;
 
281
 
 
282
    // Calculate source and target rects. The target rect is the dirtyRect:
 
283
    CGRect dirtyWindowRect = NSRectToCGRect(dirtyRect);
 
284
 
 
285
    // The backing store source rect will be larger on retina displays.
 
286
    // Scale dirtyRect by the device pixel ratio:
 
287
    const qreal devicePixelRatio = m_backingStore->getBackingStoreDevicePixelRatio();
 
288
    CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * devicePixelRatio,
 
289
                                         dirtyRect.origin.y * devicePixelRatio,
 
290
                                         dirtyRect.size.width * devicePixelRatio,
 
291
                                         dirtyRect.size.height * devicePixelRatio);
 
292
 
 
293
    NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
 
294
    CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
 
295
 
 
296
    // Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left):
 
297
    CGContextSaveGState(cgContext);
 
298
    int dy = dirtyWindowRect.origin.y + CGRectGetMaxY(dirtyWindowRect);
 
299
 
 
300
    CGContextTranslateCTM(cgContext, 0, dy);
 
301
    CGContextScaleCTM(cgContext, 1, -1);
 
302
 
 
303
    // If a mask is set, modify the sub image accordingly:
 
304
    CGImageRef subMask = 0;
 
305
    if (m_maskImage) {
 
306
        subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyWindowRect);
 
307
        CGContextClipToMask(cgContext, dirtyWindowRect, subMask);
 
308
    }
 
309
 
 
310
    // Clip out and draw the correct sub image from the (shared) backingstore:
 
311
    CGRect backingStoreRect = CGRectMake(
 
312
        dirtyBackingRect.origin.x + m_backingStoreOffset.x(),
 
313
        dirtyBackingRect.origin.y + m_backingStoreOffset.y(),
 
314
        dirtyBackingRect.size.width,
 
315
        dirtyBackingRect.size.height
 
316
    );
 
317
    CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage();
 
318
    CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect);
 
319
    CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
 
320
    CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg);
 
321
 
 
322
    // Clean-up:
 
323
    CGContextRestoreGState(cgContext);
 
324
    CGImageRelease(cleanImg);
 
325
    CGImageRelease(subMask);
 
326
}
 
327
 
 
328
- (BOOL) isFlipped
 
329
{
 
330
    return YES;
 
331
}
 
332
 
 
333
- (BOOL)acceptsFirstResponder
 
334
{
 
335
    return YES;
 
336
}
 
337
 
 
338
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
 
339
{
 
340
    Q_UNUSED(theEvent);
 
341
    return YES;
 
342
}
 
343
 
 
344
- (void)convertFromEvent:(NSEvent *)event toWindowPoint:(QPoint *)qtWindowPoint andScreenPoint:(QPoint *)qtScreenPoint
 
345
{
 
346
    // Calculate the mouse position in the QWindow and Qt screen coordinate system,
 
347
    // starting from coordinates in the NSWindow coordinate system.
 
348
    //
 
349
    // This involves translating according to the window location on screen,
 
350
    // as well as inverting the y coordinate due to the origin change.
 
351
    //
 
352
    // Coordinate system overview, outer to innermost:
 
353
    //
 
354
    // Name             Origin
 
355
    //
 
356
    // OS X screen      bottom-left
 
357
    // Qt screen        top-left
 
358
    // NSWindow         bottom-left
 
359
    // NSView/QWindow   top-left
 
360
    //
 
361
    // NSView and QWindow are equal coordinate systems: the QWindow covers the
 
362
    // entire NSView, and we've set the NSView's isFlipped property to true.
 
363
 
 
364
    NSPoint nsWindowPoint = [event locationInWindow];                    // NSWindow coordinates
 
365
 
 
366
    NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
 
367
    *qtWindowPoint = QPoint(nsViewPoint.x, nsViewPoint.y);                     // NSView/QWindow coordinates
 
368
 
 
369
    NSWindow *window = [self window];
 
370
    // Use convertRectToScreen if available (added in 10.7).
 
371
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
 
372
    if ([window respondsToSelector:@selector(convertRectToScreen:)]) {
 
373
        NSRect screenRect = [window convertRectToScreen : NSMakeRect(nsWindowPoint.x, nsWindowPoint.y, 0, 0)]; // OS X screen coordinates
 
374
        *qtScreenPoint = QPoint(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y));              // Qt screen coordinates
 
375
    } else
 
376
#endif
 
377
    {
 
378
        NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
 
379
        *qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
 
380
    }
 
381
}
 
382
 
 
383
- (void)handleMouseEvent:(NSEvent *)theEvent
 
384
{
 
385
    QPoint qtWindowPoint, qtScreenPoint;
 
386
    [self convertFromEvent:theEvent toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
 
387
    ulong timestamp = [theEvent timestamp] * 1000;
 
388
 
 
389
    QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
 
390
    nativeDrag->setLastMouseEvent(theEvent, self);
 
391
 
 
392
    Qt::KeyboardModifiers keyboardModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
 
393
    QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers);
 
394
}
 
395
 
 
396
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
 
397
{
 
398
    // get m_buttons in sync
 
399
    NSEventType ty = [theEvent type];
 
400
    switch (ty) {
 
401
    case NSLeftMouseDown:
 
402
        m_buttons |= Qt::LeftButton;
 
403
        break;
 
404
    case NSLeftMouseUp:
 
405
         m_buttons &= QFlag(~int(Qt::LeftButton));
 
406
         break;
 
407
    case NSRightMouseDown:
 
408
        m_buttons |= Qt::RightButton;
 
409
        break;
 
410
    case NSRightMouseUp:
 
411
        m_buttons &= QFlag(~int(Qt::RightButton));
 
412
        break;
 
413
    default:
 
414
        break;
 
415
    }
 
416
 
 
417
    NSWindow *window = [self window];
 
418
    NSPoint windowPoint = [theEvent locationInWindow];
 
419
 
 
420
    int windowScreenY = [window frame].origin.y + [window frame].size.height;
 
421
    int viewScreenY = [window convertBaseToScreen:[self convertPoint:[self frame].origin toView:nil]].y;
 
422
    int titleBarHeight = windowScreenY - viewScreenY;
 
423
 
 
424
    NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
 
425
    QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y);
 
426
    NSPoint screenPoint = [window convertBaseToScreen:windowPoint];
 
427
    QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
 
428
 
 
429
    ulong timestamp = [theEvent timestamp] * 1000;
 
430
    QWindowSystemInterface::handleFrameStrutMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
 
431
}
 
432
 
 
433
- (void)mouseDown:(NSEvent *)theEvent
 
434
{
 
435
    m_sendUpAsRightButton = false;
 
436
    if (m_platformWindow->m_activePopupWindow) {
 
437
        QWindowSystemInterface::handleCloseEvent(m_platformWindow->m_activePopupWindow);
 
438
        QWindowSystemInterface::flushWindowSystemEvents();
 
439
        m_platformWindow->m_activePopupWindow = 0;
 
440
    }
 
441
    if ([self hasMarkedText]) {
 
442
        NSInputManager* inputManager = [NSInputManager currentInputManager];
 
443
        if ([inputManager wantsToHandleMouseEvents]) {
 
444
            [inputManager handleMouseEvent:theEvent];
 
445
        }
 
446
    } else {
 
447
        if ([self convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) {
 
448
            m_buttons |= Qt::RightButton;
 
449
            m_sendUpAsRightButton = true;
 
450
        } else {
 
451
            m_buttons |= Qt::LeftButton;
 
452
        }
 
453
        [self handleMouseEvent:theEvent];
 
454
    }
 
455
}
 
456
 
 
457
- (void)mouseDragged:(NSEvent *)theEvent
 
458
{
 
459
    if (!(m_buttons & Qt::LeftButton))
 
460
        qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
 
461
    [self handleMouseEvent:theEvent];
 
462
}
 
463
 
 
464
- (void)mouseUp:(NSEvent *)theEvent
 
465
{
 
466
    if (m_sendUpAsRightButton) {
 
467
        m_buttons &= QFlag(~int(Qt::RightButton));
 
468
        m_sendUpAsRightButton = false;
 
469
    } else {
 
470
        m_buttons &= QFlag(~int(Qt::LeftButton));
 
471
    }
 
472
    [self handleMouseEvent:theEvent];
 
473
}
 
474
 
 
475
- (void)updateTrackingAreas
 
476
{
 
477
    [super updateTrackingAreas];
 
478
 
 
479
    // [NSView addTrackingArea] is slow, so bail out early if we can:
 
480
    if (NSIsEmptyRect([self visibleRect]))
 
481
        return;
 
482
 
 
483
    QCocoaAutoReleasePool pool;
 
484
    if (NSArray *trackingArray = [self trackingAreas]) {
 
485
        NSUInteger size = [trackingArray count];
 
486
        for (NSUInteger i = 0; i < size; ++i) {
 
487
            NSTrackingArea *t = [trackingArray objectAtIndex:i];
 
488
            [self removeTrackingArea:t];
 
489
        }
 
490
    }
 
491
 
 
492
    // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
 
493
    // only be turned on if mouseTracking, hover is on or a tool tip is set.
 
494
    // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
 
495
    // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
 
496
    // mouse moves delivered to it (Apple recommends keeping it OFF because there
 
497
    // is a performance hit). So it goes.
 
498
    NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
 
499
                                 | NSTrackingInVisibleRect | NSTrackingMouseMoved;
 
500
    NSTrackingArea *ta = [[[NSTrackingArea alloc] initWithRect:[self frame]
 
501
                                                      options:trackingOptions
 
502
                                                        owner:self
 
503
                                                     userInfo:nil]
 
504
                                                                autorelease];
 
505
    [self addTrackingArea:ta];
 
506
}
 
507
 
 
508
- (void)mouseMoved:(NSEvent *)theEvent
 
509
{
 
510
    [self handleMouseEvent:theEvent];
 
511
}
 
512
 
 
513
- (void)mouseEntered:(NSEvent *)theEvent
 
514
{
 
515
    QPoint windowPoint, screenPoint;
 
516
    [self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
 
517
    QWindowSystemInterface::handleEnterEvent(m_window, windowPoint, screenPoint);
 
518
}
 
519
 
 
520
- (void)mouseExited:(NSEvent *)theEvent
 
521
{
 
522
    Q_UNUSED(theEvent);
 
523
    QWindowSystemInterface::handleLeaveEvent(m_window);
 
524
}
 
525
 
 
526
- (void)rightMouseDown:(NSEvent *)theEvent
 
527
{
 
528
    m_buttons |= Qt::RightButton;
 
529
    [self handleMouseEvent:theEvent];
 
530
}
 
531
 
 
532
- (void)rightMouseDragged:(NSEvent *)theEvent
 
533
{
 
534
    if (!(m_buttons & Qt::RightButton))
 
535
        qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
 
536
    [self handleMouseEvent:theEvent];
 
537
}
 
538
 
 
539
- (void)rightMouseUp:(NSEvent *)theEvent
 
540
{
 
541
    m_buttons &= QFlag(~int(Qt::RightButton));
 
542
    [self handleMouseEvent:theEvent];
 
543
}
 
544
 
 
545
- (void)otherMouseDown:(NSEvent *)theEvent
 
546
{
 
547
    switch ([theEvent buttonNumber]) {
 
548
        case 3:
 
549
            m_buttons |= Qt::MiddleButton;
 
550
            break;
 
551
        case 4:
 
552
            m_buttons |= Qt::ExtraButton1;  // AKA Qt::BackButton
 
553
            break;
 
554
        case 5:
 
555
            m_buttons |= Qt::ExtraButton2;  // AKA Qt::ForwardButton
 
556
            break;
 
557
        case 6:
 
558
            m_buttons |= Qt::ExtraButton3;
 
559
            break;
 
560
        case 7:
 
561
            m_buttons |= Qt::ExtraButton4;
 
562
            break;
 
563
        case 8:
 
564
            m_buttons |= Qt::ExtraButton5;
 
565
            break;
 
566
        case 9:
 
567
            m_buttons |= Qt::ExtraButton6;
 
568
            break;
 
569
        case 10:
 
570
            m_buttons |= Qt::ExtraButton7;
 
571
            break;
 
572
        case 11:
 
573
            m_buttons |= Qt::ExtraButton8;
 
574
            break;
 
575
        case 12:
 
576
            m_buttons |= Qt::ExtraButton9;
 
577
            break;
 
578
        case 13:
 
579
            m_buttons |= Qt::ExtraButton10;
 
580
            break;
 
581
        case 14:
 
582
            m_buttons |= Qt::ExtraButton11;
 
583
            break;
 
584
        case 15:
 
585
            m_buttons |= Qt::ExtraButton12;
 
586
            break;
 
587
        case 16:
 
588
            m_buttons |= Qt::ExtraButton13;
 
589
            break;
 
590
        default:
 
591
            m_buttons |= Qt::MiddleButton;
 
592
            break;
 
593
    }
 
594
    [self handleMouseEvent:theEvent];
 
595
}
 
596
 
 
597
- (void)otherMouseDragged:(NSEvent *)theEvent
 
598
{
 
599
    if (!(m_buttons & ~(Qt::LeftButton | Qt::RightButton)))
 
600
        qWarning("QNSView otherMouseDragged: Internal mouse button tracking invalid (missing Qt::MiddleButton or Qt::ExtraButton*)");
 
601
    [self handleMouseEvent:theEvent];
 
602
}
 
603
 
 
604
- (void)otherMouseUp:(NSEvent *)theEvent
 
605
{
 
606
    switch ([theEvent buttonNumber]) {
 
607
        case 3:
 
608
            m_buttons &= QFlag(~int(Qt::MiddleButton));
 
609
            break;
 
610
        case 4:
 
611
            m_buttons &= QFlag(~int(Qt::ExtraButton1));  // AKA Qt::BackButton
 
612
            break;
 
613
        case 5:
 
614
            m_buttons &= QFlag(~int(Qt::ExtraButton2));  // AKA Qt::ForwardButton
 
615
            break;
 
616
        case 6:
 
617
            m_buttons &= QFlag(~int(Qt::ExtraButton3));
 
618
            break;
 
619
        case 7:
 
620
            m_buttons &= QFlag(~int(Qt::ExtraButton4));
 
621
            break;
 
622
        case 8:
 
623
            m_buttons &= QFlag(~int(Qt::ExtraButton5));
 
624
            break;
 
625
        case 9:
 
626
            m_buttons &= QFlag(~int(Qt::ExtraButton6));
 
627
            break;
 
628
        case 10:
 
629
            m_buttons &= QFlag(~int(Qt::ExtraButton7));
 
630
            break;
 
631
        case 11:
 
632
            m_buttons &= QFlag(~int(Qt::ExtraButton8));
 
633
            break;
 
634
        case 12:
 
635
            m_buttons &= QFlag(~int(Qt::ExtraButton9));
 
636
            break;
 
637
        case 13:
 
638
            m_buttons &= QFlag(~int(Qt::ExtraButton10));
 
639
            break;
 
640
        case 14:
 
641
            m_buttons &= QFlag(~int(Qt::ExtraButton11));
 
642
            break;
 
643
        case 15:
 
644
            m_buttons &= QFlag(~int(Qt::ExtraButton12));
 
645
            break;
 
646
        case 16:
 
647
            m_buttons &= QFlag(~int(Qt::ExtraButton13));
 
648
            break;
 
649
        default:
 
650
            m_buttons &= QFlag(~int(Qt::MiddleButton));
 
651
            break;
 
652
    }
 
653
    [self handleMouseEvent:theEvent];
 
654
}
 
655
 
 
656
- (void)touchesBeganWithEvent:(NSEvent *)event
 
657
{
 
658
    const NSTimeInterval timestamp = [event timestamp];
 
659
    const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
 
660
    QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
 
661
}
 
662
 
 
663
- (void)touchesMovedWithEvent:(NSEvent *)event
 
664
{
 
665
    const NSTimeInterval timestamp = [event timestamp];
 
666
    const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
 
667
    QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
 
668
}
 
669
 
 
670
- (void)touchesEndedWithEvent:(NSEvent *)event
 
671
{
 
672
    const NSTimeInterval timestamp = [event timestamp];
 
673
    const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
 
674
    QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
 
675
}
 
676
 
 
677
- (void)touchesCancelledWithEvent:(NSEvent *)event
 
678
{
 
679
    const NSTimeInterval timestamp = [event timestamp];
 
680
    const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
 
681
    QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
 
682
}
 
683
 
 
684
#ifndef QT_NO_WHEELEVENT
 
685
- (void)scrollWheel:(NSEvent *)theEvent
 
686
{
 
687
    const EventRef carbonEvent = (EventRef)[theEvent eventRef];
 
688
    const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
 
689
    const bool scrollEvent = carbonEventKind == kEventMouseScroll;
 
690
 
 
691
    QPoint angleDelta;
 
692
    if (scrollEvent) {
 
693
        // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad).
 
694
        // Since deviceDelta is delivered as pixels rather than degrees, we need to
 
695
        // convert from pixels to degrees in a sensible manner.
 
696
        // It looks like 1/4 degrees per pixel behaves most native.
 
697
        // (NB: Qt expects the unit for delta to be 8 per degree):
 
698
        const int pixelsToDegrees = 2; // 8 * 1/4
 
699
 
 
700
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
 
701
        if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
 
702
            angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees);
 
703
            angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees);
 
704
        } else
 
705
#endif
 
706
        {
 
707
            angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
 
708
            angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
 
709
        }
 
710
 
 
711
    } else {
 
712
        // carbonEventKind == kEventMouseWheelMoved
 
713
        // Remove acceleration, and use either -120 or 120 as delta:
 
714
        angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120));
 
715
        angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120));
 
716
    }
 
717
 
 
718
    QPoint pixelDelta;
 
719
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
 
720
    if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
 
721
        if ([theEvent hasPreciseScrollingDeltas]) {
 
722
            pixelDelta.setX([theEvent scrollingDeltaX]);
 
723
            pixelDelta.setY([theEvent scrollingDeltaY]);
 
724
        } else {
 
725
            // docs: "In the case of !hasPreciseScrollingDeltas, multiply the delta with the line width."
 
726
            // scrollingDeltaX seems to return a minimum value of 0.1 in this case, map that to two pixels.
 
727
            const CGFloat lineWithEstimate = 20.0;
 
728
            pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate);
 
729
            pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate);
 
730
        }
 
731
    }
 
732
#endif
 
733
 
 
734
 
 
735
    NSPoint windowPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
 
736
    QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
 
737
    NSTimeInterval timestamp = [theEvent timestamp];
 
738
    ulong qt_timestamp = timestamp * 1000;
 
739
 
 
740
    // Set keyboard modifiers depending on event phase. A two-finger trackpad flick
 
741
    // generates a stream of scroll events. We want the keyboard modifier state to
 
742
    // be the state at the beginning of the flick in order to avoid changing the
 
743
    // interpretation of the events mid-stream. One example of this happening would
 
744
    // be when pressing cmd after scrolling in Qt Creator: not taking the phase into
 
745
    // account causes the end of the event stream to be interpreted as font size changes.
 
746
 
 
747
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
 
748
    if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
 
749
        NSEventPhase phase = [theEvent phase];
 
750
        if (phase == NSEventPhaseBegan) {
 
751
            currentWheelModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
 
752
        }
 
753
 
 
754
        QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
 
755
 
 
756
        if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
 
757
            currentWheelModifiers = Qt::NoModifier;
 
758
        }
 
759
    } else
 
760
#endif
 
761
    {
 
762
        QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
 
763
                                                 [self convertKeyModifiers:[theEvent modifierFlags]]);
 
764
    }
 
765
}
 
766
#endif //QT_NO_WHEELEVENT
 
767
 
 
768
- (int) convertKeyCode : (QChar)keyChar
 
769
{
 
770
    return qt_mac_cocoaKey2QtKey(keyChar);
 
771
}
 
772
 
 
773
- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
 
774
{
 
775
    Qt::KeyboardModifiers qtMods =Qt::NoModifier;
 
776
    if (modifierFlags &  NSShiftKeyMask)
 
777
        qtMods |= Qt::ShiftModifier;
 
778
    if (modifierFlags & NSControlKeyMask)
 
779
        qtMods |= Qt::MetaModifier;
 
780
    if (modifierFlags & NSAlternateKeyMask)
 
781
        qtMods |= Qt::AltModifier;
 
782
    if (modifierFlags & NSCommandKeyMask)
 
783
        qtMods |= Qt::ControlModifier;
 
784
    if (modifierFlags & NSNumericPadKeyMask)
 
785
        qtMods |= Qt::KeypadModifier;
 
786
    return qtMods;
 
787
}
 
788
 
 
789
- (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
 
790
{
 
791
    ulong timestamp = [nsevent timestamp] * 1000;
 
792
    ulong nativeModifiers = [nsevent modifierFlags];
 
793
    Qt::KeyboardModifiers modifiers = [self convertKeyModifiers: nativeModifiers];
 
794
    NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
 
795
 
 
796
    // [from Qt 4 impl] There is no way to get the scan code from carbon. But we cannot
 
797
    // use the value 0, since it indicates that the event originates from somewhere
 
798
    // else than the keyboard.
 
799
    quint32 nativeScanCode = 1;
 
800
 
 
801
    UInt32 nativeVirtualKey = 0;
 
802
    EventRef eventRef = EventRef([nsevent eventRef]);
 
803
    GetEventParameter(eventRef, kEventParamKeyCode, typeUInt32, 0, sizeof(nativeVirtualKey), 0, &nativeVirtualKey);
 
804
 
 
805
    QChar ch;
 
806
    int keyCode;
 
807
    if ([charactersIgnoringModifiers length] > 0) {
 
808
        // convert the first character into a key code
 
809
        ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
 
810
        keyCode = [self convertKeyCode:ch];
 
811
    } else {
 
812
        // might be a dead key
 
813
        ch = QChar::ReplacementCharacter;
 
814
        keyCode = Qt::Key_unknown;
 
815
    }
 
816
 
 
817
    // we will send a key event unless the input method sets m_sendKeyEvent to false
 
818
    m_sendKeyEvent = true;
 
819
 
 
820
    QString text;
 
821
    if (eventType == QEvent::KeyPress) {
 
822
        // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
 
823
        // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
 
824
        if ([charactersIgnoringModifiers length] == 1 && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff))
 
825
            text = QCFString::toQString([nsevent characters]);
 
826
 
 
827
        if (m_composingText.isEmpty())
 
828
            m_sendKeyEvent = !QWindowSystemInterface::tryHandleShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
 
829
 
 
830
        QObject *fo = QGuiApplication::focusObject();
 
831
        if (m_sendKeyEvent && fo) {
 
832
            QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
 
833
            if (QCoreApplication::sendEvent(fo, &queryEvent)) {
 
834
                bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool();
 
835
                Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
 
836
                if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText)) {
 
837
                    // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call
 
838
                    [self interpretKeyEvents:[NSArray arrayWithObject:nsevent]];
 
839
                }
 
840
            }
 
841
        }
 
842
    }
 
843
 
 
844
    if (m_sendKeyEvent && m_composingText.isEmpty())
 
845
        QWindowSystemInterface::handleExtendedKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers,
 
846
                                                       nativeScanCode, nativeVirtualKey, nativeModifiers, text);
 
847
 
 
848
    m_sendKeyEvent = false;
 
849
}
 
850
 
 
851
- (void)keyDown:(NSEvent *)nsevent
 
852
{
 
853
    [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
 
854
}
 
855
 
 
856
- (void)keyUp:(NSEvent *)nsevent
 
857
{
 
858
    [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
 
859
}
 
860
 
 
861
- (void)flagsChanged:(NSEvent *)nsevent
 
862
{
 
863
    ulong timestamp = [nsevent timestamp] * 1000;
 
864
    ulong modifiers = [nsevent modifierFlags];
 
865
    Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
 
866
 
 
867
    // calculate the delta and remember the current modifiers for next time
 
868
    static ulong m_lastKnownModifiers;
 
869
    ulong lastKnownModifiers = m_lastKnownModifiers;
 
870
    ulong delta = lastKnownModifiers ^ modifiers;
 
871
    m_lastKnownModifiers = modifiers;
 
872
 
 
873
    struct qt_mac_enum_mapper
 
874
    {
 
875
        ulong mac_mask;
 
876
        Qt::Key qt_code;
 
877
    };
 
878
    static qt_mac_enum_mapper modifier_key_symbols[] = {
 
879
        { NSShiftKeyMask, Qt::Key_Shift },
 
880
        { NSControlKeyMask, Qt::Key_Meta },
 
881
        { NSCommandKeyMask, Qt::Key_Control },
 
882
        { NSAlternateKeyMask, Qt::Key_Alt },
 
883
        { NSAlphaShiftKeyMask, Qt::Key_CapsLock },
 
884
        { 0ul, Qt::Key_unknown } };
 
885
    for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) {
 
886
        uint mac_mask = modifier_key_symbols[i].mac_mask;
 
887
        if ((delta & mac_mask) == 0u)
 
888
            continue;
 
889
 
 
890
        QWindowSystemInterface::handleKeyEvent(m_window,
 
891
                                               timestamp,
 
892
                                               (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
 
893
                                               modifier_key_symbols[i].qt_code,
 
894
                                               qmodifiers);
 
895
    }
 
896
}
 
897
 
 
898
- (void) doCommandBySelector:(SEL)aSelector
 
899
{
 
900
    [self tryToPerform:aSelector with:self];
 
901
}
 
902
 
 
903
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
 
904
{
 
905
    Q_UNUSED(replacementRange)
 
906
 
 
907
    if (m_sendKeyEvent && m_composingText.isEmpty()) {
 
908
        // don't send input method events for simple text input (let handleKeyEvent send key events instead)
 
909
        return;
 
910
    }
 
911
 
 
912
    QString commitString;
 
913
    if ([aString length]) {
 
914
        if ([aString isKindOfClass:[NSAttributedString class]]) {
 
915
            commitString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
 
916
        } else {
 
917
            commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
 
918
        };
 
919
    }
 
920
    QObject *fo = QGuiApplication::focusObject();
 
921
    if (fo) {
 
922
        QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
 
923
        if (QCoreApplication::sendEvent(fo, &queryEvent)) {
 
924
            if (queryEvent.value(Qt::ImEnabled).toBool()) {
 
925
                QInputMethodEvent e;
 
926
                e.setCommitString(commitString);
 
927
                QCoreApplication::sendEvent(fo, &e);
 
928
                // prevent handleKeyEvent from sending a key event
 
929
                m_sendKeyEvent = false;
 
930
            }
 
931
        }
 
932
    }
 
933
 
 
934
    m_composingText.clear();
 
935
}
 
936
 
 
937
- (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
 
938
{
 
939
    Q_UNUSED(replacementRange)
 
940
    QString preeditString;
 
941
 
 
942
    QList<QInputMethodEvent::Attribute> attrs;
 
943
    attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
 
944
 
 
945
    if ([aString isKindOfClass:[NSAttributedString class]]) {
 
946
        // Preedit string has attribution
 
947
        preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
 
948
        int composingLength = preeditString.length();
 
949
        int index = 0;
 
950
        // Create attributes for individual sections of preedit text
 
951
        while (index < composingLength) {
 
952
            NSRange effectiveRange;
 
953
            NSRange range = NSMakeRange(index, composingLength-index);
 
954
            NSDictionary *attributes = [aString attributesAtIndex:index
 
955
                                            longestEffectiveRange:&effectiveRange
 
956
                                                          inRange:range];
 
957
            NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
 
958
            if (underlineStyle) {
 
959
                QColor clr (Qt::black);
 
960
                NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
 
961
                if (color) {
 
962
                    clr = qt_mac_toQColor(color);
 
963
                }
 
964
                QTextCharFormat format;
 
965
                format.setFontUnderline(true);
 
966
                format.setUnderlineColor(clr);
 
967
                attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
 
968
                                                    effectiveRange.location,
 
969
                                                    effectiveRange.length,
 
970
                                                    format);
 
971
            }
 
972
            index = effectiveRange.location + effectiveRange.length;
 
973
        }
 
974
    } else {
 
975
        // No attributes specified, take only the preedit text.
 
976
        preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
 
977
    }
 
978
 
 
979
    if (attrs.isEmpty()) {
 
980
        QTextCharFormat format;
 
981
        format.setFontUnderline(true);
 
982
        attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
 
983
                                            0, preeditString.length(), format);
 
984
    }
 
985
 
 
986
    m_composingText = preeditString;
 
987
 
 
988
    QObject *fo = QGuiApplication::focusObject();
 
989
    if (fo) {
 
990
        QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
 
991
        if (QCoreApplication::sendEvent(fo, &queryEvent)) {
 
992
            if (queryEvent.value(Qt::ImEnabled).toBool()) {
 
993
                QInputMethodEvent e(preeditString, attrs);
 
994
                QCoreApplication::sendEvent(fo, &e);
 
995
                // prevent handleKeyEvent from sending a key event
 
996
                m_sendKeyEvent = false;
 
997
            }
 
998
        }
 
999
    }
 
1000
}
 
1001
 
 
1002
- (void) unmarkText
 
1003
{
 
1004
    if (!m_composingText.isEmpty()) {
 
1005
        QObject *fo = QGuiApplication::focusObject();
 
1006
        if (fo) {
 
1007
            QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
 
1008
            if (QCoreApplication::sendEvent(fo, &queryEvent)) {
 
1009
                if (queryEvent.value(Qt::ImEnabled).toBool()) {
 
1010
                    QInputMethodEvent e;
 
1011
                    e.setCommitString(m_composingText);
 
1012
                    QCoreApplication::sendEvent(fo, &e);
 
1013
                }
 
1014
            }
 
1015
        }
 
1016
    }
 
1017
    m_composingText.clear();
 
1018
}
 
1019
 
 
1020
- (BOOL) hasMarkedText
 
1021
{
 
1022
    return (m_composingText.isEmpty() ? NO: YES);
 
1023
}
 
1024
 
 
1025
- (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
 
1026
{
 
1027
    Q_UNUSED(actualRange)
 
1028
    QObject *fo = QGuiApplication::focusObject();
 
1029
    if (!fo)
 
1030
        return nil;
 
1031
    QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
 
1032
    if (!QCoreApplication::sendEvent(fo, &queryEvent))
 
1033
        return nil;
 
1034
    if (!queryEvent.value(Qt::ImEnabled).toBool())
 
1035
        return nil;
 
1036
 
 
1037
    QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
 
1038
    if (selectedText.isEmpty())
 
1039
        return nil;
 
1040
 
 
1041
    QCFString string(selectedText.mid(aRange.location, aRange.length));
 
1042
    const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
 
1043
    return [[[NSAttributedString alloc]  initWithString:const_cast<NSString *>(tmpString)] autorelease];
 
1044
}
 
1045
 
 
1046
- (NSRange) markedRange
 
1047
{
 
1048
    NSRange range;
 
1049
    if (!m_composingText.isEmpty()) {
 
1050
        range.location = 0;
 
1051
        range.length = m_composingText.length();
 
1052
    } else {
 
1053
        range.location = NSNotFound;
 
1054
        range.length = 0;
 
1055
    }
 
1056
    return range;
 
1057
}
 
1058
 
 
1059
- (NSRange) selectedRange
 
1060
{
 
1061
    NSRange selectedRange = {NSNotFound, 0};
 
1062
    selectedRange.location = NSNotFound;
 
1063
    selectedRange.length = 0;
 
1064
 
 
1065
    QObject *fo = QGuiApplication::focusObject();
 
1066
    if (!fo)
 
1067
        return selectedRange;
 
1068
    QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
 
1069
    if (!QCoreApplication::sendEvent(fo, &queryEvent))
 
1070
        return selectedRange;
 
1071
    if (!queryEvent.value(Qt::ImEnabled).toBool())
 
1072
        return selectedRange;
 
1073
 
 
1074
    QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
 
1075
 
 
1076
    if (!selectedText.isEmpty()) {
 
1077
        selectedRange.location = 0;
 
1078
        selectedRange.length = selectedText.length();
 
1079
    }
 
1080
    return selectedRange;
 
1081
}
 
1082
 
 
1083
- (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
 
1084
{
 
1085
    Q_UNUSED(aRange)
 
1086
    Q_UNUSED(actualRange)
 
1087
    QObject *fo = QGuiApplication::focusObject();
 
1088
    if (!fo)
 
1089
        return NSZeroRect;
 
1090
 
 
1091
    QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
 
1092
    if (!QCoreApplication::sendEvent(fo, &queryEvent))
 
1093
        return NSZeroRect;
 
1094
    if (!queryEvent.value(Qt::ImEnabled).toBool())
 
1095
        return NSZeroRect;
 
1096
 
 
1097
    if (!m_window)
 
1098
        return NSZeroRect;
 
1099
 
 
1100
    // The returned rect is always based on the internal cursor.
 
1101
    QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
 
1102
    QPoint mp = m_window->mapToGlobal(mr.bottomLeft());
 
1103
 
 
1104
    NSRect rect;
 
1105
    rect.origin.x = mp.x();
 
1106
    rect.origin.y = qt_mac_flipYCoordinate(mp.y());
 
1107
    rect.size.width = mr.width();
 
1108
    rect.size.height = mr.height();
 
1109
    return rect;
 
1110
}
 
1111
 
 
1112
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
 
1113
{
 
1114
    // We dont support cursor movements using mouse while composing.
 
1115
    Q_UNUSED(aPoint);
 
1116
    return NSNotFound;
 
1117
}
 
1118
 
 
1119
- (NSArray*) validAttributesForMarkedText
 
1120
{
 
1121
    if (m_window != QGuiApplication::focusWindow())
 
1122
        return nil;
 
1123
 
 
1124
    QObject *fo = QGuiApplication::focusObject();
 
1125
    if (!fo)
 
1126
        return nil;
 
1127
 
 
1128
    QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
 
1129
    if (!QCoreApplication::sendEvent(fo, &queryEvent))
 
1130
        return nil;
 
1131
    if (!queryEvent.value(Qt::ImEnabled).toBool())
 
1132
        return nil;
 
1133
 
 
1134
    // Support only underline color/style.
 
1135
    return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
 
1136
                                     NSUnderlineStyleAttributeName, nil];
 
1137
}
 
1138
 
 
1139
-(void)registerDragTypes
 
1140
{
 
1141
    QCocoaAutoReleasePool pool;
 
1142
    // ### Custom types disabled.
 
1143
    QStringList customTypes;  // = qEnabledDraggedTypes();
 
1144
    if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
 
1145
        if (currentCustomDragTypes == 0)
 
1146
            currentCustomDragTypes = new QStringList();
 
1147
        *currentCustomDragTypes = customTypes;
 
1148
        const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
 
1149
        NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
 
1150
                       NSFilenamesPboardType, NSStringPboardType,
 
1151
                       NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
 
1152
                       NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
 
1153
                       NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
 
1154
                       NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
 
1155
                       NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
 
1156
                       NSFilesPromisePboardType, NSInkTextPboardType,
 
1157
                       NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
 
1158
        // Add custom types supported by the application.
 
1159
        for (int i = 0; i < customTypes.size(); i++) {
 
1160
           [supportedTypes addObject:QCFString::toNSString(customTypes[i])];
 
1161
        }
 
1162
        [self registerForDraggedTypes:supportedTypes];
 
1163
    }
 
1164
}
 
1165
 
 
1166
- (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal
 
1167
{
 
1168
    Q_UNUSED(isLocal);
 
1169
    QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
 
1170
    return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
 
1171
}
 
1172
 
 
1173
- (BOOL) ignoreModifierKeysWhileDragging
 
1174
{
 
1175
    return NO;
 
1176
}
 
1177
 
 
1178
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 
1179
{
 
1180
    return [self handleDrag : sender];
 
1181
}
 
1182
 
 
1183
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
 
1184
{
 
1185
    return [self handleDrag : sender];
 
1186
}
 
1187
 
 
1188
// Sends drag update to Qt, return the action
 
1189
- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
 
1190
{
 
1191
    NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
 
1192
    QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
 
1193
    Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
 
1194
 
 
1195
    // update these so selecting move/copy/link works
 
1196
    QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
 
1197
 
 
1198
    QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect());
 
1199
    if ([sender draggingSource] != nil) {
 
1200
        QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
 
1201
        response = QWindowSystemInterface::handleDrag(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed);
 
1202
    } else {
 
1203
        QCocoaDropData mimeData([sender draggingPasteboard]);
 
1204
        response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
 
1205
    }
 
1206
 
 
1207
    return qt_mac_mapDropAction(response.acceptedAction());
 
1208
}
 
1209
 
 
1210
- (void)draggingExited:(id <NSDraggingInfo>)sender
 
1211
{
 
1212
    NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
 
1213
    QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
 
1214
 
 
1215
    // Send 0 mime data to indicate drag exit
 
1216
    QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
 
1217
}
 
1218
 
 
1219
// called on drop, send the drop to Qt and return if it was accepted.
 
1220
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 
1221
{
 
1222
    NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
 
1223
    QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
 
1224
    Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
 
1225
 
 
1226
    QPlatformDropQtResponse response(false, Qt::IgnoreAction);
 
1227
    if ([sender draggingSource] != nil) {
 
1228
        QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
 
1229
        response = QWindowSystemInterface::handleDrop(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed);
 
1230
    } else {
 
1231
        QCocoaDropData mimeData([sender draggingPasteboard]);
 
1232
        response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
 
1233
    }
 
1234
    return response.isAccepted();
 
1235
}
 
1236
 
 
1237
- (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation
 
1238
{
 
1239
    Q_UNUSED(img);
 
1240
    QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
 
1241
    nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
 
1242
 
 
1243
// keep our state, and QGuiApplication state (buttons member) in-sync,
 
1244
// or future mouse events will be processed incorrectly
 
1245
    m_buttons &= QFlag(~int(Qt::LeftButton));
 
1246
 
 
1247
    NSPoint windowPoint = [self convertPoint: point fromView: nil];
 
1248
    QPoint qtWindowPoint(windowPoint.x, windowPoint.y);
 
1249
 
 
1250
    NSWindow *window = [self window];
 
1251
    NSPoint screenPoint = [window convertBaseToScreen :point];
 
1252
    QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
 
1253
 
 
1254
    QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons);
 
1255
}
 
1256
 
 
1257
@end