1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40
****************************************************************************/
42
#include <QtCore/qglobal.h>
44
#include <Carbon/Carbon.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>
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"
61
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
62
#include <accessibilityinspector.h>
65
static QTouchDevice *touchDevice = 0;
67
@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
68
- (CGFloat)deviceDeltaX;
69
- (CGFloat)deviceDeltaY;
70
- (CGFloat)deviceDeltaZ;
73
@implementation QNSView
77
self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
83
m_buttons = Qt::NoButton;
84
m_sendKeyEvent = false;
85
m_subscribesForGlobalFrameNotifications = false;
86
currentCustomDragTypes = 0;
87
m_sendUpAsRightButton = false;
90
touchDevice = new QTouchDevice;
91
touchDevice->setType(QTouchDevice::TouchPad);
92
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
93
QWindowSystemInterface::registerTouchDevice(touchDevice);
101
CGImageRelease(m_maskImage);
106
if (m_subscribesForGlobalFrameNotifications) {
107
m_subscribesForGlobalFrameNotifications = false;
108
[[NSNotificationCenter defaultCenter] removeObserver:self
109
name:NSViewGlobalFrameDidChangeNotification
115
- (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
122
m_platformWindow = platformWindow;
123
m_accessibleRoot = 0;
124
m_sendKeyEvent = false;
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) {
132
m_accessibleRoot = window->accessibleRoot();
134
AccessibilityInspector *inspector = new AccessibilityInspector(window);
135
skipAccessibilityForInspectorWindows = true;
136
inspector->inspectWindow(window);
137
skipAccessibilityForInspectorWindows = false;
140
m_accessibleRoot = window->accessibleRoot();
143
[self registerDragTypes];
144
[self setPostsFrameChangedNotifications : YES];
145
[[NSNotificationCenter defaultCenter] addObserver:self
146
selector:@selector(updateGeometry)
147
name:NSViewFrameDidChangeNotification
153
- (void) setQCocoaGLContext:(QCocoaGLContext *)context
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
169
- (void) globalFrameChanged:(NSNotification*)notification
171
Q_UNUSED(notification);
172
QWindowSystemInterface::handleExposeEvent(m_window, m_window->geometry());
175
- (void)updateGeometry
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);
184
// child window, use the frame rect
185
geometry = qt_mac_toQRect([self frame]);
188
if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry())
191
#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
192
qDebug() << "QNSView::udpateGeometry" << m_platformWindow << geometry;
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);
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();
207
- (void)windowNotification : (NSNotification *) windowNotification
209
//qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
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];
221
// no new key window, go ahead and set the active window to zero
222
if (!m_platformWindow->windowIsPopupType())
223
QWindowSystemInterface::handleWindowActivated(0);
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()));
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);
248
- (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset
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())];
256
- (void) setMaskRegion:(const QRegion *)region
259
CGImageRelease(m_maskImage);
260
if (region->isEmpty()) {
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));
273
maskImage = maskImage.convertToFormat(QImage::Format_Indexed8);
274
m_maskImage = qt_mac_toCGImage(maskImage, true, &m_maskData);
277
- (void) drawRect:(NSRect)dirtyRect
282
// Calculate source and target rects. The target rect is the dirtyRect:
283
CGRect dirtyWindowRect = NSRectToCGRect(dirtyRect);
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);
293
NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
294
CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
296
// Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left):
297
CGContextSaveGState(cgContext);
298
int dy = dirtyWindowRect.origin.y + CGRectGetMaxY(dirtyWindowRect);
300
CGContextTranslateCTM(cgContext, 0, dy);
301
CGContextScaleCTM(cgContext, 1, -1);
303
// If a mask is set, modify the sub image accordingly:
304
CGImageRef subMask = 0;
306
subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyWindowRect);
307
CGContextClipToMask(cgContext, dirtyWindowRect, subMask);
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
317
CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage();
318
CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect);
319
CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
320
CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg);
323
CGContextRestoreGState(cgContext);
324
CGImageRelease(cleanImg);
325
CGImageRelease(subMask);
333
- (BOOL)acceptsFirstResponder
338
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
344
- (void)convertFromEvent:(NSEvent *)event toWindowPoint:(QPoint *)qtWindowPoint andScreenPoint:(QPoint *)qtScreenPoint
346
// Calculate the mouse position in the QWindow and Qt screen coordinate system,
347
// starting from coordinates in the NSWindow coordinate system.
349
// This involves translating according to the window location on screen,
350
// as well as inverting the y coordinate due to the origin change.
352
// Coordinate system overview, outer to innermost:
356
// OS X screen bottom-left
357
// Qt screen top-left
358
// NSWindow bottom-left
359
// NSView/QWindow top-left
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.
364
NSPoint nsWindowPoint = [event locationInWindow]; // NSWindow coordinates
366
NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
367
*qtWindowPoint = QPoint(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
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
378
NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
379
*qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
383
- (void)handleMouseEvent:(NSEvent *)theEvent
385
QPoint qtWindowPoint, qtScreenPoint;
386
[self convertFromEvent:theEvent toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
387
ulong timestamp = [theEvent timestamp] * 1000;
389
QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
390
nativeDrag->setLastMouseEvent(theEvent, self);
392
Qt::KeyboardModifiers keyboardModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
393
QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers);
396
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
398
// get m_buttons in sync
399
NSEventType ty = [theEvent type];
401
case NSLeftMouseDown:
402
m_buttons |= Qt::LeftButton;
405
m_buttons &= QFlag(~int(Qt::LeftButton));
407
case NSRightMouseDown:
408
m_buttons |= Qt::RightButton;
411
m_buttons &= QFlag(~int(Qt::RightButton));
417
NSWindow *window = [self window];
418
NSPoint windowPoint = [theEvent locationInWindow];
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;
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));
429
ulong timestamp = [theEvent timestamp] * 1000;
430
QWindowSystemInterface::handleFrameStrutMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
433
- (void)mouseDown:(NSEvent *)theEvent
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;
441
if ([self hasMarkedText]) {
442
NSInputManager* inputManager = [NSInputManager currentInputManager];
443
if ([inputManager wantsToHandleMouseEvents]) {
444
[inputManager handleMouseEvent:theEvent];
447
if ([self convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) {
448
m_buttons |= Qt::RightButton;
449
m_sendUpAsRightButton = true;
451
m_buttons |= Qt::LeftButton;
453
[self handleMouseEvent:theEvent];
457
- (void)mouseDragged:(NSEvent *)theEvent
459
if (!(m_buttons & Qt::LeftButton))
460
qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
461
[self handleMouseEvent:theEvent];
464
- (void)mouseUp:(NSEvent *)theEvent
466
if (m_sendUpAsRightButton) {
467
m_buttons &= QFlag(~int(Qt::RightButton));
468
m_sendUpAsRightButton = false;
470
m_buttons &= QFlag(~int(Qt::LeftButton));
472
[self handleMouseEvent:theEvent];
475
- (void)updateTrackingAreas
477
[super updateTrackingAreas];
479
// [NSView addTrackingArea] is slow, so bail out early if we can:
480
if (NSIsEmptyRect([self visibleRect]))
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];
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
505
[self addTrackingArea:ta];
508
- (void)mouseMoved:(NSEvent *)theEvent
510
[self handleMouseEvent:theEvent];
513
- (void)mouseEntered:(NSEvent *)theEvent
515
QPoint windowPoint, screenPoint;
516
[self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
517
QWindowSystemInterface::handleEnterEvent(m_window, windowPoint, screenPoint);
520
- (void)mouseExited:(NSEvent *)theEvent
523
QWindowSystemInterface::handleLeaveEvent(m_window);
526
- (void)rightMouseDown:(NSEvent *)theEvent
528
m_buttons |= Qt::RightButton;
529
[self handleMouseEvent:theEvent];
532
- (void)rightMouseDragged:(NSEvent *)theEvent
534
if (!(m_buttons & Qt::RightButton))
535
qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
536
[self handleMouseEvent:theEvent];
539
- (void)rightMouseUp:(NSEvent *)theEvent
541
m_buttons &= QFlag(~int(Qt::RightButton));
542
[self handleMouseEvent:theEvent];
545
- (void)otherMouseDown:(NSEvent *)theEvent
547
switch ([theEvent buttonNumber]) {
549
m_buttons |= Qt::MiddleButton;
552
m_buttons |= Qt::ExtraButton1; // AKA Qt::BackButton
555
m_buttons |= Qt::ExtraButton2; // AKA Qt::ForwardButton
558
m_buttons |= Qt::ExtraButton3;
561
m_buttons |= Qt::ExtraButton4;
564
m_buttons |= Qt::ExtraButton5;
567
m_buttons |= Qt::ExtraButton6;
570
m_buttons |= Qt::ExtraButton7;
573
m_buttons |= Qt::ExtraButton8;
576
m_buttons |= Qt::ExtraButton9;
579
m_buttons |= Qt::ExtraButton10;
582
m_buttons |= Qt::ExtraButton11;
585
m_buttons |= Qt::ExtraButton12;
588
m_buttons |= Qt::ExtraButton13;
591
m_buttons |= Qt::MiddleButton;
594
[self handleMouseEvent:theEvent];
597
- (void)otherMouseDragged:(NSEvent *)theEvent
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];
604
- (void)otherMouseUp:(NSEvent *)theEvent
606
switch ([theEvent buttonNumber]) {
608
m_buttons &= QFlag(~int(Qt::MiddleButton));
611
m_buttons &= QFlag(~int(Qt::ExtraButton1)); // AKA Qt::BackButton
614
m_buttons &= QFlag(~int(Qt::ExtraButton2)); // AKA Qt::ForwardButton
617
m_buttons &= QFlag(~int(Qt::ExtraButton3));
620
m_buttons &= QFlag(~int(Qt::ExtraButton4));
623
m_buttons &= QFlag(~int(Qt::ExtraButton5));
626
m_buttons &= QFlag(~int(Qt::ExtraButton6));
629
m_buttons &= QFlag(~int(Qt::ExtraButton7));
632
m_buttons &= QFlag(~int(Qt::ExtraButton8));
635
m_buttons &= QFlag(~int(Qt::ExtraButton9));
638
m_buttons &= QFlag(~int(Qt::ExtraButton10));
641
m_buttons &= QFlag(~int(Qt::ExtraButton11));
644
m_buttons &= QFlag(~int(Qt::ExtraButton12));
647
m_buttons &= QFlag(~int(Qt::ExtraButton13));
650
m_buttons &= QFlag(~int(Qt::MiddleButton));
653
[self handleMouseEvent:theEvent];
656
- (void)touchesBeganWithEvent:(NSEvent *)event
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);
663
- (void)touchesMovedWithEvent:(NSEvent *)event
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);
670
- (void)touchesEndedWithEvent:(NSEvent *)event
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);
677
- (void)touchesCancelledWithEvent:(NSEvent *)event
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);
684
#ifndef QT_NO_WHEELEVENT
685
- (void)scrollWheel:(NSEvent *)theEvent
687
const EventRef carbonEvent = (EventRef)[theEvent eventRef];
688
const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
689
const bool scrollEvent = carbonEventKind == kEventMouseScroll;
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
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);
707
angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
708
angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
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));
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]);
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);
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;
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.
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]];
754
QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
756
if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
757
currentWheelModifiers = Qt::NoModifier;
762
QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
763
[self convertKeyModifiers:[theEvent modifierFlags]]);
766
#endif //QT_NO_WHEELEVENT
768
- (int) convertKeyCode : (QChar)keyChar
770
return qt_mac_cocoaKey2QtKey(keyChar);
773
- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
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;
789
- (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
791
ulong timestamp = [nsevent timestamp] * 1000;
792
ulong nativeModifiers = [nsevent modifierFlags];
793
Qt::KeyboardModifiers modifiers = [self convertKeyModifiers: nativeModifiers];
794
NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
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;
801
UInt32 nativeVirtualKey = 0;
802
EventRef eventRef = EventRef([nsevent eventRef]);
803
GetEventParameter(eventRef, kEventParamKeyCode, typeUInt32, 0, sizeof(nativeVirtualKey), 0, &nativeVirtualKey);
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];
812
// might be a dead key
813
ch = QChar::ReplacementCharacter;
814
keyCode = Qt::Key_unknown;
817
// we will send a key event unless the input method sets m_sendKeyEvent to false
818
m_sendKeyEvent = true;
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]);
827
if (m_composingText.isEmpty())
828
m_sendKeyEvent = !QWindowSystemInterface::tryHandleShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
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]];
844
if (m_sendKeyEvent && m_composingText.isEmpty())
845
QWindowSystemInterface::handleExtendedKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers,
846
nativeScanCode, nativeVirtualKey, nativeModifiers, text);
848
m_sendKeyEvent = false;
851
- (void)keyDown:(NSEvent *)nsevent
853
[self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
856
- (void)keyUp:(NSEvent *)nsevent
858
[self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
861
- (void)flagsChanged:(NSEvent *)nsevent
863
ulong timestamp = [nsevent timestamp] * 1000;
864
ulong modifiers = [nsevent modifierFlags];
865
Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
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;
873
struct qt_mac_enum_mapper
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)
890
QWindowSystemInterface::handleKeyEvent(m_window,
892
(lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
893
modifier_key_symbols[i].qt_code,
898
- (void) doCommandBySelector:(SEL)aSelector
900
[self tryToPerform:aSelector with:self];
903
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
905
Q_UNUSED(replacementRange)
907
if (m_sendKeyEvent && m_composingText.isEmpty()) {
908
// don't send input method events for simple text input (let handleKeyEvent send key events instead)
912
QString commitString;
913
if ([aString length]) {
914
if ([aString isKindOfClass:[NSAttributedString class]]) {
915
commitString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
917
commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
920
QObject *fo = QGuiApplication::focusObject();
922
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
923
if (QCoreApplication::sendEvent(fo, &queryEvent)) {
924
if (queryEvent.value(Qt::ImEnabled).toBool()) {
926
e.setCommitString(commitString);
927
QCoreApplication::sendEvent(fo, &e);
928
// prevent handleKeyEvent from sending a key event
929
m_sendKeyEvent = false;
934
m_composingText.clear();
937
- (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
939
Q_UNUSED(replacementRange)
940
QString preeditString;
942
QList<QInputMethodEvent::Attribute> attrs;
943
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
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();
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
957
NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
958
if (underlineStyle) {
959
QColor clr (Qt::black);
960
NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
962
clr = qt_mac_toQColor(color);
964
QTextCharFormat format;
965
format.setFontUnderline(true);
966
format.setUnderlineColor(clr);
967
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
968
effectiveRange.location,
969
effectiveRange.length,
972
index = effectiveRange.location + effectiveRange.length;
975
// No attributes specified, take only the preedit text.
976
preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
979
if (attrs.isEmpty()) {
980
QTextCharFormat format;
981
format.setFontUnderline(true);
982
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
983
0, preeditString.length(), format);
986
m_composingText = preeditString;
988
QObject *fo = QGuiApplication::focusObject();
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;
1004
if (!m_composingText.isEmpty()) {
1005
QObject *fo = QGuiApplication::focusObject();
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);
1017
m_composingText.clear();
1020
- (BOOL) hasMarkedText
1022
return (m_composingText.isEmpty() ? NO: YES);
1025
- (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
1027
Q_UNUSED(actualRange)
1028
QObject *fo = QGuiApplication::focusObject();
1031
QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
1032
if (!QCoreApplication::sendEvent(fo, &queryEvent))
1034
if (!queryEvent.value(Qt::ImEnabled).toBool())
1037
QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
1038
if (selectedText.isEmpty())
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];
1046
- (NSRange) markedRange
1049
if (!m_composingText.isEmpty()) {
1051
range.length = m_composingText.length();
1053
range.location = NSNotFound;
1059
- (NSRange) selectedRange
1061
NSRange selectedRange = {NSNotFound, 0};
1062
selectedRange.location = NSNotFound;
1063
selectedRange.length = 0;
1065
QObject *fo = QGuiApplication::focusObject();
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;
1074
QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
1076
if (!selectedText.isEmpty()) {
1077
selectedRange.location = 0;
1078
selectedRange.length = selectedText.length();
1080
return selectedRange;
1083
- (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
1086
Q_UNUSED(actualRange)
1087
QObject *fo = QGuiApplication::focusObject();
1091
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
1092
if (!QCoreApplication::sendEvent(fo, &queryEvent))
1094
if (!queryEvent.value(Qt::ImEnabled).toBool())
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());
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();
1112
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
1114
// We dont support cursor movements using mouse while composing.
1119
- (NSArray*) validAttributesForMarkedText
1121
if (m_window != QGuiApplication::focusWindow())
1124
QObject *fo = QGuiApplication::focusObject();
1128
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
1129
if (!QCoreApplication::sendEvent(fo, &queryEvent))
1131
if (!queryEvent.value(Qt::ImEnabled).toBool())
1134
// Support only underline color/style.
1135
return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
1136
NSUnderlineStyleAttributeName, nil];
1139
-(void)registerDragTypes
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])];
1162
[self registerForDraggedTypes:supportedTypes];
1166
- (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal
1169
QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1170
return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
1173
- (BOOL) ignoreModifierKeysWhileDragging
1178
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1180
return [self handleDrag : sender];
1183
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1185
return [self handleDrag : sender];
1188
// Sends drag update to Qt, return the action
1189
- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
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]);
1195
// update these so selecting move/copy/link works
1196
QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
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);
1203
QCocoaDropData mimeData([sender draggingPasteboard]);
1204
response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
1207
return qt_mac_mapDropAction(response.acceptedAction());
1210
- (void)draggingExited:(id <NSDraggingInfo>)sender
1212
NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
1213
QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
1215
// Send 0 mime data to indicate drag exit
1216
QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
1219
// called on drop, send the drop to Qt and return if it was accepted.
1220
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
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]);
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);
1231
QCocoaDropData mimeData([sender draggingPasteboard]);
1232
response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
1234
return response.isAccepted();
1237
- (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation
1240
QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1241
nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
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));
1247
NSPoint windowPoint = [self convertPoint: point fromView: nil];
1248
QPoint qtWindowPoint(windowPoint.x, windowPoint.y);
1250
NSWindow *window = [self window];
1251
NSPoint screenPoint = [window convertBaseToScreen :point];
1252
QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
1254
QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons);