Cocoa implementation of QPA menu interface.
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qnsview.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtCore/qglobal.h>
43
44 #include <Carbon/Carbon.h>
45
46 #include "qnsview.h"
47 #include "qcocoawindow.h"
48 #include "qcocoahelpers.h"
49 #include "qcocoaautoreleasepool.h"
50 #include "qmultitouch_mac_p.h"
51 #include "qcocoadrag.h"
52
53 #include <QtGui/QWindowSystemInterface>
54 #include <QtGui/QTextFormat>
55 #include <QtCore/QDebug>
56
57 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
58 #include <accessibilityinspector.h>
59 #endif
60
61 static QTouchDevice *touchDevice = 0;
62
63 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
64   - (CGFloat)deviceDeltaX;
65   - (CGFloat)deviceDeltaY;
66   - (CGFloat)deviceDeltaZ;
67 @end
68
69 @implementation QNSView
70
71 - (id) init
72 {
73     self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
74     if (self) {
75         m_cgImage = 0;
76         m_window = 0;
77         m_buttons = Qt::NoButton;
78         currentCustomDragTypes = 0;
79         if (!touchDevice) {
80             touchDevice = new QTouchDevice;
81             touchDevice->setType(QTouchDevice::TouchPad);
82             touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
83             QWindowSystemInterface::registerTouchDevice(touchDevice);
84         }
85     }
86     return self;
87 }
88
89 - (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
90 {
91     self = [self init];
92     if (!self)
93         return 0;
94
95     m_window = window;
96     m_platformWindow = platformWindow;
97     m_accessibleRoot = 0;
98     m_keyEventsAccepted = false;
99
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) {
105
106         m_accessibleRoot = window->accessibleRoot();
107
108         AccessibilityInspector *inspector = new AccessibilityInspector(window);
109         skipAccessibilityForInspectorWindows = true;
110         inspector->inspectWindow(window);
111         skipAccessibilityForInspectorWindows = false;
112     }
113 #else
114     m_accessibleRoot = window->accessibleRoot();
115 #endif
116
117     [self registerDragTypes];
118     [self setPostsFrameChangedNotifications : YES];
119     [[NSNotificationCenter defaultCenter] addObserver:self
120                                           selector:@selector(updateGeometry)
121                                           name:NSViewFrameDidChangeNotification
122                                           object:self];
123
124     return self;
125 }
126
127 - (void)updateGeometry
128 {
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);
132
133 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
134     qDebug() << "QNSView::udpateGeometry" << geo;
135 #endif
136
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);
141
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);
145 }
146
147 - (void)windowNotification : (NSNotification *) windowNotification
148 {
149     //qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
150
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()));
166     } else {
167
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);
174         }
175     }
176 #endif
177
178     }
179 }
180
181 - (void) setImage:(QImage *)image
182 {
183     CGImageRelease(m_cgImage);
184
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();
191
192     CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB();
193
194     CGDataProviderRef cgDataProviderRef = CGDataProviderCreateWithData(
195                 NULL,
196                 imageData,
197                 image->byteCount(),
198                 NULL);
199
200     m_cgImage = CGImageCreate(width,
201                               height,
202                               colorBufferSize,
203                               bitDepth,
204                               bytesPrLine,
205                               cgColourSpaceRef,
206                               kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
207                               cgDataProviderRef,
208                               NULL,
209                               false,
210                               kCGRenderingIntentDefault);
211
212     CGColorSpaceRelease(cgColourSpaceRef);
213
214 }
215
216 - (void) drawRect:(NSRect)dirtyRect
217 {
218     if (!m_cgImage)
219         return;
220
221     CGRect dirtyCGRect = NSRectToCGRect(dirtyRect);
222
223     NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
224     CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
225
226     CGContextSaveGState( cgContext );
227     int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect);
228     CGContextTranslateCTM(cgContext, 0, dy);
229     CGContextScaleCTM(cgContext, 1, -1);
230
231     CGImageRef subImage = CGImageCreateWithImageInRect(m_cgImage, dirtyCGRect);
232     CGContextDrawImage(cgContext,dirtyCGRect,subImage);
233
234     CGContextRestoreGState(cgContext);
235
236     CGImageRelease(subImage);
237
238 }
239
240 - (BOOL) isFlipped
241 {
242     return YES;
243 }
244
245 - (BOOL)acceptsFirstResponder
246 {
247     return YES;
248 }
249
250 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
251 {
252     Q_UNUSED(theEvent);
253     return YES;
254 }
255
256 - (void)handleMouseEvent:(NSEvent *)theEvent
257 {
258     // Calculate the mouse position in the QWindow and Qt screen coordinate system,
259     // starting from coordinates in the NSWindow coordinate system.
260     //
261     // This involves translating according to the window location on screen,
262     // as well as inverting the y coordinate due to the origin change.
263     //
264     // Coordinate system overview, outer to innermost:
265     //
266     // Name             Origin
267     //
268     // OS X screen      bottom-left
269     // Qt screen        top-left
270     // NSWindow         bottom-left
271     // NSView/QWindow   top-left
272     //
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.
275
276     NSPoint nsWindowPoint = [theEvent locationInWindow];                    // NSWindow coordinates
277
278     NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
279     QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);                     // NSView/QWindow coordinates
280
281     QPoint qtScreenPoint;
282
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
289     } else
290 #endif
291     {
292         NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
293         qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
294     }
295     ulong timestamp = [theEvent timestamp] * 1000;
296     QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
297 }
298
299 - (void)mouseDown:(NSEvent *)theEvent
300 {
301     if (m_platformWindow->m_activePopupWindow) {
302         QWindowSystemInterface::handleSynchronousCloseEvent(m_platformWindow->m_activePopupWindow);
303         m_platformWindow->m_activePopupWindow = 0;
304     }
305     if ([self hasMarkedText]) {
306         NSInputManager* inputManager = [NSInputManager currentInputManager];
307         if ([inputManager wantsToHandleMouseEvents]) {
308             [inputManager handleMouseEvent:theEvent];
309         }
310     } else {
311         m_buttons |= Qt::LeftButton;
312         [self handleMouseEvent:theEvent];
313     }
314 }
315
316 - (void)mouseDragged:(NSEvent *)theEvent
317 {
318     if (!(m_buttons & Qt::LeftButton))
319         qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
320     [self handleMouseEvent:theEvent];
321 }
322
323 - (void)mouseUp:(NSEvent *)theEvent
324 {
325     m_buttons &= QFlag(~int(Qt::LeftButton));
326     [self handleMouseEvent:theEvent];
327 }
328
329 - (void)mouseMoved:(NSEvent *)theEvent
330 {
331     [self handleMouseEvent:theEvent];
332 }
333
334 - (void)mouseEntered:(NSEvent *)theEvent
335 {
336     Q_UNUSED(theEvent);
337     QWindowSystemInterface::handleEnterEvent(m_window);
338 }
339
340 - (void)mouseExited:(NSEvent *)theEvent
341 {
342     Q_UNUSED(theEvent);
343     QWindowSystemInterface::handleLeaveEvent(m_window);
344 }
345
346 - (void)rightMouseDown:(NSEvent *)theEvent
347 {
348     m_buttons |= Qt::RightButton;
349     [self handleMouseEvent:theEvent];
350 }
351
352 - (void)rightMouseDragged:(NSEvent *)theEvent
353 {
354     if (!(m_buttons & Qt::RightButton))
355         qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
356     [self handleMouseEvent:theEvent];
357 }
358
359 - (void)rightMouseUp:(NSEvent *)theEvent
360 {
361     m_buttons &= QFlag(~int(Qt::RightButton));
362     [self handleMouseEvent:theEvent];
363 }
364
365 - (void)otherMouseDown:(NSEvent *)theEvent
366 {
367     switch ([theEvent buttonNumber]) {
368         case 3:
369             m_buttons |= Qt::MiddleButton;
370             break;
371         case 4:
372             m_buttons |= Qt::ExtraButton1;  // AKA Qt::BackButton
373             break;
374         case 5:
375             m_buttons |= Qt::ExtraButton2;  // AKA Qt::ForwardButton
376             break;
377         case 6:
378             m_buttons |= Qt::ExtraButton3;
379             break;
380         case 7:
381             m_buttons |= Qt::ExtraButton4;
382             break;
383         case 8:
384             m_buttons |= Qt::ExtraButton5;
385             break;
386         case 9:
387             m_buttons |= Qt::ExtraButton6;
388             break;
389         case 10:
390             m_buttons |= Qt::ExtraButton7;
391             break;
392         case 11:
393             m_buttons |= Qt::ExtraButton8;
394             break;
395         case 12:
396             m_buttons |= Qt::ExtraButton9;
397             break;
398         case 13:
399             m_buttons |= Qt::ExtraButton10;
400             break;
401         case 14:
402             m_buttons |= Qt::ExtraButton11;
403             break;
404         case 15:
405             m_buttons |= Qt::ExtraButton12;
406             break;
407         case 16:
408             m_buttons |= Qt::ExtraButton13;
409             break;
410         default:
411             m_buttons |= Qt::MiddleButton;
412             break;
413     }
414     [self handleMouseEvent:theEvent];
415 }
416
417 - (void)otherMouseDragged:(NSEvent *)theEvent
418 {
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];
422 }
423
424 - (void)otherMouseUp:(NSEvent *)theEvent
425 {
426     switch ([theEvent buttonNumber]) {
427         case 3:
428             m_buttons &= QFlag(~int(Qt::MiddleButton));
429             break;
430         case 4:
431             m_buttons &= QFlag(~int(Qt::ExtraButton1));  // AKA Qt::BackButton
432             break;
433         case 5:
434             m_buttons &= QFlag(~int(Qt::ExtraButton2));  // AKA Qt::ForwardButton
435             break;
436         case 6:
437             m_buttons &= QFlag(~int(Qt::ExtraButton3));
438             break;
439         case 7:
440             m_buttons &= QFlag(~int(Qt::ExtraButton4));
441             break;
442         case 8:
443             m_buttons &= QFlag(~int(Qt::ExtraButton5));
444             break;
445         case 9:
446             m_buttons &= QFlag(~int(Qt::ExtraButton6));
447             break;
448         case 10:
449             m_buttons &= QFlag(~int(Qt::ExtraButton7));
450             break;
451         case 11:
452             m_buttons &= QFlag(~int(Qt::ExtraButton8));
453             break;
454         case 12:
455             m_buttons &= QFlag(~int(Qt::ExtraButton9));
456             break;
457         case 13:
458             m_buttons &= QFlag(~int(Qt::ExtraButton10));
459             break;
460         case 14:
461             m_buttons &= QFlag(~int(Qt::ExtraButton11));
462             break;
463         case 15:
464             m_buttons &= QFlag(~int(Qt::ExtraButton12));
465             break;
466         case 16:
467             m_buttons &= QFlag(~int(Qt::ExtraButton13));
468             break;
469         default:
470             m_buttons &= QFlag(~int(Qt::MiddleButton));
471             break;
472     }
473     [self handleMouseEvent:theEvent];
474 }
475
476 - (void)touchesBeganWithEvent:(NSEvent *)event
477 {
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);
481 }
482
483 - (void)touchesMovedWithEvent:(NSEvent *)event
484 {
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);
488 }
489
490 - (void)touchesEndedWithEvent:(NSEvent *)event
491 {
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);
495 }
496
497 - (void)touchesCancelledWithEvent:(NSEvent *)event
498 {
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);
502 }
503
504 #ifndef QT_NO_WHEELEVENT
505 - (void)scrollWheel:(NSEvent *)theEvent
506 {
507     const EventRef carbonEvent = (EventRef)[theEvent eventRef];
508     const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
509     const bool scrollEvent = carbonEventKind == kEventMouseScroll;
510
511     QPoint angleDelta;
512     if (scrollEvent) {
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
519
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);
524         } else
525 #endif
526         {
527             angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
528             angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
529         }
530
531     } else {
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));
536     }
537
538     QPoint pixelDelta;
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]);
544         } else {
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);
550         }
551     }
552 #endif
553
554
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;
559
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.
566
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]];
572         }
573
574         QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
575
576         if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
577             currentWheelModifiers = Qt::NoModifier;
578         }
579     }
580 #else
581     QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
582                                              [self convertKeyModifiers:[theEvent modifierFlags]]);
583 #endif
584
585 }
586 #endif //QT_NO_WHEELEVENT
587
588 - (int) convertKeyCode : (QChar)keyChar
589 {
590     return qt_mac_cocoaKey2QtKey(keyChar);
591 }
592
593 - (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
594 {
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;
606     return qtMods;
607 }
608
609 - (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
610 {
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];
616
617     QString text;
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]);
623
624         if (!m_keyEventsAccepted && m_composingText.isEmpty())
625             m_keyEventsAccepted = QWindowSystemInterface::tryHandleSynchronousShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
626
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]];
635             }
636         }
637     }
638     if (!m_keyEventsAccepted && m_composingText.isEmpty())
639         QWindowSystemInterface::handleKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers, text);
640 }
641
642 - (void)keyDown:(NSEvent *)nsevent
643 {
644     m_keyEventsAccepted = false;
645     [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
646 }
647
648 - (void)keyUp:(NSEvent *)nsevent
649 {
650     [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
651 }
652
653 - (void)flagsChanged:(NSEvent *)nsevent
654 {
655     ulong timestamp = [nsevent timestamp] * 1000;
656     ulong modifiers = [nsevent modifierFlags];
657     Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
658
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;
664
665     struct qt_mac_enum_mapper
666     {
667         ulong mac_mask;
668         Qt::Key qt_code;
669     };
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)
680             continue;
681
682         QWindowSystemInterface::handleKeyEvent(m_window,
683                                                timestamp,
684                                                (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
685                                                modifier_key_symbols[i].qt_code,
686                                                qmodifiers);
687     }
688 }
689
690 - (void) doCommandBySelector:(SEL)aSelector
691 {
692     [self tryToPerform:aSelector with:self];
693 }
694
695 - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
696 {
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]));
702         } else {
703             commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
704         };
705     }
706     QObject *fo = QGuiApplication::focusObject();
707     if (fo) {
708         QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
709         if (QCoreApplication::sendEvent(fo, &queryEvent)) {
710             if (queryEvent.value(Qt::ImEnabled).toBool()) {
711                 QInputMethodEvent e;
712                 e.setCommitString(commitString);
713                 QCoreApplication::sendEvent(fo, &e);
714                 m_keyEventsAccepted = true;
715             }
716         }
717     }
718
719     m_composingText.clear();
720  }
721
722 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
723 {
724     Q_UNUSED(replacementRange)
725     QString preeditString;
726
727     QList<QInputMethodEvent::Attribute> attrs;
728     attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
729
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();
734         int index = 0;
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
741                                                           inRange:range];
742             NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
743             if (underlineStyle) {
744                 QColor clr (Qt::black);
745                 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
746                 if (color) {
747                     clr = qt_mac_toQColor(color);
748                 }
749                 QTextCharFormat format;
750                 format.setFontUnderline(true);
751                 format.setUnderlineColor(clr);
752                 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
753                                                     effectiveRange.location,
754                                                     effectiveRange.length,
755                                                     format);
756             }
757             index = effectiveRange.location + effectiveRange.length;
758         }
759     } else {
760         // No attributes specified, take only the preedit text.
761         preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
762     }
763
764     if (attrs.isEmpty()) {
765         QTextCharFormat format;
766         format.setFontUnderline(true);
767         attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
768                                             0, preeditString.length(), format);
769     }
770
771     m_composingText = preeditString;
772
773     QObject *fo = QGuiApplication::focusObject();
774     if (fo) {
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;
781             }
782         }
783     }
784 }
785
786 - (void) unmarkText
787 {
788     if (!m_composingText.isEmpty()) {
789         QObject *fo = QGuiApplication::focusObject();
790         if (fo) {
791             QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
792             if (QCoreApplication::sendEvent(fo, &queryEvent)) {
793                 if (queryEvent.value(Qt::ImEnabled).toBool()) {
794                     QInputMethodEvent e;
795                     e.setCommitString(m_composingText);
796                     QCoreApplication::sendEvent(fo, &e);
797                 }
798             }
799         }
800     }
801     m_composingText.clear();
802 }
803
804 - (BOOL) hasMarkedText
805 {
806     return (m_composingText.isEmpty() ? NO: YES);
807 }
808
809 - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
810 {
811     Q_UNUSED(actualRange)
812     QObject *fo = QGuiApplication::focusObject();
813     if (!fo)
814         return nil;
815     QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
816     if (!QCoreApplication::sendEvent(fo, &queryEvent))
817         return nil;
818     if (!queryEvent.value(Qt::ImEnabled).toBool())
819         return nil;
820
821     QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
822     if (selectedText.isEmpty())
823         return nil;
824
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];
828 }
829
830 - (NSRange) markedRange
831 {
832     NSRange range;
833     if (!m_composingText.isEmpty()) {
834         range.location = 0;
835         range.length = m_composingText.length();
836     } else {
837         range.location = NSNotFound;
838         range.length = 0;
839     }
840     return range;
841 }
842
843 - (NSRange) selectedRange
844 {
845     NSRange selectedRange = {NSNotFound, 0};
846     selectedRange.location = NSNotFound;
847     selectedRange.length = 0;
848
849     QObject *fo = QGuiApplication::focusObject();
850     if (!fo)
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;
857
858     QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
859
860     if (!selectedText.isEmpty()) {
861         selectedRange.location = 0;
862         selectedRange.length = selectedText.length();
863     }
864     return selectedRange;
865 }
866
867 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
868 {
869     Q_UNUSED(aRange)
870     Q_UNUSED(actualRange)
871     QObject *fo = QGuiApplication::focusObject();
872     if (!fo)
873         return NSZeroRect;
874
875     QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
876     if (!QCoreApplication::sendEvent(fo, &queryEvent))
877         return NSZeroRect;
878     if (!queryEvent.value(Qt::ImEnabled).toBool())
879         return NSZeroRect;
880
881     if (!m_window)
882         return NSZeroRect;
883
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());
887
888     NSRect rect;
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();
893     return rect;
894 }
895
896 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
897 {
898     // We dont support cursor movements using mouse while composing.
899     Q_UNUSED(aPoint);
900     return NSNotFound;
901 }
902
903 - (NSArray*) validAttributesForMarkedText
904 {
905     if (m_window != QGuiApplication::focusWindow())
906         return nil;
907
908     QObject *fo = QGuiApplication::focusObject();
909     if (!fo)
910         return nil;
911
912     QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
913     if (!QCoreApplication::sendEvent(fo, &queryEvent))
914         return nil;
915     if (!queryEvent.value(Qt::ImEnabled).toBool())
916         return nil;
917
918     // Support only underline color/style.
919     return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
920                                      NSUnderlineStyleAttributeName, nil];
921 }
922
923 -(void)registerDragTypes
924 {
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])];
945         }
946         [self registerForDraggedTypes:supportedTypes];
947     }
948 }
949
950 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
951 {
952     return [self handleDrag : sender];
953 }
954
955 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
956 {
957     return [self handleDrag : sender];
958 }
959
960 // Sends drag update to Qt, return the action
961 - (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
962 {
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]);
967
968     QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
969     return qt_mac_mapDropAction(response.acceptedAction());
970 }
971
972 - (void)draggingExited:(id <NSDraggingInfo>)sender
973 {
974     NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
975     QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
976
977     // Send 0 mime data to indicate drag exit
978     QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
979 }
980
981 // called on drop, send the drop to Qt and return if it was accepted.
982 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
983 {
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]);
988
989     QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
990     return response.isAccepted();
991 }
992
993 @end