1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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>
59 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
60 #include <accessibilityinspector.h>
63 static QTouchDevice *touchDevice = 0;
65 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
66 - (CGFloat)deviceDeltaX;
67 - (CGFloat)deviceDeltaY;
68 - (CGFloat)deviceDeltaZ;
71 @implementation QNSView
75 self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
79 m_buttons = Qt::NoButton;
80 m_sendKeyEvent = false;
81 currentCustomDragTypes = 0;
83 touchDevice = new QTouchDevice;
84 touchDevice->setType(QTouchDevice::TouchPad);
85 touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
86 QWindowSystemInterface::registerTouchDevice(touchDevice);
92 - (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
99 m_platformWindow = platformWindow;
100 m_accessibleRoot = 0;
101 m_sendKeyEvent = false;
103 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
104 // prevent rift in space-time continuum, disable
105 // accessibility for the accessibility inspector's windows.
106 static bool skipAccessibilityForInspectorWindows = false;
107 if (!skipAccessibilityForInspectorWindows) {
109 m_accessibleRoot = window->accessibleRoot();
111 AccessibilityInspector *inspector = new AccessibilityInspector(window);
112 skipAccessibilityForInspectorWindows = true;
113 inspector->inspectWindow(window);
114 skipAccessibilityForInspectorWindows = false;
117 m_accessibleRoot = window->accessibleRoot();
120 [self registerDragTypes];
121 [self setPostsFrameChangedNotifications : YES];
122 [[NSNotificationCenter defaultCenter] addObserver:self
123 selector:@selector(updateGeometry)
124 name:NSViewFrameDidChangeNotification
130 - (void)updateGeometry
132 NSRect rect = [self frame];
133 NSRect windowRect = [[self window] frame];
134 QRect geo(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height);
136 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
137 qDebug() << "QNSView::udpateGeometry" << geo;
140 // Call setGeometry on QPlatformWindow. (not on QCocoaWindow,
141 // doing that will initiate a geometry change it and possibly create
142 // an infinite loop when this notification is triggered again.)
143 m_platformWindow->QPlatformWindow::setGeometry(geo);
145 // Send a geometry change event to Qt, if it's ready to handle events
146 if (!m_platformWindow->m_inConstructor)
147 QWindowSystemInterface::handleSynchronousGeometryChange(m_window, geo);
150 - (void)windowNotification : (NSNotification *) windowNotification
152 //qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
154 NSString *notificationName = [windowNotification name];
155 if (notificationName == NSWindowDidBecomeKeyNotification) {
156 if (!m_platformWindow->windowIsPopupType())
157 QWindowSystemInterface::handleWindowActivated(m_window);
158 } else if (notificationName == NSWindowDidResignKeyNotification) {
159 // key window will be non-nil if another window became key... do not
160 // set the active window to zero here, the new key window's
161 // NSWindowDidBecomeKeyNotification hander will change the active window
162 NSWindow *keyWindow = [NSApp keyWindow];
164 // no new key window, go ahead and set the active window to zero
165 if (!m_platformWindow->windowIsPopupType())
166 QWindowSystemInterface::handleWindowActivated(0);
168 } else if (notificationName == NSWindowDidMiniaturizeNotification) {
169 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowMinimized);
170 } else if (notificationName == NSWindowDidDeminiaturizeNotification) {
171 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
172 // Qt expects an expose event after restore/deminiaturize. This also needs
173 // to be a non-synchronous event to make sure it gets processed after
174 // the state change event sent above.
175 QWindowSystemInterface::handleExposeEvent(m_window, QRegion(m_window->geometry()));
178 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
179 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
180 if (notificationName == NSWindowDidEnterFullScreenNotification) {
181 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowFullScreen);
182 } else if (notificationName == NSWindowDidExitFullScreenNotification) {
183 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
191 - (void) setImage:(QImage *)image
193 CGImageRelease(m_cgImage);
195 const uchar *imageData = image->bits();
196 int bitDepth = image->depth();
197 int colorBufferSize = 8;
198 int bytesPrLine = image->bytesPerLine();
199 int width = image->width();
200 int height = image->height();
202 CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB();
204 CGDataProviderRef cgDataProviderRef = CGDataProviderCreateWithData(
210 m_cgImage = CGImageCreate(width,
216 kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
220 kCGRenderingIntentDefault);
222 CGColorSpaceRelease(cgColourSpaceRef);
226 - (void) drawRect:(NSRect)dirtyRect
231 CGRect dirtyCGRect = NSRectToCGRect(dirtyRect);
233 NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
234 CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
236 CGContextSaveGState( cgContext );
237 int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect);
238 CGContextTranslateCTM(cgContext, 0, dy);
239 CGContextScaleCTM(cgContext, 1, -1);
241 CGImageRef subImage = CGImageCreateWithImageInRect(m_cgImage, dirtyCGRect);
242 CGContextDrawImage(cgContext,dirtyCGRect,subImage);
244 CGContextRestoreGState(cgContext);
246 CGImageRelease(subImage);
255 - (BOOL)acceptsFirstResponder
260 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
266 - (void)handleMouseEvent:(NSEvent *)theEvent
268 // Calculate the mouse position in the QWindow and Qt screen coordinate system,
269 // starting from coordinates in the NSWindow coordinate system.
271 // This involves translating according to the window location on screen,
272 // as well as inverting the y coordinate due to the origin change.
274 // Coordinate system overview, outer to innermost:
278 // OS X screen bottom-left
279 // Qt screen top-left
280 // NSWindow bottom-left
281 // NSView/QWindow top-left
283 // NSView and QWindow are equal coordinate systems: the QWindow covers the
284 // entire NSView, and we've set the NSView's isFlipped property to true.
286 NSPoint nsWindowPoint = [theEvent locationInWindow]; // NSWindow coordinates
288 NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
289 QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
291 QPoint qtScreenPoint;
293 NSWindow *window = [self window];
294 // Use convertRectToScreen if available (added in 10.7).
295 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
296 if ([window respondsToSelector:@selector(convertRectToScreen:)]) {
297 NSRect screenRect = [window convertRectToScreen : NSMakeRect(nsWindowPoint.x, nsWindowPoint.y, 0, 0)]; // OS X screen coordinates
298 qtScreenPoint = QPoint(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y)); // Qt screen coordinates
302 NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
303 qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
305 ulong timestamp = [theEvent timestamp] * 1000;
307 QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
308 nativeDrag->setLastMouseEvent(theEvent, self);
310 QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
313 - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
315 // get m_buttons in sync
316 NSEventType ty = [theEvent type];
318 case NSLeftMouseDown:
319 m_buttons |= Qt::LeftButton;
322 m_buttons &= QFlag(~int(Qt::LeftButton));
324 case NSRightMouseDown:
325 m_buttons |= Qt::RightButton;
328 m_buttons &= QFlag(~int(Qt::RightButton));
334 NSWindow *window = [self window];
335 int windowHeight = [window frame].size.height;
336 NSPoint windowPoint = [theEvent locationInWindow];
337 NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
338 QPoint qtWindowPoint = QPoint(windowPoint.x, windowHeight - windowPoint.y);
339 NSPoint screenPoint = [window convertBaseToScreen : windowPoint];
340 QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
342 ulong timestamp = [theEvent timestamp] * 1000;
343 QWindowSystemInterface::handleFrameStrutMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
346 - (void)mouseDown:(NSEvent *)theEvent
348 if (m_platformWindow->m_activePopupWindow) {
349 QWindowSystemInterface::handleSynchronousCloseEvent(m_platformWindow->m_activePopupWindow);
350 m_platformWindow->m_activePopupWindow = 0;
352 if ([self hasMarkedText]) {
353 NSInputManager* inputManager = [NSInputManager currentInputManager];
354 if ([inputManager wantsToHandleMouseEvents]) {
355 [inputManager handleMouseEvent:theEvent];
358 m_buttons |= Qt::LeftButton;
359 [self handleMouseEvent:theEvent];
363 - (void)mouseDragged:(NSEvent *)theEvent
365 if (!(m_buttons & Qt::LeftButton))
366 qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
367 [self handleMouseEvent:theEvent];
370 - (void)mouseUp:(NSEvent *)theEvent
372 m_buttons &= QFlag(~int(Qt::LeftButton));
373 [self handleMouseEvent:theEvent];
376 - (void)mouseMoved:(NSEvent *)theEvent
378 [self handleMouseEvent:theEvent];
381 - (void)mouseEntered:(NSEvent *)theEvent
384 QWindowSystemInterface::handleEnterEvent(m_window);
387 - (void)mouseExited:(NSEvent *)theEvent
390 QWindowSystemInterface::handleLeaveEvent(m_window);
393 - (void)rightMouseDown:(NSEvent *)theEvent
395 m_buttons |= Qt::RightButton;
396 [self handleMouseEvent:theEvent];
399 - (void)rightMouseDragged:(NSEvent *)theEvent
401 if (!(m_buttons & Qt::RightButton))
402 qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
403 [self handleMouseEvent:theEvent];
406 - (void)rightMouseUp:(NSEvent *)theEvent
408 m_buttons &= QFlag(~int(Qt::RightButton));
409 [self handleMouseEvent:theEvent];
412 - (void)otherMouseDown:(NSEvent *)theEvent
414 switch ([theEvent buttonNumber]) {
416 m_buttons |= Qt::MiddleButton;
419 m_buttons |= Qt::ExtraButton1; // AKA Qt::BackButton
422 m_buttons |= Qt::ExtraButton2; // AKA Qt::ForwardButton
425 m_buttons |= Qt::ExtraButton3;
428 m_buttons |= Qt::ExtraButton4;
431 m_buttons |= Qt::ExtraButton5;
434 m_buttons |= Qt::ExtraButton6;
437 m_buttons |= Qt::ExtraButton7;
440 m_buttons |= Qt::ExtraButton8;
443 m_buttons |= Qt::ExtraButton9;
446 m_buttons |= Qt::ExtraButton10;
449 m_buttons |= Qt::ExtraButton11;
452 m_buttons |= Qt::ExtraButton12;
455 m_buttons |= Qt::ExtraButton13;
458 m_buttons |= Qt::MiddleButton;
461 [self handleMouseEvent:theEvent];
464 - (void)otherMouseDragged:(NSEvent *)theEvent
466 if (!(m_buttons & ~(Qt::LeftButton | Qt::RightButton)))
467 qWarning("QNSView otherMouseDragged: Internal mouse button tracking invalid (missing Qt::MiddleButton or Qt::ExtraButton*)");
468 [self handleMouseEvent:theEvent];
471 - (void)otherMouseUp:(NSEvent *)theEvent
473 switch ([theEvent buttonNumber]) {
475 m_buttons &= QFlag(~int(Qt::MiddleButton));
478 m_buttons &= QFlag(~int(Qt::ExtraButton1)); // AKA Qt::BackButton
481 m_buttons &= QFlag(~int(Qt::ExtraButton2)); // AKA Qt::ForwardButton
484 m_buttons &= QFlag(~int(Qt::ExtraButton3));
487 m_buttons &= QFlag(~int(Qt::ExtraButton4));
490 m_buttons &= QFlag(~int(Qt::ExtraButton5));
493 m_buttons &= QFlag(~int(Qt::ExtraButton6));
496 m_buttons &= QFlag(~int(Qt::ExtraButton7));
499 m_buttons &= QFlag(~int(Qt::ExtraButton8));
502 m_buttons &= QFlag(~int(Qt::ExtraButton9));
505 m_buttons &= QFlag(~int(Qt::ExtraButton10));
508 m_buttons &= QFlag(~int(Qt::ExtraButton11));
511 m_buttons &= QFlag(~int(Qt::ExtraButton12));
514 m_buttons &= QFlag(~int(Qt::ExtraButton13));
517 m_buttons &= QFlag(~int(Qt::MiddleButton));
520 [self handleMouseEvent:theEvent];
523 - (void)touchesBeganWithEvent:(NSEvent *)event
525 const NSTimeInterval timestamp = [event timestamp];
526 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
527 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
530 - (void)touchesMovedWithEvent:(NSEvent *)event
532 const NSTimeInterval timestamp = [event timestamp];
533 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
534 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
537 - (void)touchesEndedWithEvent:(NSEvent *)event
539 const NSTimeInterval timestamp = [event timestamp];
540 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
541 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
544 - (void)touchesCancelledWithEvent:(NSEvent *)event
546 const NSTimeInterval timestamp = [event timestamp];
547 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
548 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
551 #ifndef QT_NO_WHEELEVENT
552 - (void)scrollWheel:(NSEvent *)theEvent
554 const EventRef carbonEvent = (EventRef)[theEvent eventRef];
555 const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
556 const bool scrollEvent = carbonEventKind == kEventMouseScroll;
560 // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad).
561 // Since deviceDelta is delivered as pixels rather than degrees, we need to
562 // convert from pixels to degrees in a sensible manner.
563 // It looks like 1/4 degrees per pixel behaves most native.
564 // (NB: Qt expects the unit for delta to be 8 per degree):
565 const int pixelsToDegrees = 2; // 8 * 1/4
567 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
568 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
569 angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees);
570 angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees);
574 angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
575 angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
579 // carbonEventKind == kEventMouseWheelMoved
580 // Remove acceleration, and use either -120 or 120 as delta:
581 angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120));
582 angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120));
586 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
587 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
588 if ([theEvent hasPreciseScrollingDeltas]) {
589 pixelDelta.setX([theEvent scrollingDeltaX]);
590 pixelDelta.setY([theEvent scrollingDeltaY]);
592 // docs: "In the case of !hasPreciseScrollingDeltas, multiply the delta with the line width."
593 // scrollingDeltaX seems to return a minimum value of 0.1 in this case, map that to two pixels.
594 const CGFloat lineWithEstimate = 20.0;
595 pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate);
596 pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate);
602 NSPoint windowPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
603 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
604 NSTimeInterval timestamp = [theEvent timestamp];
605 ulong qt_timestamp = timestamp * 1000;
607 // Set keyboard modifiers depending on event phase. A two-finger trackpad flick
608 // generates a stream of scroll events. We want the keyboard modifier state to
609 // be the state at the beginning of the flick in order to avoid changing the
610 // interpretation of the events mid-stream. One example of this happening would
611 // be when pressing cmd after scrolling in Qt Creator: not taking the phase into
612 // account causes the end of the event stream to be interpreted as font size changes.
614 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
615 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
616 NSEventPhase phase = [theEvent phase];
617 if (phase == NSEventPhaseBegan) {
618 currentWheelModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
621 QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
623 if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
624 currentWheelModifiers = Qt::NoModifier;
628 QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
629 [self convertKeyModifiers:[theEvent modifierFlags]]);
633 #endif //QT_NO_WHEELEVENT
635 - (int) convertKeyCode : (QChar)keyChar
637 return qt_mac_cocoaKey2QtKey(keyChar);
640 - (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
642 Qt::KeyboardModifiers qtMods =Qt::NoModifier;
643 if (modifierFlags & NSShiftKeyMask)
644 qtMods |= Qt::ShiftModifier;
645 if (modifierFlags & NSControlKeyMask)
646 qtMods |= Qt::MetaModifier;
647 if (modifierFlags & NSAlternateKeyMask)
648 qtMods |= Qt::AltModifier;
649 if (modifierFlags & NSCommandKeyMask)
650 qtMods |= Qt::ControlModifier;
651 if (modifierFlags & NSNumericPadKeyMask)
652 qtMods |= Qt::KeypadModifier;
656 - (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
658 ulong timestamp = [nsevent timestamp] * 1000;
659 Qt::KeyboardModifiers modifiers = [self convertKeyModifiers:[nsevent modifierFlags]];
660 NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
664 if ([charactersIgnoringModifiers length] > 0) {
665 // convert the first character into a key code
666 ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
667 keyCode = [self convertKeyCode:ch];
669 // might be a dead key
670 ch = QChar::ReplacementCharacter;
671 keyCode = Qt::Key_unknown;
674 // we will send a key event unless the input method sets m_sendKeyEvent to false
675 m_sendKeyEvent = true;
678 if (eventType == QEvent::KeyPress) {
679 // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
680 // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
681 if ([charactersIgnoringModifiers length] == 1 && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff))
682 text = QCFString::toQString([nsevent characters]);
684 if (m_composingText.isEmpty())
685 m_sendKeyEvent = !QWindowSystemInterface::tryHandleSynchronousShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
687 QObject *fo = QGuiApplication::focusObject();
688 if (m_sendKeyEvent && fo) {
689 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
690 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
691 bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool();
692 Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
693 if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText)) {
694 // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call
695 [self interpretKeyEvents:[NSArray arrayWithObject:nsevent]];
701 if (m_sendKeyEvent && m_composingText.isEmpty())
702 QWindowSystemInterface::handleKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers, text);
704 m_sendKeyEvent = false;
707 - (void)keyDown:(NSEvent *)nsevent
709 [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
712 - (void)keyUp:(NSEvent *)nsevent
714 [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
717 - (void)flagsChanged:(NSEvent *)nsevent
719 ulong timestamp = [nsevent timestamp] * 1000;
720 ulong modifiers = [nsevent modifierFlags];
721 Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
723 // calculate the delta and remember the current modifiers for next time
724 static ulong m_lastKnownModifiers;
725 ulong lastKnownModifiers = m_lastKnownModifiers;
726 ulong delta = lastKnownModifiers ^ modifiers;
727 m_lastKnownModifiers = modifiers;
729 struct qt_mac_enum_mapper
734 static qt_mac_enum_mapper modifier_key_symbols[] = {
735 { NSShiftKeyMask, Qt::Key_Shift },
736 { NSControlKeyMask, Qt::Key_Meta },
737 { NSCommandKeyMask, Qt::Key_Control },
738 { NSAlternateKeyMask, Qt::Key_Alt },
739 { NSAlphaShiftKeyMask, Qt::Key_CapsLock },
740 { 0ul, Qt::Key_unknown } };
741 for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) {
742 uint mac_mask = modifier_key_symbols[i].mac_mask;
743 if ((delta & mac_mask) == 0u)
746 QWindowSystemInterface::handleKeyEvent(m_window,
748 (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
749 modifier_key_symbols[i].qt_code,
754 - (void) doCommandBySelector:(SEL)aSelector
756 [self tryToPerform:aSelector with:self];
759 - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
761 Q_UNUSED(replacementRange)
763 if (m_sendKeyEvent && m_composingText.isEmpty()) {
764 // don't send input method events for simple text input (let handleKeyEvent send key events instead)
768 QString commitString;
769 if ([aString length]) {
770 if ([aString isKindOfClass:[NSAttributedString class]]) {
771 commitString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
773 commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
776 QObject *fo = QGuiApplication::focusObject();
778 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
779 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
780 if (queryEvent.value(Qt::ImEnabled).toBool()) {
782 e.setCommitString(commitString);
783 QCoreApplication::sendEvent(fo, &e);
784 // prevent handleKeyEvent from sending a key event
785 m_sendKeyEvent = false;
790 m_composingText.clear();
793 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
795 Q_UNUSED(replacementRange)
796 QString preeditString;
798 QList<QInputMethodEvent::Attribute> attrs;
799 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
801 if ([aString isKindOfClass:[NSAttributedString class]]) {
802 // Preedit string has attribution
803 preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
804 int composingLength = preeditString.length();
806 // Create attributes for individual sections of preedit text
807 while (index < composingLength) {
808 NSRange effectiveRange;
809 NSRange range = NSMakeRange(index, composingLength-index);
810 NSDictionary *attributes = [aString attributesAtIndex:index
811 longestEffectiveRange:&effectiveRange
813 NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
814 if (underlineStyle) {
815 QColor clr (Qt::black);
816 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
818 clr = qt_mac_toQColor(color);
820 QTextCharFormat format;
821 format.setFontUnderline(true);
822 format.setUnderlineColor(clr);
823 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
824 effectiveRange.location,
825 effectiveRange.length,
828 index = effectiveRange.location + effectiveRange.length;
831 // No attributes specified, take only the preedit text.
832 preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
835 if (attrs.isEmpty()) {
836 QTextCharFormat format;
837 format.setFontUnderline(true);
838 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
839 0, preeditString.length(), format);
842 m_composingText = preeditString;
844 QObject *fo = QGuiApplication::focusObject();
846 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
847 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
848 if (queryEvent.value(Qt::ImEnabled).toBool()) {
849 QInputMethodEvent e(preeditString, attrs);
850 QCoreApplication::sendEvent(fo, &e);
851 // prevent handleKeyEvent from sending a key event
852 m_sendKeyEvent = false;
860 if (!m_composingText.isEmpty()) {
861 QObject *fo = QGuiApplication::focusObject();
863 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
864 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
865 if (queryEvent.value(Qt::ImEnabled).toBool()) {
867 e.setCommitString(m_composingText);
868 QCoreApplication::sendEvent(fo, &e);
873 m_composingText.clear();
876 - (BOOL) hasMarkedText
878 return (m_composingText.isEmpty() ? NO: YES);
881 - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
883 Q_UNUSED(actualRange)
884 QObject *fo = QGuiApplication::focusObject();
887 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
888 if (!QCoreApplication::sendEvent(fo, &queryEvent))
890 if (!queryEvent.value(Qt::ImEnabled).toBool())
893 QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
894 if (selectedText.isEmpty())
897 QCFString string(selectedText.mid(aRange.location, aRange.length));
898 const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
899 return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
902 - (NSRange) markedRange
905 if (!m_composingText.isEmpty()) {
907 range.length = m_composingText.length();
909 range.location = NSNotFound;
915 - (NSRange) selectedRange
917 NSRange selectedRange = {NSNotFound, 0};
918 selectedRange.location = NSNotFound;
919 selectedRange.length = 0;
921 QObject *fo = QGuiApplication::focusObject();
923 return selectedRange;
924 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
925 if (!QCoreApplication::sendEvent(fo, &queryEvent))
926 return selectedRange;
927 if (!queryEvent.value(Qt::ImEnabled).toBool())
928 return selectedRange;
930 QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
932 if (!selectedText.isEmpty()) {
933 selectedRange.location = 0;
934 selectedRange.length = selectedText.length();
936 return selectedRange;
939 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
942 Q_UNUSED(actualRange)
943 QObject *fo = QGuiApplication::focusObject();
947 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
948 if (!QCoreApplication::sendEvent(fo, &queryEvent))
950 if (!queryEvent.value(Qt::ImEnabled).toBool())
956 // The returned rect is always based on the internal cursor.
957 QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
958 QPoint mp = m_window->mapToGlobal(mr.bottomLeft());
961 rect.origin.x = mp.x();
962 rect.origin.y = qt_mac_flipYCoordinate(mp.y());
963 rect.size.width = mr.width();
964 rect.size.height = mr.height();
968 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
970 // We dont support cursor movements using mouse while composing.
975 - (NSArray*) validAttributesForMarkedText
977 if (m_window != QGuiApplication::focusWindow())
980 QObject *fo = QGuiApplication::focusObject();
984 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
985 if (!QCoreApplication::sendEvent(fo, &queryEvent))
987 if (!queryEvent.value(Qt::ImEnabled).toBool())
990 // Support only underline color/style.
991 return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
992 NSUnderlineStyleAttributeName, nil];
995 -(void)registerDragTypes
997 QCocoaAutoReleasePool pool;
998 // ### Custom types disabled.
999 QStringList customTypes; // = qEnabledDraggedTypes();
1000 if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
1001 if (currentCustomDragTypes == 0)
1002 currentCustomDragTypes = new QStringList();
1003 *currentCustomDragTypes = customTypes;
1004 const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
1005 NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
1006 NSFilenamesPboardType, NSStringPboardType,
1007 NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
1008 NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
1009 NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
1010 NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
1011 NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
1012 NSFilesPromisePboardType, NSInkTextPboardType,
1013 NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
1014 // Add custom types supported by the application.
1015 for (int i = 0; i < customTypes.size(); i++) {
1016 [supportedTypes addObject:QCFString::toNSString(customTypes[i])];
1018 [self registerForDraggedTypes:supportedTypes];
1022 - (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal
1025 QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1026 return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
1029 - (BOOL) ignoreModifierKeysWhileDragging
1034 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1036 return [self handleDrag : sender];
1039 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1041 return [self handleDrag : sender];
1044 // Sends drag update to Qt, return the action
1045 - (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
1047 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
1048 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
1049 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
1050 QCocoaDropData mimeData([sender draggingPasteboard]);
1052 // update these so selecting move/copy/link works
1053 QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
1055 QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
1056 return qt_mac_mapDropAction(response.acceptedAction());
1059 - (void)draggingExited:(id <NSDraggingInfo>)sender
1061 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
1062 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
1064 // Send 0 mime data to indicate drag exit
1065 QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
1068 // called on drop, send the drop to Qt and return if it was accepted.
1069 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1071 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
1072 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
1073 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
1074 QCocoaDropData mimeData([sender draggingPasteboard]);
1076 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
1077 return response.isAccepted();
1080 - (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation
1082 QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1083 nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
1085 // keep our state, and QGuiApplication state (buttons member) in-sync,
1086 // or future mouse events will be processed incorrectly
1087 m_buttons &= QFlag(~int(Qt::LeftButton));
1089 NSPoint windowPoint = [self convertPoint: point fromView: nil];
1090 QPoint qtWindowPoint(point.x, point.y);
1092 NSWindow *window = [self window];
1093 NSPoint screenPoint = [window convertBaseToScreen :point];
1094 QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
1096 QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons);