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"
53 #include <QtGui/QWindowSystemInterface>
54 #include <QtGui/QTextFormat>
55 #include <QtCore/QDebug>
57 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
58 #include <accessibilityinspector.h>
61 static QTouchDevice *touchDevice = 0;
63 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
64 - (CGFloat)deviceDeltaX;
65 - (CGFloat)deviceDeltaY;
66 - (CGFloat)deviceDeltaZ;
69 @implementation QNSView
73 self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
77 m_buttons = Qt::NoButton;
78 currentCustomDragTypes = 0;
80 touchDevice = new QTouchDevice;
81 touchDevice->setType(QTouchDevice::TouchPad);
82 touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
83 QWindowSystemInterface::registerTouchDevice(touchDevice);
89 - (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
96 m_platformWindow = platformWindow;
98 m_keyEventsAccepted = false;
100 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
101 // prevent rift in space-time continuum, disable
102 // accessibility for the accessibility inspector's windows.
103 static bool skipAccessibilityForInspectorWindows = false;
104 if (!skipAccessibilityForInspectorWindows) {
106 m_accessibleRoot = window->accessibleRoot();
108 AccessibilityInspector *inspector = new AccessibilityInspector(window);
109 skipAccessibilityForInspectorWindows = true;
110 inspector->inspectWindow(window);
111 skipAccessibilityForInspectorWindows = false;
114 m_accessibleRoot = window->accessibleRoot();
117 [self registerDragTypes];
118 [self setPostsFrameChangedNotifications : YES];
119 [[NSNotificationCenter defaultCenter] addObserver:self
120 selector:@selector(updateGeometry)
121 name:NSViewFrameDidChangeNotification
127 - (void)updateGeometry
129 NSRect rect = [self frame];
130 NSRect windowRect = [[self window] frame];
131 QRect geo(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height);
133 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
134 qDebug() << "QNSView::udpateGeometry" << geo;
137 // Call setGeometry on QPlatformWindow. (not on QCocoaWindow,
138 // doing that will initiate a geometry change it and possibly create
139 // an infinite loop when this notification is triggered again.)
140 m_platformWindow->QPlatformWindow::setGeometry(geo);
142 // Send a geometry change event to Qt, if it's ready to handle events
143 if (!m_platformWindow->m_inConstructor)
144 QWindowSystemInterface::handleSynchronousGeometryChange(m_window, geo);
147 - (void)windowNotification : (NSNotification *) windowNotification
149 //qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
151 NSString *notificationName = [windowNotification name];
152 if (notificationName == NSWindowDidBecomeKeyNotification) {
153 if (!m_platformWindow->windowIsPopupType())
154 QWindowSystemInterface::handleWindowActivated(m_window);
155 } else if (notificationName == NSWindowDidResignKeyNotification) {
156 if (!m_platformWindow->windowIsPopupType())
157 QWindowSystemInterface::handleWindowActivated(0);
158 } else if (notificationName == NSWindowDidMiniaturizeNotification) {
159 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowMinimized);
160 } else if (notificationName == NSWindowDidDeminiaturizeNotification) {
161 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
162 // Qt expects an expose event after restore/deminiaturize. This also needs
163 // to be a non-synchronous event to make sure it gets processed after
164 // the state change event sent above.
165 QWindowSystemInterface::handleExposeEvent(m_window, QRegion(m_window->geometry()));
168 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
169 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
170 if (notificationName == NSWindowDidEnterFullScreenNotification) {
171 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowFullScreen);
172 } else if (notificationName == NSWindowDidExitFullScreenNotification) {
173 QWindowSystemInterface::handleWindowStateChanged(m_window, Qt::WindowNoState);
181 - (void) setImage:(QImage *)image
183 CGImageRelease(m_cgImage);
185 const uchar *imageData = image->bits();
186 int bitDepth = image->depth();
187 int colorBufferSize = 8;
188 int bytesPrLine = image->bytesPerLine();
189 int width = image->width();
190 int height = image->height();
192 CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB();
194 CGDataProviderRef cgDataProviderRef = CGDataProviderCreateWithData(
200 m_cgImage = CGImageCreate(width,
206 kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
210 kCGRenderingIntentDefault);
212 CGColorSpaceRelease(cgColourSpaceRef);
216 - (void) drawRect:(NSRect)dirtyRect
221 CGRect dirtyCGRect = NSRectToCGRect(dirtyRect);
223 NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
224 CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
226 CGContextSaveGState( cgContext );
227 int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect);
228 CGContextTranslateCTM(cgContext, 0, dy);
229 CGContextScaleCTM(cgContext, 1, -1);
231 CGImageRef subImage = CGImageCreateWithImageInRect(m_cgImage, dirtyCGRect);
232 CGContextDrawImage(cgContext,dirtyCGRect,subImage);
234 CGContextRestoreGState(cgContext);
236 CGImageRelease(subImage);
245 - (BOOL)acceptsFirstResponder
250 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
256 - (void)handleMouseEvent:(NSEvent *)theEvent
258 // Calculate the mouse position in the QWindow and Qt screen coordinate system,
259 // starting from coordinates in the NSWindow coordinate system.
261 // This involves translating according to the window location on screen,
262 // as well as inverting the y coordinate due to the origin change.
264 // Coordinate system overview, outer to innermost:
268 // OS X screen bottom-left
269 // Qt screen top-left
270 // NSWindow bottom-left
271 // NSView/QWindow top-left
273 // NSView and QWindow are equal coordinate systems: the QWindow covers the
274 // entire NSView, and we've set the NSView's isFlipped property to true.
276 NSPoint nsWindowPoint = [theEvent locationInWindow]; // NSWindow coordinates
278 NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
279 QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
281 QPoint qtScreenPoint;
283 NSWindow *window = [self window];
284 // Use convertRectToScreen if available (added in 10.7).
285 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
286 if ([window respondsToSelector:@selector(convertRectToScreen:)]) {
287 NSRect screenRect = [window convertRectToScreen : NSMakeRect(nsWindowPoint.x, nsWindowPoint.y, 0, 0)]; // OS X screen coordinates
288 qtScreenPoint = QPoint(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y)); // Qt screen coordinates
292 NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
293 qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
295 ulong timestamp = [theEvent timestamp] * 1000;
296 QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
299 - (void)mouseDown:(NSEvent *)theEvent
301 if (m_platformWindow->m_activePopupWindow) {
302 QWindowSystemInterface::handleSynchronousCloseEvent(m_platformWindow->m_activePopupWindow);
303 m_platformWindow->m_activePopupWindow = 0;
305 if ([self hasMarkedText]) {
306 NSInputManager* inputManager = [NSInputManager currentInputManager];
307 if ([inputManager wantsToHandleMouseEvents]) {
308 [inputManager handleMouseEvent:theEvent];
311 m_buttons |= Qt::LeftButton;
312 [self handleMouseEvent:theEvent];
316 - (void)mouseDragged:(NSEvent *)theEvent
318 if (!(m_buttons & Qt::LeftButton))
319 qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
320 [self handleMouseEvent:theEvent];
323 - (void)mouseUp:(NSEvent *)theEvent
325 m_buttons &= QFlag(~int(Qt::LeftButton));
326 [self handleMouseEvent:theEvent];
329 - (void)mouseMoved:(NSEvent *)theEvent
331 [self handleMouseEvent:theEvent];
334 - (void)mouseEntered:(NSEvent *)theEvent
337 QWindowSystemInterface::handleEnterEvent(m_window);
340 - (void)mouseExited:(NSEvent *)theEvent
343 QWindowSystemInterface::handleLeaveEvent(m_window);
346 - (void)rightMouseDown:(NSEvent *)theEvent
348 m_buttons |= Qt::RightButton;
349 [self handleMouseEvent:theEvent];
352 - (void)rightMouseDragged:(NSEvent *)theEvent
354 if (!(m_buttons & Qt::RightButton))
355 qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
356 [self handleMouseEvent:theEvent];
359 - (void)rightMouseUp:(NSEvent *)theEvent
361 m_buttons &= QFlag(~int(Qt::RightButton));
362 [self handleMouseEvent:theEvent];
365 - (void)otherMouseDown:(NSEvent *)theEvent
367 switch ([theEvent buttonNumber]) {
369 m_buttons |= Qt::MiddleButton;
372 m_buttons |= Qt::ExtraButton1; // AKA Qt::BackButton
375 m_buttons |= Qt::ExtraButton2; // AKA Qt::ForwardButton
378 m_buttons |= Qt::ExtraButton3;
381 m_buttons |= Qt::ExtraButton4;
384 m_buttons |= Qt::ExtraButton5;
387 m_buttons |= Qt::ExtraButton6;
390 m_buttons |= Qt::ExtraButton7;
393 m_buttons |= Qt::ExtraButton8;
396 m_buttons |= Qt::ExtraButton9;
399 m_buttons |= Qt::ExtraButton10;
402 m_buttons |= Qt::ExtraButton11;
405 m_buttons |= Qt::ExtraButton12;
408 m_buttons |= Qt::ExtraButton13;
411 m_buttons |= Qt::MiddleButton;
414 [self handleMouseEvent:theEvent];
417 - (void)otherMouseDragged:(NSEvent *)theEvent
419 if (!(m_buttons & ~(Qt::LeftButton | Qt::RightButton)))
420 qWarning("QNSView otherMouseDragged: Internal mouse button tracking invalid (missing Qt::MiddleButton or Qt::ExtraButton*)");
421 [self handleMouseEvent:theEvent];
424 - (void)otherMouseUp:(NSEvent *)theEvent
426 switch ([theEvent buttonNumber]) {
428 m_buttons &= QFlag(~int(Qt::MiddleButton));
431 m_buttons &= QFlag(~int(Qt::ExtraButton1)); // AKA Qt::BackButton
434 m_buttons &= QFlag(~int(Qt::ExtraButton2)); // AKA Qt::ForwardButton
437 m_buttons &= QFlag(~int(Qt::ExtraButton3));
440 m_buttons &= QFlag(~int(Qt::ExtraButton4));
443 m_buttons &= QFlag(~int(Qt::ExtraButton5));
446 m_buttons &= QFlag(~int(Qt::ExtraButton6));
449 m_buttons &= QFlag(~int(Qt::ExtraButton7));
452 m_buttons &= QFlag(~int(Qt::ExtraButton8));
455 m_buttons &= QFlag(~int(Qt::ExtraButton9));
458 m_buttons &= QFlag(~int(Qt::ExtraButton10));
461 m_buttons &= QFlag(~int(Qt::ExtraButton11));
464 m_buttons &= QFlag(~int(Qt::ExtraButton12));
467 m_buttons &= QFlag(~int(Qt::ExtraButton13));
470 m_buttons &= QFlag(~int(Qt::MiddleButton));
473 [self handleMouseEvent:theEvent];
476 - (void)touchesBeganWithEvent:(NSEvent *)event
478 const NSTimeInterval timestamp = [event timestamp];
479 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
480 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
483 - (void)touchesMovedWithEvent:(NSEvent *)event
485 const NSTimeInterval timestamp = [event timestamp];
486 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
487 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
490 - (void)touchesEndedWithEvent:(NSEvent *)event
492 const NSTimeInterval timestamp = [event timestamp];
493 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
494 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
497 - (void)touchesCancelledWithEvent:(NSEvent *)event
499 const NSTimeInterval timestamp = [event timestamp];
500 const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false);
501 QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points);
504 #ifndef QT_NO_WHEELEVENT
505 - (void)scrollWheel:(NSEvent *)theEvent
507 const EventRef carbonEvent = (EventRef)[theEvent eventRef];
508 const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
509 const bool scrollEvent = carbonEventKind == kEventMouseScroll;
513 // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad).
514 // Since deviceDelta is delivered as pixels rather than degrees, we need to
515 // convert from pixels to degrees in a sensible manner.
516 // It looks like 1/4 degrees per pixel behaves most native.
517 // (NB: Qt expects the unit for delta to be 8 per degree):
518 const int pixelsToDegrees = 2; // 8 * 1/4
520 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
521 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
522 angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees);
523 angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees);
527 angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
528 angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
532 // carbonEventKind == kEventMouseWheelMoved
533 // Remove acceleration, and use either -120 or 120 as delta:
534 angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120));
535 angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120));
539 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
540 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
541 if ([theEvent hasPreciseScrollingDeltas]) {
542 pixelDelta.setX([theEvent scrollingDeltaX]);
543 pixelDelta.setY([theEvent scrollingDeltaY]);
545 // docs: "In the case of !hasPreciseScrollingDeltas, multiply the delta with the line width."
546 // scrollingDeltaX seems to return a minimum value of 0.1 in this case, map that to two pixels.
547 const CGFloat lineWithEstimate = 20.0;
548 pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate);
549 pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate);
555 NSPoint windowPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
556 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
557 NSTimeInterval timestamp = [theEvent timestamp];
558 ulong qt_timestamp = timestamp * 1000;
560 // Set keyboard modifiers depending on event phase. A two-finger trackpad flick
561 // generates a stream of scroll events. We want the keyboard modifier state to
562 // be the state at the beginning of the flick in order to avoid changing the
563 // interpretation of the events mid-stream. One example of this happening would
564 // be when pressing cmd after scrolling in Qt Creator: not taking the phase into
565 // account causes the end of the event stream to be interpreted as font size changes.
567 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
568 if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
569 NSEventPhase phase = [theEvent phase];
570 if (phase == NSEventPhaseBegan) {
571 currentWheelModifiers = [self convertKeyModifiers:[theEvent modifierFlags]];
574 QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
576 if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
577 currentWheelModifiers = Qt::NoModifier;
581 QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
582 [self convertKeyModifiers:[theEvent modifierFlags]]);
586 #endif //QT_NO_WHEELEVENT
588 - (int) convertKeyCode : (QChar)keyChar
590 return qt_mac_cocoaKey2QtKey(keyChar);
593 - (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
595 Qt::KeyboardModifiers qtMods =Qt::NoModifier;
596 if (modifierFlags & NSShiftKeyMask)
597 qtMods |= Qt::ShiftModifier;
598 if (modifierFlags & NSControlKeyMask)
599 qtMods |= Qt::MetaModifier;
600 if (modifierFlags & NSAlternateKeyMask)
601 qtMods |= Qt::AltModifier;
602 if (modifierFlags & NSCommandKeyMask)
603 qtMods |= Qt::ControlModifier;
604 if (modifierFlags & NSNumericPadKeyMask)
605 qtMods |= Qt::KeypadModifier;
609 - (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
611 ulong timestamp = [nsevent timestamp] * 1000;
612 Qt::KeyboardModifiers modifiers = [self convertKeyModifiers:[nsevent modifierFlags]];
613 NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
614 QChar ch([charactersIgnoringModifiers characterAtIndex:0]);
615 int keyCode = [self convertKeyCode:ch];
618 if (eventType == QEvent::KeyPress) {
619 // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
620 // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
621 if ([charactersIgnoringModifiers length] == 1 && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff))
622 text = QString::fromUtf8([[nsevent characters] UTF8String]);
624 if (!m_keyEventsAccepted && m_composingText.isEmpty())
625 m_keyEventsAccepted = QWindowSystemInterface::tryHandleSynchronousShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
627 QObject *fo = QGuiApplication::focusObject();
628 if (!m_keyEventsAccepted && fo) {
629 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
630 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
631 bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool();
632 Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
633 if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText))
634 [self interpretKeyEvents:[NSArray arrayWithObject:nsevent]];
638 if (!m_keyEventsAccepted && m_composingText.isEmpty())
639 QWindowSystemInterface::handleKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers, text);
642 - (void)keyDown:(NSEvent *)nsevent
644 m_keyEventsAccepted = false;
645 [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
648 - (void)keyUp:(NSEvent *)nsevent
650 [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
653 - (void)flagsChanged:(NSEvent *)nsevent
655 ulong timestamp = [nsevent timestamp] * 1000;
656 ulong modifiers = [nsevent modifierFlags];
657 Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
659 // calculate the delta and remember the current modifiers for next time
660 static ulong m_lastKnownModifiers;
661 ulong lastKnownModifiers = m_lastKnownModifiers;
662 ulong delta = lastKnownModifiers ^ modifiers;
663 m_lastKnownModifiers = modifiers;
665 struct qt_mac_enum_mapper
670 static qt_mac_enum_mapper modifier_key_symbols[] = {
671 { NSShiftKeyMask, Qt::Key_Shift },
672 { NSControlKeyMask, Qt::Key_Meta },
673 { NSCommandKeyMask, Qt::Key_Control },
674 { NSAlternateKeyMask, Qt::Key_Alt },
675 { NSAlphaShiftKeyMask, Qt::Key_CapsLock },
676 { 0ul, Qt::Key_unknown } };
677 for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) {
678 uint mac_mask = modifier_key_symbols[i].mac_mask;
679 if ((delta & mac_mask) == 0u)
682 QWindowSystemInterface::handleKeyEvent(m_window,
684 (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
685 modifier_key_symbols[i].qt_code,
690 - (void) doCommandBySelector:(SEL)aSelector
692 [self tryToPerform:aSelector with:self];
695 - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
697 Q_UNUSED(replacementRange)
698 QString commitString;
699 if ([aString length]) {
700 if ([aString isKindOfClass:[NSAttributedString class]]) {
701 commitString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
703 commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
706 QObject *fo = QGuiApplication::focusObject();
708 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
709 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
710 if (queryEvent.value(Qt::ImEnabled).toBool()) {
712 e.setCommitString(commitString);
713 QCoreApplication::sendEvent(fo, &e);
714 m_keyEventsAccepted = true;
719 m_composingText.clear();
722 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
724 Q_UNUSED(replacementRange)
725 QString preeditString;
727 QList<QInputMethodEvent::Attribute> attrs;
728 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
730 if ([aString isKindOfClass:[NSAttributedString class]]) {
731 // Preedit string has attribution
732 preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
733 int composingLength = preeditString.length();
735 // Create attributes for individual sections of preedit text
736 while (index < composingLength) {
737 NSRange effectiveRange;
738 NSRange range = NSMakeRange(index, composingLength-index);
739 NSDictionary *attributes = [aString attributesAtIndex:index
740 longestEffectiveRange:&effectiveRange
742 NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
743 if (underlineStyle) {
744 QColor clr (Qt::black);
745 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
747 clr = qt_mac_toQColor(color);
749 QTextCharFormat format;
750 format.setFontUnderline(true);
751 format.setUnderlineColor(clr);
752 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
753 effectiveRange.location,
754 effectiveRange.length,
757 index = effectiveRange.location + effectiveRange.length;
760 // No attributes specified, take only the preedit text.
761 preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
764 if (attrs.isEmpty()) {
765 QTextCharFormat format;
766 format.setFontUnderline(true);
767 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
768 0, preeditString.length(), format);
771 m_composingText = preeditString;
773 QObject *fo = QGuiApplication::focusObject();
775 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
776 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
777 if (queryEvent.value(Qt::ImEnabled).toBool()) {
778 QInputMethodEvent e(preeditString, attrs);
779 QCoreApplication::sendEvent(fo, &e);
780 m_keyEventsAccepted = true;
788 if (!m_composingText.isEmpty()) {
789 QObject *fo = QGuiApplication::focusObject();
791 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
792 if (QCoreApplication::sendEvent(fo, &queryEvent)) {
793 if (queryEvent.value(Qt::ImEnabled).toBool()) {
795 e.setCommitString(m_composingText);
796 QCoreApplication::sendEvent(fo, &e);
801 m_composingText.clear();
804 - (BOOL) hasMarkedText
806 return (m_composingText.isEmpty() ? NO: YES);
809 - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
811 Q_UNUSED(actualRange)
812 QObject *fo = QGuiApplication::focusObject();
815 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
816 if (!QCoreApplication::sendEvent(fo, &queryEvent))
818 if (!queryEvent.value(Qt::ImEnabled).toBool())
821 QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
822 if (selectedText.isEmpty())
825 QCFString string(selectedText.mid(aRange.location, aRange.length));
826 const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
827 return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
830 - (NSRange) markedRange
833 if (!m_composingText.isEmpty()) {
835 range.length = m_composingText.length();
837 range.location = NSNotFound;
843 - (NSRange) selectedRange
845 NSRange selectedRange = {NSNotFound, 0};
846 selectedRange.location = NSNotFound;
847 selectedRange.length = 0;
849 QObject *fo = QGuiApplication::focusObject();
851 return selectedRange;
852 QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
853 if (!QCoreApplication::sendEvent(fo, &queryEvent))
854 return selectedRange;
855 if (!queryEvent.value(Qt::ImEnabled).toBool())
856 return selectedRange;
858 QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
860 if (!selectedText.isEmpty()) {
861 selectedRange.location = 0;
862 selectedRange.length = selectedText.length();
864 return selectedRange;
867 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
870 Q_UNUSED(actualRange)
871 QObject *fo = QGuiApplication::focusObject();
875 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
876 if (!QCoreApplication::sendEvent(fo, &queryEvent))
878 if (!queryEvent.value(Qt::ImEnabled).toBool())
884 // The returned rect is always based on the internal cursor.
885 QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
886 QPoint mp = m_window->mapToGlobal(mr.bottomLeft());
889 rect.origin.x = mp.x();
890 rect.origin.y = qt_mac_flipYCoordinate(mp.y());
891 rect.size.width = mr.width();
892 rect.size.height = mr.height();
896 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
898 // We dont support cursor movements using mouse while composing.
903 - (NSArray*) validAttributesForMarkedText
905 if (m_window != QGuiApplication::focusWindow())
908 QObject *fo = QGuiApplication::focusObject();
912 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
913 if (!QCoreApplication::sendEvent(fo, &queryEvent))
915 if (!queryEvent.value(Qt::ImEnabled).toBool())
918 // Support only underline color/style.
919 return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
920 NSUnderlineStyleAttributeName, nil];
923 -(void)registerDragTypes
925 QCocoaAutoReleasePool pool;
926 // ### Custom types disabled.
927 QStringList customTypes; // = qEnabledDraggedTypes();
928 if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
929 if (currentCustomDragTypes == 0)
930 currentCustomDragTypes = new QStringList();
931 *currentCustomDragTypes = customTypes;
932 const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
933 NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
934 NSFilenamesPboardType, NSStringPboardType,
935 NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
936 NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
937 NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
938 NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
939 NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
940 NSFilesPromisePboardType, NSInkTextPboardType,
941 NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
942 // Add custom types supported by the application.
943 for (int i = 0; i < customTypes.size(); i++) {
944 [supportedTypes addObject:QCFString::toNSString(customTypes[i])];
946 [self registerForDraggedTypes:supportedTypes];
950 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
952 return [self handleDrag : sender];
955 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
957 return [self handleDrag : sender];
960 // Sends drag update to Qt, return the action
961 - (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
963 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
964 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
965 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
966 QCocoaDropData mimeData([sender draggingPasteboard]);
968 QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
969 return qt_mac_mapDropAction(response.acceptedAction());
972 - (void)draggingExited:(id <NSDraggingInfo>)sender
974 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
975 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
977 // Send 0 mime data to indicate drag exit
978 QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
981 // called on drop, send the drop to Qt and return if it was accepted.
982 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
984 NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
985 QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
986 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
987 QCocoaDropData mimeData([sender draggingPasteboard]);
989 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
990 return response.isAccepted();