Revert "Move QWindowSystemInterface out of qpa."
[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 #include <qpa/qplatformintegration.h>
53
54 #include <qpa/qwindowsysteminterface.h>
55 #include <QtGui/QTextFormat>
56 #include <QtCore/QDebug>
57 #include <private/qguiapplication_p.h>
58
59 #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
60 #include <accessibilityinspector.h>
61 #endif
62
63 static QTouchDevice *touchDevice = 0;
64
65 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
66   - (CGFloat)deviceDeltaX;
67   - (CGFloat)deviceDeltaY;
68   - (CGFloat)deviceDeltaZ;
69 @end
70
71 @implementation QNSView
72
73 - (id) init
74 {
75     self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
76     if (self) {
77         m_cgImage = 0;
78         m_window = 0;
79         m_buttons = Qt::NoButton;
80         m_sendKeyEvent = false;
81         currentCustomDragTypes = 0;
82         if (!touchDevice) {
83             touchDevice = new QTouchDevice;
84             touchDevice->setType(QTouchDevice::TouchPad);
85             touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
86             QWindowSystemInterface::registerTouchDevice(touchDevice);
87         }
88     }
89     return self;
90 }
91
92 - (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow
93 {
94     self = [self init];
95     if (!self)
96         return 0;
97
98     m_window = window;
99     m_platformWindow = platformWindow;
100     m_accessibleRoot = 0;
101     m_sendKeyEvent = false;
102
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) {
108
109         m_accessibleRoot = window->accessibleRoot();
110
111         AccessibilityInspector *inspector = new AccessibilityInspector(window);
112         skipAccessibilityForInspectorWindows = true;
113         inspector->inspectWindow(window);
114         skipAccessibilityForInspectorWindows = false;
115     }
116 #else
117     m_accessibleRoot = window->accessibleRoot();
118 #endif
119
120     [self registerDragTypes];
121     [self setPostsFrameChangedNotifications : YES];
122     [[NSNotificationCenter defaultCenter] addObserver:self
123                                           selector:@selector(updateGeometry)
124                                           name:NSViewFrameDidChangeNotification
125                                           object:self];
126
127     return self;
128 }
129
130 - (void)updateGeometry
131 {
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);
135
136 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
137     qDebug() << "QNSView::udpateGeometry" << geo;
138 #endif
139
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);
144
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);
148 }
149
150 - (void)windowNotification : (NSNotification *) windowNotification
151 {
152     //qDebug() << "windowNotification" << QCFString::toQString([windowNotification name]);
153
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];
163         if (!keyWindow) {
164             // no new key window, go ahead and set the active window to zero
165             if (!m_platformWindow->windowIsPopupType())
166                 QWindowSystemInterface::handleWindowActivated(0);
167         }
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()));
176     } else {
177
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);
184         }
185     }
186 #endif
187
188     }
189 }
190
191 - (void) setImage:(QImage *)image
192 {
193     CGImageRelease(m_cgImage);
194
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();
201
202     CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB();
203
204     CGDataProviderRef cgDataProviderRef = CGDataProviderCreateWithData(
205                 NULL,
206                 imageData,
207                 image->byteCount(),
208                 NULL);
209
210     m_cgImage = CGImageCreate(width,
211                               height,
212                               colorBufferSize,
213                               bitDepth,
214                               bytesPrLine,
215                               cgColourSpaceRef,
216                               kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
217                               cgDataProviderRef,
218                               NULL,
219                               false,
220                               kCGRenderingIntentDefault);
221
222     CGColorSpaceRelease(cgColourSpaceRef);
223
224 }
225
226 - (void) drawRect:(NSRect)dirtyRect
227 {
228     if (!m_cgImage)
229         return;
230
231     CGRect dirtyCGRect = NSRectToCGRect(dirtyRect);
232
233     NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
234     CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
235
236     CGContextSaveGState( cgContext );
237     int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect);
238     CGContextTranslateCTM(cgContext, 0, dy);
239     CGContextScaleCTM(cgContext, 1, -1);
240
241     CGImageRef subImage = CGImageCreateWithImageInRect(m_cgImage, dirtyCGRect);
242     CGContextDrawImage(cgContext,dirtyCGRect,subImage);
243
244     CGContextRestoreGState(cgContext);
245
246     CGImageRelease(subImage);
247
248 }
249
250 - (BOOL) isFlipped
251 {
252     return YES;
253 }
254
255 - (BOOL)acceptsFirstResponder
256 {
257     return YES;
258 }
259
260 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
261 {
262     Q_UNUSED(theEvent);
263     return YES;
264 }
265
266 - (void)handleMouseEvent:(NSEvent *)theEvent
267 {
268     // Calculate the mouse position in the QWindow and Qt screen coordinate system,
269     // starting from coordinates in the NSWindow coordinate system.
270     //
271     // This involves translating according to the window location on screen,
272     // as well as inverting the y coordinate due to the origin change.
273     //
274     // Coordinate system overview, outer to innermost:
275     //
276     // Name             Origin
277     //
278     // OS X screen      bottom-left
279     // Qt screen        top-left
280     // NSWindow         bottom-left
281     // NSView/QWindow   top-left
282     //
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.
285
286     NSPoint nsWindowPoint = [theEvent locationInWindow];                    // NSWindow coordinates
287
288     NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
289     QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);                     // NSView/QWindow coordinates
290
291     QPoint qtScreenPoint;
292
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
299     } else
300 #endif
301     {
302         NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)];
303         qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
304     }
305     ulong timestamp = [theEvent timestamp] * 1000;
306
307     QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
308     nativeDrag->setLastMouseEvent(theEvent, self);
309
310     QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
311 }
312
313 - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
314 {
315     // get m_buttons in sync
316     NSEventType ty = [theEvent type];
317     switch (ty) {
318     case NSLeftMouseDown:
319         m_buttons |= Qt::LeftButton;
320         break;
321     case NSLeftMouseUp:
322          m_buttons &= QFlag(~int(Qt::LeftButton));
323          break;
324     case NSRightMouseDown:
325         m_buttons |= Qt::RightButton;
326         break;
327     case NSRightMouseUp:
328         m_buttons &= QFlag(~int(Qt::RightButton));
329         break;
330     default:
331         break;
332     }
333
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));
341
342     ulong timestamp = [theEvent timestamp] * 1000;
343     QWindowSystemInterface::handleFrameStrutMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
344 }
345
346 - (void)mouseDown:(NSEvent *)theEvent
347 {
348     if (m_platformWindow->m_activePopupWindow) {
349         QWindowSystemInterface::handleSynchronousCloseEvent(m_platformWindow->m_activePopupWindow);
350         m_platformWindow->m_activePopupWindow = 0;
351     }
352     if ([self hasMarkedText]) {
353         NSInputManager* inputManager = [NSInputManager currentInputManager];
354         if ([inputManager wantsToHandleMouseEvents]) {
355             [inputManager handleMouseEvent:theEvent];
356         }
357     } else {
358         m_buttons |= Qt::LeftButton;
359         [self handleMouseEvent:theEvent];
360     }
361 }
362
363 - (void)mouseDragged:(NSEvent *)theEvent
364 {
365     if (!(m_buttons & Qt::LeftButton))
366         qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)");
367     [self handleMouseEvent:theEvent];
368 }
369
370 - (void)mouseUp:(NSEvent *)theEvent
371 {
372     m_buttons &= QFlag(~int(Qt::LeftButton));
373     [self handleMouseEvent:theEvent];
374 }
375
376 - (void)mouseMoved:(NSEvent *)theEvent
377 {
378     [self handleMouseEvent:theEvent];
379 }
380
381 - (void)mouseEntered:(NSEvent *)theEvent
382 {
383     Q_UNUSED(theEvent);
384     QWindowSystemInterface::handleEnterEvent(m_window);
385 }
386
387 - (void)mouseExited:(NSEvent *)theEvent
388 {
389     Q_UNUSED(theEvent);
390     QWindowSystemInterface::handleLeaveEvent(m_window);
391 }
392
393 - (void)rightMouseDown:(NSEvent *)theEvent
394 {
395     m_buttons |= Qt::RightButton;
396     [self handleMouseEvent:theEvent];
397 }
398
399 - (void)rightMouseDragged:(NSEvent *)theEvent
400 {
401     if (!(m_buttons & Qt::RightButton))
402         qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)");
403     [self handleMouseEvent:theEvent];
404 }
405
406 - (void)rightMouseUp:(NSEvent *)theEvent
407 {
408     m_buttons &= QFlag(~int(Qt::RightButton));
409     [self handleMouseEvent:theEvent];
410 }
411
412 - (void)otherMouseDown:(NSEvent *)theEvent
413 {
414     switch ([theEvent buttonNumber]) {
415         case 3:
416             m_buttons |= Qt::MiddleButton;
417             break;
418         case 4:
419             m_buttons |= Qt::ExtraButton1;  // AKA Qt::BackButton
420             break;
421         case 5:
422             m_buttons |= Qt::ExtraButton2;  // AKA Qt::ForwardButton
423             break;
424         case 6:
425             m_buttons |= Qt::ExtraButton3;
426             break;
427         case 7:
428             m_buttons |= Qt::ExtraButton4;
429             break;
430         case 8:
431             m_buttons |= Qt::ExtraButton5;
432             break;
433         case 9:
434             m_buttons |= Qt::ExtraButton6;
435             break;
436         case 10:
437             m_buttons |= Qt::ExtraButton7;
438             break;
439         case 11:
440             m_buttons |= Qt::ExtraButton8;
441             break;
442         case 12:
443             m_buttons |= Qt::ExtraButton9;
444             break;
445         case 13:
446             m_buttons |= Qt::ExtraButton10;
447             break;
448         case 14:
449             m_buttons |= Qt::ExtraButton11;
450             break;
451         case 15:
452             m_buttons |= Qt::ExtraButton12;
453             break;
454         case 16:
455             m_buttons |= Qt::ExtraButton13;
456             break;
457         default:
458             m_buttons |= Qt::MiddleButton;
459             break;
460     }
461     [self handleMouseEvent:theEvent];
462 }
463
464 - (void)otherMouseDragged:(NSEvent *)theEvent
465 {
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];
469 }
470
471 - (void)otherMouseUp:(NSEvent *)theEvent
472 {
473     switch ([theEvent buttonNumber]) {
474         case 3:
475             m_buttons &= QFlag(~int(Qt::MiddleButton));
476             break;
477         case 4:
478             m_buttons &= QFlag(~int(Qt::ExtraButton1));  // AKA Qt::BackButton
479             break;
480         case 5:
481             m_buttons &= QFlag(~int(Qt::ExtraButton2));  // AKA Qt::ForwardButton
482             break;
483         case 6:
484             m_buttons &= QFlag(~int(Qt::ExtraButton3));
485             break;
486         case 7:
487             m_buttons &= QFlag(~int(Qt::ExtraButton4));
488             break;
489         case 8:
490             m_buttons &= QFlag(~int(Qt::ExtraButton5));
491             break;
492         case 9:
493             m_buttons &= QFlag(~int(Qt::ExtraButton6));
494             break;
495         case 10:
496             m_buttons &= QFlag(~int(Qt::ExtraButton7));
497             break;
498         case 11:
499             m_buttons &= QFlag(~int(Qt::ExtraButton8));
500             break;
501         case 12:
502             m_buttons &= QFlag(~int(Qt::ExtraButton9));
503             break;
504         case 13:
505             m_buttons &= QFlag(~int(Qt::ExtraButton10));
506             break;
507         case 14:
508             m_buttons &= QFlag(~int(Qt::ExtraButton11));
509             break;
510         case 15:
511             m_buttons &= QFlag(~int(Qt::ExtraButton12));
512             break;
513         case 16:
514             m_buttons &= QFlag(~int(Qt::ExtraButton13));
515             break;
516         default:
517             m_buttons &= QFlag(~int(Qt::MiddleButton));
518             break;
519     }
520     [self handleMouseEvent:theEvent];
521 }
522
523 - (void)touchesBeganWithEvent:(NSEvent *)event
524 {
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);
528 }
529
530 - (void)touchesMovedWithEvent:(NSEvent *)event
531 {
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);
535 }
536
537 - (void)touchesEndedWithEvent:(NSEvent *)event
538 {
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);
542 }
543
544 - (void)touchesCancelledWithEvent:(NSEvent *)event
545 {
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);
549 }
550
551 #ifndef QT_NO_WHEELEVENT
552 - (void)scrollWheel:(NSEvent *)theEvent
553 {
554     const EventRef carbonEvent = (EventRef)[theEvent eventRef];
555     const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
556     const bool scrollEvent = carbonEventKind == kEventMouseScroll;
557
558     QPoint angleDelta;
559     if (scrollEvent) {
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
566
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);
571         } else
572 #endif
573         {
574             angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees);
575             angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees);
576         }
577
578     } else {
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));
583     }
584
585     QPoint pixelDelta;
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]);
591         } else {
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);
597         }
598     }
599 #endif
600
601
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;
606
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.
613
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]];
619         }
620
621         QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta, currentWheelModifiers);
622
623         if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) {
624             currentWheelModifiers = Qt::NoModifier;
625         }
626     }
627 #else
628     QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, pixelDelta, angleDelta,
629                                              [self convertKeyModifiers:[theEvent modifierFlags]]);
630 #endif
631
632 }
633 #endif //QT_NO_WHEELEVENT
634
635 - (int) convertKeyCode : (QChar)keyChar
636 {
637     return qt_mac_cocoaKey2QtKey(keyChar);
638 }
639
640 - (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
641 {
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;
653     return qtMods;
654 }
655
656 - (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
657 {
658     ulong timestamp = [nsevent timestamp] * 1000;
659     Qt::KeyboardModifiers modifiers = [self convertKeyModifiers:[nsevent modifierFlags]];
660     NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
661
662     QChar ch;
663     int keyCode;
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];
668     } else {
669         // might be a dead key
670         ch = QChar::ReplacementCharacter;
671         keyCode = Qt::Key_unknown;
672     }
673
674     // we will send a key event unless the input method sets m_sendKeyEvent to false
675     m_sendKeyEvent = true;
676
677     QString text;
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]);
683
684         if (m_composingText.isEmpty())
685             m_sendKeyEvent = !QWindowSystemInterface::tryHandleSynchronousShortcutEvent(m_window, timestamp, keyCode, modifiers, text);
686
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]];
696                 }
697             }
698         }
699     }
700
701     if (m_sendKeyEvent && m_composingText.isEmpty())
702         QWindowSystemInterface::handleKeyEvent(m_window, timestamp, QEvent::Type(eventType), keyCode, modifiers, text);
703
704     m_sendKeyEvent = false;
705 }
706
707 - (void)keyDown:(NSEvent *)nsevent
708 {
709     [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
710 }
711
712 - (void)keyUp:(NSEvent *)nsevent
713 {
714     [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
715 }
716
717 - (void)flagsChanged:(NSEvent *)nsevent
718 {
719     ulong timestamp = [nsevent timestamp] * 1000;
720     ulong modifiers = [nsevent modifierFlags];
721     Qt::KeyboardModifiers qmodifiers = [self convertKeyModifiers:modifiers];
722
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;
728
729     struct qt_mac_enum_mapper
730     {
731         ulong mac_mask;
732         Qt::Key qt_code;
733     };
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)
744             continue;
745
746         QWindowSystemInterface::handleKeyEvent(m_window,
747                                                timestamp,
748                                                (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
749                                                modifier_key_symbols[i].qt_code,
750                                                qmodifiers);
751     }
752 }
753
754 - (void) doCommandBySelector:(SEL)aSelector
755 {
756     [self tryToPerform:aSelector with:self];
757 }
758
759 - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
760 {
761     Q_UNUSED(replacementRange)
762
763     if (m_sendKeyEvent && m_composingText.isEmpty()) {
764         // don't send input method events for simple text input (let handleKeyEvent send key events instead)
765         return;
766     }
767
768     QString commitString;
769     if ([aString length]) {
770         if ([aString isKindOfClass:[NSAttributedString class]]) {
771             commitString = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
772         } else {
773             commitString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
774         };
775     }
776     QObject *fo = QGuiApplication::focusObject();
777     if (fo) {
778         QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
779         if (QCoreApplication::sendEvent(fo, &queryEvent)) {
780             if (queryEvent.value(Qt::ImEnabled).toBool()) {
781                 QInputMethodEvent e;
782                 e.setCommitString(commitString);
783                 QCoreApplication::sendEvent(fo, &e);
784                 // prevent handleKeyEvent from sending a key event
785                 m_sendKeyEvent = false;
786             }
787         }
788     }
789
790     m_composingText.clear();
791 }
792
793 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
794 {
795     Q_UNUSED(replacementRange)
796     QString preeditString;
797
798     QList<QInputMethodEvent::Attribute> attrs;
799     attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
800
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();
805         int index = 0;
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
812                                                           inRange:range];
813             NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
814             if (underlineStyle) {
815                 QColor clr (Qt::black);
816                 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
817                 if (color) {
818                     clr = qt_mac_toQColor(color);
819                 }
820                 QTextCharFormat format;
821                 format.setFontUnderline(true);
822                 format.setUnderlineColor(clr);
823                 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
824                                                     effectiveRange.location,
825                                                     effectiveRange.length,
826                                                     format);
827             }
828             index = effectiveRange.location + effectiveRange.length;
829         }
830     } else {
831         // No attributes specified, take only the preedit text.
832         preeditString = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
833     }
834
835     if (attrs.isEmpty()) {
836         QTextCharFormat format;
837         format.setFontUnderline(true);
838         attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
839                                             0, preeditString.length(), format);
840     }
841
842     m_composingText = preeditString;
843
844     QObject *fo = QGuiApplication::focusObject();
845     if (fo) {
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;
853             }
854         }
855     }
856 }
857
858 - (void) unmarkText
859 {
860     if (!m_composingText.isEmpty()) {
861         QObject *fo = QGuiApplication::focusObject();
862         if (fo) {
863             QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
864             if (QCoreApplication::sendEvent(fo, &queryEvent)) {
865                 if (queryEvent.value(Qt::ImEnabled).toBool()) {
866                     QInputMethodEvent e;
867                     e.setCommitString(m_composingText);
868                     QCoreApplication::sendEvent(fo, &e);
869                 }
870             }
871         }
872     }
873     m_composingText.clear();
874 }
875
876 - (BOOL) hasMarkedText
877 {
878     return (m_composingText.isEmpty() ? NO: YES);
879 }
880
881 - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
882 {
883     Q_UNUSED(actualRange)
884     QObject *fo = QGuiApplication::focusObject();
885     if (!fo)
886         return nil;
887     QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
888     if (!QCoreApplication::sendEvent(fo, &queryEvent))
889         return nil;
890     if (!queryEvent.value(Qt::ImEnabled).toBool())
891         return nil;
892
893     QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
894     if (selectedText.isEmpty())
895         return nil;
896
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];
900 }
901
902 - (NSRange) markedRange
903 {
904     NSRange range;
905     if (!m_composingText.isEmpty()) {
906         range.location = 0;
907         range.length = m_composingText.length();
908     } else {
909         range.location = NSNotFound;
910         range.length = 0;
911     }
912     return range;
913 }
914
915 - (NSRange) selectedRange
916 {
917     NSRange selectedRange = {NSNotFound, 0};
918     selectedRange.location = NSNotFound;
919     selectedRange.length = 0;
920
921     QObject *fo = QGuiApplication::focusObject();
922     if (!fo)
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;
929
930     QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
931
932     if (!selectedText.isEmpty()) {
933         selectedRange.location = 0;
934         selectedRange.length = selectedText.length();
935     }
936     return selectedRange;
937 }
938
939 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
940 {
941     Q_UNUSED(aRange)
942     Q_UNUSED(actualRange)
943     QObject *fo = QGuiApplication::focusObject();
944     if (!fo)
945         return NSZeroRect;
946
947     QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
948     if (!QCoreApplication::sendEvent(fo, &queryEvent))
949         return NSZeroRect;
950     if (!queryEvent.value(Qt::ImEnabled).toBool())
951         return NSZeroRect;
952
953     if (!m_window)
954         return NSZeroRect;
955
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());
959
960     NSRect rect;
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();
965     return rect;
966 }
967
968 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
969 {
970     // We dont support cursor movements using mouse while composing.
971     Q_UNUSED(aPoint);
972     return NSNotFound;
973 }
974
975 - (NSArray*) validAttributesForMarkedText
976 {
977     if (m_window != QGuiApplication::focusWindow())
978         return nil;
979
980     QObject *fo = QGuiApplication::focusObject();
981     if (!fo)
982         return nil;
983
984     QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
985     if (!QCoreApplication::sendEvent(fo, &queryEvent))
986         return nil;
987     if (!queryEvent.value(Qt::ImEnabled).toBool())
988         return nil;
989
990     // Support only underline color/style.
991     return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
992                                      NSUnderlineStyleAttributeName, nil];
993 }
994
995 -(void)registerDragTypes
996 {
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])];
1017         }
1018         [self registerForDraggedTypes:supportedTypes];
1019     }
1020 }
1021
1022 - (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal
1023 {
1024     Q_UNUSED(isLocal);
1025     QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1026     return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
1027 }
1028
1029 - (BOOL) ignoreModifierKeysWhileDragging
1030 {
1031     return NO;
1032 }
1033
1034 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1035 {
1036     return [self handleDrag : sender];
1037 }
1038
1039 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1040 {
1041     return [self handleDrag : sender];
1042 }
1043
1044 // Sends drag update to Qt, return the action
1045 - (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
1046 {
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]);
1051
1052     // update these so selecting move/copy/link works
1053     QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
1054
1055     QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
1056     return qt_mac_mapDropAction(response.acceptedAction());
1057 }
1058
1059 - (void)draggingExited:(id <NSDraggingInfo>)sender
1060 {
1061     NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
1062     QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
1063
1064     // Send 0 mime data to indicate drag exit
1065     QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction);
1066 }
1067
1068 // called on drop, send the drop to Qt and return if it was accepted.
1069 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1070 {
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]);
1075
1076     QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed);
1077     return response.isAccepted();
1078 }
1079
1080 - (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation
1081 {
1082     QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
1083     nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
1084
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));
1088
1089     NSPoint windowPoint = [self convertPoint: point fromView: nil];
1090     QPoint qtWindowPoint(point.x, point.y);
1091
1092     NSWindow *window = [self window];
1093     NSPoint screenPoint = [window convertBaseToScreen :point];
1094     QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
1095
1096     QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons);
1097 }
1098
1099 @end