1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #import <private/qcocoaview_mac_p.h>
44 #include <private/qwidget_p.h>
45 #include <private/qt_mac_p.h>
46 #include <private/qapplication_p.h>
47 #include <private/qabstractscrollarea_p.h>
48 #include <private/qt_cocoa_helpers_mac_p.h>
49 #include <private/qdnd_p.h>
50 #include <private/qmacinputcontext_p.h>
51 #include <private/qevent_p.h>
52 #include <private/qbackingstore_p.h>
53 #include <private/qwindowsurface_raster_p.h>
54 #include <private/qunifiedtoolbarsurface_mac_p.h>
56 #include <qscrollarea.h>
58 #include <qtextformat.h>
59 #include <qpaintengine.h>
61 #include <QAccessible>
67 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
68 - (CGFloat)deviceDeltaX;
69 - (CGFloat)deviceDeltaY;
70 - (CGFloat)deviceDeltaZ;
73 @interface NSEvent (Qt_Compile_Leopard_Gestures)
74 - (CGFloat)magnification;
79 extern void qt_mac_update_cursor(); // qcursor_mac.mm
80 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
81 extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp
82 extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm
83 extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
84 extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm
85 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
86 extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
87 extern QWidget *mac_mouse_grabber;
88 extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp
90 static QColor colorFrom(NSColor *color)
93 NSString *colorSpace = [color colorSpaceName];
94 if (colorSpace == NSDeviceCMYKColorSpace) {
95 CGFloat cyan, magenta, yellow, black, alpha;
96 [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
97 qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
100 tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
101 CGFloat red, green, blue, alpha;
102 [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
103 qtColor.setRgbF(red, green, blue, alpha);
110 QT_FORWARD_DECLARE_CLASS(QMacCocoaAutoReleasePool)
111 QT_FORWARD_DECLARE_CLASS(QCFString)
112 QT_FORWARD_DECLARE_CLASS(QDragManager)
113 QT_FORWARD_DECLARE_CLASS(QMimeData)
114 QT_FORWARD_DECLARE_CLASS(QPoint)
115 QT_FORWARD_DECLARE_CLASS(QApplication)
116 QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
117 QT_FORWARD_DECLARE_CLASS(QDragEnterEvent)
118 QT_FORWARD_DECLARE_CLASS(QDragMoveEvent)
119 QT_FORWARD_DECLARE_CLASS(QStringList)
120 QT_FORWARD_DECLARE_CLASS(QString)
121 QT_FORWARD_DECLARE_CLASS(QRect)
122 QT_FORWARD_DECLARE_CLASS(QRegion)
123 QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea)
124 QT_FORWARD_DECLARE_CLASS(QAbstractScrollAreaPrivate)
125 QT_FORWARD_DECLARE_CLASS(QPaintEvent)
126 QT_FORWARD_DECLARE_CLASS(QPainter)
127 QT_FORWARD_DECLARE_CLASS(QHoverEvent)
128 QT_FORWARD_DECLARE_CLASS(QCursor)
131 extern NSString *NSTextInputReplacementRangeAttributeName;
134 //#define ALIEN_DEBUG 1
136 static int qCocoaViewCount = 0;
139 @implementation QT_MANGLE_NAMESPACE(QCocoaView)
141 - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
145 [self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
147 [self setFocusRingType:NSFocusRingTypeNone];
148 composingText = new QString();
152 qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount;
156 sendKeyEvents = true;
157 fromKeyDownEvent = false;
160 [self setHidden:YES];
164 - (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
167 qwidgetprivate = widgetprivate;
168 [[NSNotificationCenter defaultCenter] addObserver:self
169 selector:@selector(frameDidChange:)
170 name:@"NSViewFrameDidChangeNotification"
176 QMacCocoaAutoReleasePool pool;
177 delete composingText;
178 [[NSNotificationCenter defaultCenter] removeObserver:self];
182 qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount;
191 return [super isOpaque];
192 return qwidgetprivate->isOpaque;
200 // We preserve the content of the view if WA_StaticContents is defined.
202 // More info in the Cocoa documentation:
203 // http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html
204 - (BOOL) preservesContentDuringLiveResize
206 return qwidget->testAttribute(Qt::WA_StaticContents);
209 - (void) setFrameSize:(NSSize)newSize
211 [super setFrameSize:newSize];
213 // A change in size has required the view to be invalidated.
214 if ([self inLiveResize]) {
217 [self getRectsExposedDuringLiveResize:rects count:&count];
220 [self setNeedsDisplayInRect:rects[count]];
223 [self setNeedsDisplay:YES];
226 // Make sure the opengl context is updated on resize.
227 if (qwidgetprivate && qwidgetprivate->isGLWidget && [self window]) {
228 qwidgetprivate->needWindowChange = true;
229 QEvent event(QEvent::MacGLWindowChange);
230 qApp->sendEvent(qwidget, &event);
234 // We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint.
235 // During the resize, the top of the widget is repainted, probably because of the
236 // change of coordinate space (Quartz vs Qt). This is then followed by this message:
237 // -[NSView _setNeedsDisplayIfTopLeftChanged]
238 // which force a full repaint by sending the message 'setNeedsDisplay:'.
239 // That is what we are preventing here.
240 - (void)setNeedsDisplay:(BOOL)flag {
241 if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) {
242 [super setNeedsDisplay:flag];
246 - (void)drawRect:(NSRect)aRect
252 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
253 qt_mac_retain_graphics_context(context);
255 // We use a different graphics system.
257 // Widgets that are set to paint on screen, specifically QGLWidget,
258 // requires the native engine to execute in order to be drawn.
259 if (QApplicationPrivate::graphicsSystem() != 0 && !qwidget->testAttribute(Qt::WA_PaintOnScreen)) {
262 if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) {
264 if (!qwidgetprivate->isInUnifiedToolbar) {
266 // Qt handles the painting occuring inside the window.
267 // Cocoa also keeps track of all widgets as NSView and therefore might
268 // ask for a repainting of a widget even if Qt is already taking care of it.
270 // The only valid reason for Cocoa to call drawRect: is for window manipulation
271 // (ie. resize, ...).
273 // Qt will then forward the update to the children.
274 if (!qwidget->isWindow()) {
275 qt_mac_release_graphics_context(context);
279 QRasterWindowSurface *winSurface = dynamic_cast<QRasterWindowSurface *>(qwidget->windowSurface());
280 if (!winSurface || !winSurface->needsFlush) {
281 qt_mac_release_graphics_context(context);
286 const QVector<QRect> &rects = winSurface->regionToFlush.rects();
287 for (int i = 0; i < rects.size(); ++i) {
288 const QRect &rect = rects.at(i);
289 CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
291 CGContextClip(context);
293 QRect r = winSurface->regionToFlush.boundingRect();
294 const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height());
296 qt_mac_draw_image(context, winSurface->imageContext(), area, area);
298 winSurface->needsFlush = false;
299 winSurface->regionToFlush = QRegion();
303 QUnifiedToolbarSurface *unifiedSurface = qwidgetprivate->unifiedSurface;
304 if (!unifiedSurface) {
305 qt_mac_release_graphics_context(context);
309 int areaX = qwidgetprivate->toolbar_offset.x();
310 int areaY = qwidgetprivate->toolbar_offset.y();
311 int areaWidth = qwidget->geometry().width();
312 int areaHeight = qwidget->geometry().height();
313 const CGRect area = CGRectMake(areaX, areaY, areaWidth, areaHeight);
314 const CGRect drawingArea = CGRectMake(0, 0, areaWidth, areaHeight);
316 qt_mac_draw_image(context, unifiedSurface->imageContext(), area, drawingArea);
318 qwidgetprivate->flushRequested = false;
322 CGContextFlush(context);
323 qt_mac_release_graphics_context(context);
327 // Qt handles the painting occuring inside the window.
328 // Cocoa also keeps track of all widgets as NSView and therefore might
329 // ask for a repainting of a widget even if Qt is already taking care of it.
331 // The only valid reason for Cocoa to call drawRect: is for window manipulation
332 // (ie. resize, ...).
334 // Qt will then forward the update to the children.
335 if (qwidget->isWindow()) {
336 qwidgetprivate->syncBackingStore(qwidget->rect());
341 qwidgetprivate->hd = context;
343 if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event.
344 if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent))
345 qWarning("QWidget::repaint: Recursive repaint detected");
347 const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
352 [self getRectsBeingDrawn:&rects count:&count];
353 for (int i = 0; i < count; ++i) {
354 QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height);
358 if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
359 const QRegion &parentMask = qwidget->window()->mask();
360 if (!parentMask.isEmpty()) {
361 const QPoint mappedPoint = qwidget->mapTo(qwidget->window(), qrect.topLeft());
362 qrgn.translate(mappedPoint);
364 qrgn.translate(-mappedPoint.x(), -mappedPoint.y());
368 QPoint redirectionOffset(0, 0);
370 qwidget->setAttribute(Qt::WA_WState_InPaintEvent);
371 QPaintEngine *engine = qwidget->paintEngine();
373 engine->setSystemClip(qrgn);
374 if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) {
375 CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height());
376 CGContextTranslateCTM (context, 0, widgetRect.size.height);
377 CGContextScaleCTM(context, 1, -1);
378 if (qwidget->isWindow())
379 CGContextClearRect(context, widgetRect);
380 CGContextClipToMask(context, widgetRect, qwidgetprivate->extra->imageMask);
381 CGContextScaleCTM(context, 1, -1);
382 CGContextTranslateCTM (context, 0, -widgetRect.size.height);
385 if (qwidget->isWindow() && !qwidgetprivate->isOpaque
386 && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) {
387 CGContextClearRect(context, NSRectToCGRect(aRect));
390 qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
391 QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget);
393 // We specify that we want to draw the widget itself, and
394 // all its children recursive. But we skip native children, because
395 // they will receive drawRect calls by themselves as needed:
396 int flags = QWidgetPrivate::DrawPaintOnScreen
397 | QWidgetPrivate::DrawRecursive
398 | QWidgetPrivate::DontDrawNativeChildren;
400 if (qwidget->isWindow())
401 flags |= QWidgetPrivate::DrawAsRoot;
404 qt_mac_clearDirtyOnWidgetInsideDrawWidget = true;
405 qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0);
406 qt_mac_clearDirtyOnWidgetInsideDrawWidget = false;
408 if (!redirectionOffset.isNull())
409 QPainter::restoreRedirected(qwidget);
411 engine->setSystemClip(QRegion());
412 qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
413 if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive())
414 qWarning("QWidget: It is dangerous to leave painters active on a"
415 " widget outside of the PaintEvent");
417 qwidgetprivate->hd = 0;
418 qt_mac_release_graphics_context(context);
421 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
423 // Find the widget that should receive the event:
424 QPoint qlocal, qglobal;
425 QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0);
426 if (!widgetToGetMouse)
429 return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough);
432 - (NSView *)hitTest:(NSPoint)aPoint
435 return [super hitTest:aPoint];
437 if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
438 return nil; // You cannot hit a transparent for mouse event widget.
439 return [super hitTest:aPoint];
442 - (void)updateTrackingAreas
447 // [NSView addTrackingArea] is slow, so bail out early if we can:
448 if (NSIsEmptyRect([self visibleRect]))
451 QMacCocoaAutoReleasePool pool;
452 if (NSArray *trackingArray = [self trackingAreas]) {
453 NSUInteger size = [trackingArray count];
454 for (NSUInteger i = 0; i < size; ++i) {
455 NSTrackingArea *t = [trackingArray objectAtIndex:i];
456 [self removeTrackingArea:t];
460 // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
461 // only be turned on if mouseTracking, hover is on or a tool tip is set.
462 // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
463 // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
464 // mouse moves delivered to it (Apple recommends keeping it OFF because there
465 // is a performance hit). So it goes.
466 NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
467 | NSTrackingInVisibleRect | NSTrackingMouseMoved;
468 NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0,
471 options:trackingOptions
474 [self addTrackingArea:ta];
478 - (void)mouseEntered:(NSEvent *)event
480 // Cocoa will not send a move event on mouseEnter. But since
481 // Qt expect this, we fake one now. See also mouseExited below
482 // for info about enter/leave event handling
483 NSEvent *nsmoveEvent = [NSEvent
484 mouseEventWithType:NSMouseMoved
485 location:[[self window] mouseLocationOutsideOfEventStream]
486 modifierFlags: [event modifierFlags]
487 timestamp: [event timestamp]
488 windowNumber: [event windowNumber]
489 context: [event context]
490 eventNumber: [event eventNumber]
494 // Important: Cocoa sends us mouseEnter on all views under the mouse
495 // and not just the one on top. Therefore, to we cannot use qwidget
496 // as native widget for this case. Instead, we let qt_mac_handleMouseEvent
497 // resolve it (last argument set to 0):
498 qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0);
501 - (void)mouseExited:(NSEvent *)event
503 // Note: normal enter/leave handling is done from within mouseMove. This handler
504 // catches the case when the mouse moves out of the window (which mouseMove do not).
505 // Updating the mouse cursor follows the same logic as enter/leave. And we update
506 // neither if a grab exists (even if the grab points to this widget, it seems, ref X11)
508 if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) {
509 qt_mac_update_cursor();
510 // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still
511 // reports a target, it means that either there is a grab involved, or the mouse
512 // hovered over another window in the application. In both cases, move events will
513 // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave.
514 QPoint qlocal, qglobal;
515 QWidget *widgetUnderMouse = 0;
516 qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse);
518 if (widgetUnderMouse == 0) {
519 QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
520 qt_last_mouse_receiver = 0;
521 qt_last_native_mouse_receiver = 0;
526 - (void)flagsChanged:(NSEvent *)theEvent
528 QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
532 qt_dispatchModifiersChanged(theEvent, widgetToGetKey);
533 [super flagsChanged:theEvent];
536 - (void)mouseMoved:(NSEvent *)theEvent
538 // Important: this method will only be called when the view's window is _not_ inside
539 // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event
540 // before it ends up here. So, this method is added for supporting QMacNativeWidget.
541 // TODO: Cocoa send move events to all views under the mouse. So make sure we only
542 // handle the event for the widget on top when using QMacNativeWidget.
543 qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
546 - (void)mouseDown:(NSEvent *)theEvent
548 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget);
549 // Don't call super here. This prevents us from getting the mouseUp event,
550 // which we need to send even if the mouseDown event was not accepted.
551 // (this is standard Qt behavior.)
554 - (void)mouseUp:(NSEvent *)theEvent
556 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget);
559 - (void)rightMouseDown:(NSEvent *)theEvent
561 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget);
564 - (void)rightMouseUp:(NSEvent *)theEvent
566 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget);
569 - (void)otherMouseDown:(NSEvent *)theEvent
571 Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
572 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget);
575 - (void)otherMouseUp:(NSEvent *)theEvent
577 Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
578 qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget);
581 - (void)mouseDragged:(NSEvent *)theEvent
583 qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
586 - (void)rightMouseDragged:(NSEvent *)theEvent
588 qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
591 - (void)otherMouseDragged:(NSEvent *)theEvent
593 qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
596 - (void)scrollWheel:(NSEvent *)theEvent
598 // Give the Input Manager a chance to process the wheel event.
599 NSInputManager *currentIManager = [NSInputManager currentInputManager];
600 if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
601 [currentIManager handleMouseEvent:theEvent];
604 Qt::MouseButtons buttons = QApplication::mouseButtons();
605 Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
607 // Find the widget that should receive the event:
608 QPoint qlocal, qglobal;
609 QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0);
610 if (!widgetToGetMouse)
617 const EventRef carbonEvent = (EventRef)[theEvent eventRef];
618 const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
619 const bool scrollEvent = carbonEventKind == kEventMouseScroll;
622 // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
623 // Since deviceDelta is delivered as pixels rather than degrees, we need to
624 // convert from pixels to degrees in a sensible manner.
625 // It looks like 1/4 degrees per pixel behaves most native.
626 // (NB: Qt expects the unit for delta to be 8 per degree):
627 const int pixelsToDegrees = 2; // 8 * 1/4
628 deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
629 deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
630 deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
632 // carbonEventKind == kEventMouseWheelMoved
633 // Remove acceleration, and use either -120 or 120 as delta:
634 deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120);
635 deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120);
636 deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120);
639 #ifndef QT_NO_WHEELEVENT
640 // ### Qt 5: Send one QWheelEvent with dx, dy and dz
642 if (deltaX != 0 && deltaY != 0)
643 QMacScrollOptimization::initDelayedScroll();
646 QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal);
647 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
651 QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical);
652 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
656 // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
657 // try to be ahead of the pack, I'm adding this extra value.
658 QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3);
659 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
662 if (deltaX != 0 && deltaY != 0)
663 QMacScrollOptimization::performDelayedScroll();
664 #endif //QT_NO_WHEELEVENT
667 - (void)tabletProximity:(NSEvent *)tabletEvent
669 qt_dispatchTabletProximityEvent(tabletEvent);
672 - (void)tabletPoint:(NSEvent *)tabletEvent
674 if (!qt_mac_handleTabletEvent(self, tabletEvent))
675 [super tabletPoint:tabletEvent];
678 - (void)magnifyWithEvent:(NSEvent *)event
680 QPoint qlocal, qglobal;
681 QWidget *widgetToGetGesture = 0;
682 qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
683 if (!widgetToGetGesture)
685 if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
688 #ifndef QT_NO_GESTURES
689 QNativeGestureEvent qNGEvent;
690 qNGEvent.gestureType = QNativeGestureEvent::Zoom;
691 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
692 qNGEvent.position = flipPoint(p).toPoint();
693 qNGEvent.percentage = [event magnification];
694 qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
695 #endif // QT_NO_GESTURES
698 - (void)rotateWithEvent:(NSEvent *)event
700 QPoint qlocal, qglobal;
701 QWidget *widgetToGetGesture = 0;
702 qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
703 if (!widgetToGetGesture)
705 if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
708 #ifndef QT_NO_GESTURES
709 QNativeGestureEvent qNGEvent;
710 qNGEvent.gestureType = QNativeGestureEvent::Rotate;
711 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
712 qNGEvent.position = flipPoint(p).toPoint();
713 qNGEvent.percentage = -[event rotation];
714 qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
715 #endif // QT_NO_GESTURES
718 - (void)swipeWithEvent:(NSEvent *)event
720 QPoint qlocal, qglobal;
721 QWidget *widgetToGetGesture = 0;
722 qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
723 if (!widgetToGetGesture)
725 if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
728 #ifndef QT_NO_GESTURES
729 QNativeGestureEvent qNGEvent;
730 qNGEvent.gestureType = QNativeGestureEvent::Swipe;
731 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
732 qNGEvent.position = flipPoint(p).toPoint();
733 if ([event deltaX] == 1)
734 qNGEvent.angle = 180.0f;
735 else if ([event deltaX] == -1)
736 qNGEvent.angle = 0.0f;
737 else if ([event deltaY] == 1)
738 qNGEvent.angle = 90.0f;
739 else if ([event deltaY] == -1)
740 qNGEvent.angle = 270.0f;
741 qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
742 #endif // QT_NO_GESTURES
745 - (void)beginGestureWithEvent:(NSEvent *)event
747 QPoint qlocal, qglobal;
748 QWidget *widgetToGetGesture = 0;
749 qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
750 if (!widgetToGetGesture)
752 if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
755 #ifndef QT_NO_GESTURES
756 QNativeGestureEvent qNGEvent;
757 qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
758 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
759 qNGEvent.position = flipPoint(p).toPoint();
760 qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
761 #endif // QT_NO_GESTURES
764 - (void)endGestureWithEvent:(NSEvent *)event
766 QPoint qlocal, qglobal;
767 QWidget *widgetToGetGesture = 0;
768 qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
769 if (!widgetToGetGesture)
771 if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
774 #ifndef QT_NO_GESTURES
775 QNativeGestureEvent qNGEvent;
776 qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
777 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
778 qNGEvent.position = flipPoint(p).toPoint();
779 qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
781 #endif // QT_NO_GESTURES
783 - (void)frameDidChange:(NSNotification *)note
788 if (qwidget->isWindow())
790 NSRect newFrame = [self frame];
791 QRect newGeo(newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height);
792 bool moved = qwidget->testAttribute(Qt::WA_Moved);
793 bool resized = qwidget->testAttribute(Qt::WA_Resized);
794 qwidget->setGeometry(newGeo);
795 qwidget->setAttribute(Qt::WA_Moved, moved);
796 qwidget->setAttribute(Qt::WA_Resized, resized);
797 qwidgetprivate->syncCocoaMask();
803 return [super isEnabled];
804 return [super isEnabled] && qwidget->isEnabled();
807 - (void)setEnabled:(BOOL)flag
809 QMacCocoaAutoReleasePool pool;
810 [super setEnabled:flag];
811 if (qwidget && qwidget->isEnabled() != flag)
812 qwidget->setEnabled(flag);
817 return [NSActionCell class];
820 - (BOOL)acceptsFirstResponder
825 // Disabled widget shouldn't get focus even if it's a window.
826 // hence disabled windows will not get any key or mouse events.
827 if (!qwidget->isEnabled())
830 if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) {
831 QWidget *focusWidget = qApp->focusWidget();
833 // There is no focus widget, but we still want to receive key events
834 // for shortcut handling etc. So we accept first responer for the
835 // content view as a last resort:
838 if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) {
839 // The current focus widget is alien, and hence, cannot get acceptsFirstResponder
840 // calls. Since the focus widget is a child of qwidget, we let this view say YES:
843 if (focusWidget->window() != qwidget) {
844 // The current focus widget is in another window. Since cocoa
845 // suggest that this window should be key now, we accept:
850 return qwidget->focusPolicy() != Qt::NoFocus;
853 - (BOOL)resignFirstResponder
858 // Seems like the following test only triggers if this
859 // view is inside a QMacNativeWidget:
860 // if (QWidget *fw = QApplication::focusWidget()) {
861 // if (qwidget == fw || qwidget == fw->nativeParentWidget())
867 - (BOOL)becomeFirstResponder
869 // see the comment in the acceptsFirstResponder - if the window "stole" focus
870 // let it become the responder, but don't tell Qt
871 if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded
872 && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus)
873 qwidget->setFocus(Qt::OtherFocusReason);
877 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
880 return supportedActions;
883 - (void)setSupportedActions:(NSDragOperation)actions
885 supportedActions = actions;
888 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
892 macCurrentDnDParameters()->performedAction = operation;
893 if (QDragManager::self()->object
894 && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) {
895 macCurrentDnDParameters()->performedAction =
896 qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action);
900 - (QWidget *)qt_qwidget
905 - (void) qt_clearQWidget
911 - (void)keyDown:(NSEvent *)theEvent
915 QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
919 sendKeyEvents = true;
921 if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)
922 && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly
923 || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly
924 || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) {
925 fromKeyDownEvent = true;
926 [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]];
927 fromKeyDownEvent = false;
930 if (sendKeyEvents && !composing) {
931 bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
932 if (!keyEventEaten && qwidget) {
933 // The event is not yet eaten, and if Qt is embedded inside a native
934 // cocoa application, send it to first responder not owned by Qt.
935 // The exception is if widgetToGetKey was redirected to a popup.
936 QWidget *toplevel = qwidget->window();
937 if (toplevel == widgetToGetKey->window()) {
938 if (qt_widget_private(toplevel)->topData()->embedded) {
939 if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
940 [w keyDown:theEvent];
948 - (void)keyUp:(NSEvent *)theEvent
951 QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
955 bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
956 if (!keyEventEaten && qwidget) {
957 // The event is not yet eaten, and if Qt is embedded inside a native
958 // cocoa application, send it to first responder not owned by Qt.
959 // The exception is if widgetToGetKey was redirected to a popup.
960 QWidget *toplevel = qwidget->window();
961 if (toplevel == widgetToGetKey->window()) {
962 if (qt_widget_private(toplevel)->topData()->embedded) {
963 if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
971 - (void)viewWillMoveToWindow:(NSWindow *)window
976 if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
977 && (window != [self window])) { // OpenGL Widget
978 QEvent event(QEvent::MacGLClearDrawable);
979 qApp->sendEvent(qwidget, &event);
983 - (void)viewDidMoveToWindow
988 if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
989 // call update paint event
990 qwidgetprivate->needWindowChange = true;
991 QEvent event(QEvent::MacGLWindowChange);
992 qApp->sendEvent(qwidget, &event);
997 // NSTextInput Protocol implementation
999 - (void) insertText:(id)aString
1002 if ([aString length]) {
1003 if ([aString isKindOfClass:[NSAttributedString class]]) {
1004 commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
1006 commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
1010 // When entering characters through Character Viewer or Keyboard Viewer, the text is passed
1011 // through this insertText method. Since we dont receive a keyDown Event in such cases, the
1012 // composing flag will be false.
1013 if (([aString length] && composing) || !fromKeyDownEvent) {
1014 // Send the commit string to the widget.
1016 sendKeyEvents = false;
1017 QInputMethodEvent e;
1018 e.setCommitString(commitText);
1019 if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
1020 qt_sendSpontaneousEvent(widgetToGetKey, &e);
1022 // The key sequence "`q" on a French Keyboard will generate two calls to insertText before
1023 // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept
1024 // the "`" key. The last keyDown event needs to be processed by the widget to get the
1025 // character "q". The string parameter is ignored for the second call.
1026 sendKeyEvents = true;
1029 composingText->clear();
1032 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
1034 // Generate the QInputMethodEvent with preedit string and the attributes
1035 // for rendering it. The attributes handled here are 'underline',
1036 // 'underline color' and 'cursor position'.
1037 sendKeyEvents = false;
1040 // Cursor position is retrived from the range.
1041 QList<QInputMethodEvent::Attribute> attrs;
1042 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location + selRange.length, 1, QVariant());
1043 if ([aString isKindOfClass:[NSAttributedString class]]) {
1044 qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
1045 composingLength = qtText.length();
1047 // Create attributes for individual sections of preedit text
1048 while (index < composingLength) {
1049 NSRange effectiveRange;
1050 NSRange range = NSMakeRange(index, composingLength-index);
1051 NSDictionary *attributes = [aString attributesAtIndex:index
1052 longestEffectiveRange:&effectiveRange
1054 NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
1055 if (underlineStyle) {
1056 QColor clr (Qt::black);
1057 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
1059 clr = colorFrom(color);
1061 QTextCharFormat format;
1062 format.setFontUnderline(true);
1063 format.setUnderlineColor(clr);
1064 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
1065 effectiveRange.location,
1066 effectiveRange.length,
1069 index = effectiveRange.location + effectiveRange.length;
1072 // No attributes specified, take only the preedit text.
1073 qtText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
1074 composingLength = qtText.length();
1076 // Make sure that we have at least one text format.
1077 if (attrs.size() <= 1) {
1078 QTextCharFormat format;
1079 format.setFontUnderline(true);
1080 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
1081 0, composingLength, format);
1083 *composingText = qtText;
1085 QInputMethodEvent e(qtText, attrs);
1086 if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
1087 qt_sendSpontaneousEvent(widgetToGetKey, &e);
1089 if (!composingLength)
1096 QInputMethodEvent e;
1097 e.setCommitString(*composingText);
1098 if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
1099 qt_sendSpontaneousEvent(widgetToGetKey, &e);
1101 composingText->clear();
1105 - (BOOL) hasMarkedText
1107 return (composing ? YES: NO);
1110 - (void) doCommandBySelector:(SEL)aSelector
1112 Q_UNUSED(aSelector);
1120 - (NSInteger) conversationIdentifier
1122 // Return a unique identifier fot this ime conversation
1123 return (NSInteger)self;
1126 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
1128 QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
1129 if (!selectedText.isEmpty()) {
1130 QCFString string(selectedText.mid(theRange.location, theRange.length));
1131 const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
1132 return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
1138 - (NSRange) markedRange
1143 range.length = composingLength;
1145 range.location = NSNotFound;
1151 - (NSRange) selectedRange
1154 QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
1155 if (!selectedText.isEmpty()) {
1156 // Consider only the selected text.
1157 selRange.location = 0;
1158 selRange.length = selectedText.length();
1160 // No selected text.
1161 selRange.location = NSNotFound;
1162 selRange.length = 0;
1168 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
1171 // The returned rect is always based on the internal cursor.
1172 QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
1173 if (!widgetToGetKey)
1176 QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect());
1177 QPoint mp(widgetToGetKey->mapToGlobal(QPoint(mr.bottomLeft())));
1179 rect.origin.x = mp.x();
1180 rect.origin.y = flipYCoordinate(mp.y());
1181 rect.size.width = mr.width();
1182 rect.size.height = mr.height();
1186 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1188 // We dont support cursor movements using mouse while composing.
1193 - (NSArray*) validAttributesForMarkedText
1195 QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
1196 if (!widgetToGetKey)
1199 if (!widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled))
1200 return nil; // Not sure if that's correct, but it's saves a malloc.
1202 // Support only underline color/style.
1203 return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
1204 NSUnderlineStyleAttributeName, nil];
1209 void QMacInputContext::reset()
1211 QWidget *w = QInputContext::focusWidget();
1213 NSView *view = qt_mac_effectiveview_for(w);
1214 if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
1215 QMacCocoaAutoReleasePool pool;
1216 QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
1217 NSInputManager *currentIManager = [NSInputManager currentInputManager];
1218 if (currentIManager) {
1219 [currentIManager markedTextAbandoned:view];
1226 bool QMacInputContext::isComposing() const
1228 QWidget *w = QInputContext::focusWidget();
1230 NSView *view = qt_mac_effectiveview_for(w);
1231 if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
1232 return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing];
1238 extern bool qt_mac_in_drag;
1239 void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
1240 static const int default_pm_hotx = -2;
1241 static const int default_pm_hoty = -16;
1242 static const char* default_pm[] = {
1258 Qt::DropAction QDragManager::drag(QDrag *o)
1260 if(qt_mac_in_drag) { //just make sure..
1261 qWarning("Qt: Internal error: WH0A, unexpected condition reached");
1262 return Qt::IgnoreAction;
1265 return Qt::IgnoreAction;
1266 /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
1267 so we just bail early to prevent it */
1268 if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
1269 return Qt::IgnoreAction;
1272 dragPrivate()->source->removeEventFilter(this);
1274 beingCancelled = false;
1278 dragPrivate()->target = 0;
1280 #ifndef QT_NO_ACCESSIBILITY
1281 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
1285 QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
1286 dragPrivate()->data->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
1287 dragBoard.setMimeData(dragPrivate()->data);
1291 QPixmap pix = dragPrivate()->pixmap;
1293 if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
1295 QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
1296 : dragPrivate()->data->urls().first().toString();
1298 s = s.left(23) + QChar(0x2026);
1301 QFont f(qApp->font());
1304 QPixmap tmp(fm.width(s), fm.height());
1307 p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
1308 p.setPen(Qt::color1);
1310 p.drawText(0, fm.ascent(), s);
1313 hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
1317 pix = QPixmap(default_pm);
1318 hotspot = QPoint(default_pm_hotx, default_pm_hoty);
1321 hotspot = dragPrivate()->hotspot;
1324 // Convert the image to NSImage:
1325 NSImage *image = (NSImage *)qt_mac_create_nsimage(pix);
1328 DnDParams *dndParams = macCurrentDnDParameters();
1329 QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view);
1331 // Save supported actions:
1332 [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)];
1333 QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint);
1334 NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()};
1335 NSSize mouseOffset = {0.0, 0.0};
1336 NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1337 dragPrivate()->executed_action = Qt::ActionMask;
1339 // Execute the drag:
1341 [theView dragImage:image
1344 event:dndParams->theEvent
1349 // Reset the implicit grab widget when drag ends because we will not
1350 // receive the mouse release event when DND is active:
1355 dragPrivate()->executed_action = Qt::IgnoreAction;
1357 Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction));
1359 // Do post drag processing, if required.
1360 if (performedAction != Qt::IgnoreAction) {
1361 // Check if the receiver points us to a file location.
1362 // if so, we need to do the file copy/move ourselves.
1363 QCFType<CFURLRef> pasteLocation = 0;
1364 PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
1365 if (pasteLocation) {
1366 QList<QUrl> urls = o->mimeData()->urls();
1367 for (int i = 0; i < urls.size(); ++i) {
1368 QUrl fromUrl = urls.at(i);
1369 QString filename = QFileInfo(fromUrl.path()).fileName();
1370 QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
1371 if (performedAction == Qt::MoveAction)
1372 QFile::rename(fromUrl.path(), toUrl.path());
1373 else if (performedAction == Qt::CopyAction)
1374 QFile::copy(fromUrl.path(), toUrl.path());
1382 return performedAction;