--- a/src/gui/kernel/qcocoaview_mac.mm Mon Jun 21 22:38:13 2010 +0100
+++ b/src/gui/kernel/qcocoaview_mac.mm Thu Jul 22 16:41:55 2010 +0100
@@ -81,24 +81,9 @@
extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm
extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
-extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp
extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
-
-Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
-{
- if (buttonNum == 0)
- return Qt::LeftButton;
- if (buttonNum == 1)
- return Qt::RightButton;
- if (buttonNum == 2)
- return Qt::MidButton;
- if (buttonNum == 3)
- return Qt::XButton1;
- if (buttonNum == 4)
- return Qt::XButton2;
- return Qt::NoButton;
-}
+extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
struct dndenum_mapper
{
@@ -200,6 +185,9 @@
extern NSString *NSTextInputReplacementRangeAttributeName;
}
+#ifdef ALIEN_DEBUG
+static int qCocoaViewCount = 0;
+#endif
@implementation QT_MANGLE_NAMESPACE(QCocoaView)
@@ -209,10 +197,16 @@
if (self) {
[self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
}
+ [self setFocusRingType:NSFocusRingTypeNone];
composingText = new QString();
+
+#ifdef ALIEN_DEBUG
+ ++qCocoaViewCount;
+ qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount;
+#endif
+
composing = false;
sendKeyEvents = true;
- currentCustomTypes = 0;
[self setHidden:YES];
return self;
}
@@ -227,36 +221,12 @@
object:self];
}
--(void)registerDragTypes
-{
- QMacCocoaAutoReleasePool pool;
- // Calling registerForDraggedTypes is slow, so only do it once for each widget
- // or when the custom types change.
- const QStringList& customTypes = qEnabledDraggedTypes();
- if (currentCustomTypes == 0 || *currentCustomTypes != customTypes) {
- if (currentCustomTypes == 0)
- currentCustomTypes = new QStringList();
- *currentCustomTypes = customTypes;
- const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
- NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
- NSFilenamesPboardType, NSStringPboardType,
- NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
- NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
- NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
- NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
- NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
- NSFilesPromisePboardType, NSInkTextPboardType,
- NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
- // Add custom types supported by the application.
- for (int i = 0; i < customTypes.size(); i++) {
- [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))];
- }
- [self registerForDraggedTypes:supportedTypes];
- }
-}
-
- (void)resetCursorRects
{
+ // [NSView addCursorRect] is slow, so bail out early if we can:
+ if (NSIsEmptyRect([self visibleRect]))
+ return;
+
QWidget *cursorWidget = qwidget;
if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents))
@@ -272,7 +242,8 @@
QRegion mask = qt_widget_private(cursorWidget)->extra->mask;
NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor()));
- if (mask.isEmpty()) {
+ // The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask.
+ if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) {
[self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor];
} else {
const QVector<QRect> &rects = mask.rects();
@@ -300,15 +271,9 @@
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
- if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false)
- return NSDragOperationNone;
+ // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
+ // from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
- if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
- // pass the drag enter event to the view underneath.
- NSView *candidateView = [[[self window] contentView] hitTest:windowPoint];
- if (candidateView && candidateView != self)
- return [candidateView draggingEntered:sender];
- }
dragEnterSequence = [sender draggingSequenceNumber];
[self addDropData:sender];
QMimeData *mimeData = dropData;
@@ -361,13 +326,9 @@
}
- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
{
+ // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
+ // from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
- if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
- // pass the drag move event to the view underneath.
- NSView *candidateView = [[[self window] contentView] hitTest:windowPoint];
- if (candidateView && candidateView != self)
- return [candidateView draggingUpdated:sender];
- }
// in cases like QFocusFrame, the view under the mouse might
// not have received the drag enter. Generate a synthetic
// drag enter event for that view.
@@ -417,14 +378,10 @@
- (void)draggingExited:(id < NSDraggingInfo >)sender
{
+ // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
+ // from Cocoa. They modify the drag target, and might fake enter/leave events.
+ Q_UNUSED(sender);
dragEnterSequence = -1;
- if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
- // try sending the leave event to the last view which accepted drag enter.
- DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
- NSView *candidateView = [[[self window] contentView] hitTest:dndParams->activeDragEnterPos];
- if (candidateView && candidateView != self)
- return [candidateView draggingExited:sender];
- }
// drag enter event was rejected, so ignore the move event.
if (dropData) {
QDragLeaveEvent de;
@@ -435,14 +392,10 @@
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
+ // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
+ // from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
dragEnterSequence = -1;
- if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
- // pass the drop event to the view underneath.
- NSView *candidateView = [[[self window] contentView] hitTest:windowPoint];
- if (candidateView && candidateView != self)
- return [candidateView performDragOperation:sender];
- }
[self addDropData:sender];
NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
@@ -472,13 +425,19 @@
{
delete composingText;
[[NSNotificationCenter defaultCenter] removeObserver:self];
- delete currentCustomTypes;
- [self unregisterDraggedTypes];
+
+#ifdef ALIEN_DEBUG
+ --qCocoaViewCount;
+ qDebug() << "qCocoaViewCount is" << qCocoaViewCount;
+#endif
+
[super dealloc];
}
- (BOOL)isOpaque;
{
+ if (!qwidgetprivate)
+ return [super isOpaque];
return qwidgetprivate->isOpaque;
}
@@ -487,7 +446,11 @@
return YES;
}
-- (BOOL) preservesContentDuringLiveResize;
+// We preserve the content of the view if WA_StaticContents is defined.
+//
+// More info in the Cocoa documentation:
+// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html
+- (BOOL) preservesContentDuringLiveResize
{
return qwidget->testAttribute(Qt::WA_StaticContents);
}
@@ -510,20 +473,36 @@
}
// Make sure the opengl context is updated on resize.
- if (qwidgetprivate->isGLWidget) {
+ if (qwidgetprivate && qwidgetprivate->isGLWidget) {
qwidgetprivate->needWindowChange = true;
QEvent event(QEvent::MacGLWindowChange);
qApp->sendEvent(qwidget, &event);
}
}
+// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint.
+// During the resize, the top of the widget is repainted, probably because of the
+// change of coordinate space (Quartz vs Qt). This is then followed by this message:
+// -[NSView _setNeedsDisplayIfTopLeftChanged]
+// which force a full repaint by sending the message 'setNeedsDisplay:'.
+// That is what we are preventing here.
+- (void)setNeedsDisplay:(BOOL)flag {
+ if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) {
+ [super setNeedsDisplay:flag];
+ }
+}
+
- (void)drawRect:(NSRect)aRect
{
+ if (!qwidget)
+ return;
+
if (QApplicationPrivate::graphicsSystem() != 0) {
- if (QWidgetBackingStore *bs = qwidgetprivate->maybeBackingStore()) {
+ if (qwidgetprivate->maybeBackingStore()) {
// Drawing is handled on the window level
- // See qcocoasharedwindowmethods_mac_p.
- return;
+ // See qcocoasharedwindowmethods_mac_p.h
+ if (!qwidget->testAttribute(Qt::WA_PaintOnScreen))
+ return;
}
}
CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
@@ -535,7 +514,15 @@
qWarning("QWidget::repaint: Recursive repaint detected");
const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
- QRegion qrgn(qrect);
+ QRegion qrgn;
+
+ const NSRect *rects;
+ NSInteger count;
+ [self getRectsBeingDrawn:&rects count:&count];
+ for (int i = 0; i < count; ++i) {
+ QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height);
+ qrgn += tmpRect;
+ }
if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
const QRegion &parentMask = qwidget->window()->mask();
@@ -569,6 +556,10 @@
CGContextClearRect(cg, NSRectToCGRect(aRect));
}
+ // Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this
+ // is the case. This makes sure child widgets are drawn as well, Cocoa does not know about
+ // those and wont send them drawRect calls.
+ if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) {
if (engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground)
&& (qwidget->isWindow() || qwidget->autoFillBackground())
|| qwidget->testAttribute(Qt::WA_TintedBackground)
@@ -588,6 +579,12 @@
e.setErased(true);
#endif
qt_sendSpontaneousEvent(qwidget, &e);
+ } else {
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this
+ QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget);
+ qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0);
+ }
+
if (!redirectionOffset.isNull())
QPainter::restoreRedirected(qwidget);
if (engine)
@@ -603,12 +600,18 @@
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return NO;
+
Q_UNUSED(theEvent);
return !qwidget->testAttribute(Qt::WA_MacNoClickThrough);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
+ if (!qwidget)
+ return [super hitTest:aPoint];
+
if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
return nil; // You cannot hit a transparent for mouse event widget.
return [super hitTest:aPoint];
@@ -616,6 +619,13 @@
- (void)updateTrackingAreas
{
+ if (!qwidget)
+ return;
+
+ // [NSView addTrackingArea] is slow, so bail out early if we can:
+ if (NSIsEmptyRect([self visibleRect]))
+ return;
+
QMacCocoaAutoReleasePool pool;
if (NSArray *trackingArray = [self trackingAreas]) {
NSUInteger size = [trackingArray count];
@@ -645,28 +655,45 @@
- (void)mouseEntered:(NSEvent *)event
{
+ if (!qwidget)
+ return;
if (qwidgetprivate->data.in_destructor)
return;
- QEvent enterEvent(QEvent::Enter);
- NSPoint windowPoint = [event locationInWindow];
- NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
- NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
+
if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
+ QEvent enterEvent(QEvent::Enter);
+ NSPoint windowPoint = [event locationInWindow];
+ NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
+ NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
QApplication::sendEvent(qwidget, &enterEvent);
qt_mouseover = qwidget;
- // Update cursor and dispatch hover events.
+ // Update cursor icon:
qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
- if (qwidget->testAttribute(Qt::WA_Hover) &&
- (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) {
- QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1));
- QApplicationPrivate::instance()->notify_helper(qwidget, &he);
+
+ // Send mouse move and hover events as well:
+ if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) {
+ // This mouse move event should be sendt, even when mouse
+ // tracking is switched off (to trigger tooltips):
+ NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved
+ location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp]
+ windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber]
+ clickCount:0 pressure:0];
+ qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton);
+
+ if (qwidget->testAttribute(Qt::WA_Hover)) {
+ QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1));
+ QApplicationPrivate::instance()->notify_helper(qwidget, &he);
+ }
}
}
}
- (void)mouseExited:(NSEvent *)event
{
+ if (!qwidget)
+ return;
+
QEvent leaveEvent(QEvent::Leave);
NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
@@ -685,6 +712,9 @@
- (void)flagsChanged:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return;
+
QWidget *widgetToGetKey = qwidget;
QWidget *popup = qAppInstance()->activePopupWidget();
@@ -696,6 +726,9 @@
- (void)mouseMoved:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return;
+
// We always enable mouse tracking for all QCocoaView-s. In cases where we have
// child views, we will receive mouseMoved for both parent & the child (if
// mouse is over the child). We need to ignore the parent mouseMoved in such
@@ -815,11 +848,12 @@
// The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
// Since deviceDelta is delivered as pixels rather than degrees, we need to
// convert from pixels to degrees in a sensible manner.
- // It looks like four degrees per pixel behaves most native.
- // Qt expects the unit for delta to be 1/8 of a degree:
- deltaX = [theEvent deviceDeltaX];
- deltaY = [theEvent deviceDeltaY];
- deltaZ = [theEvent deviceDeltaZ];
+ // It looks like 1/4 degrees per pixel behaves most native.
+ // (NB: Qt expects the unit for delta to be 8 per degree):
+ const int pixelsToDegrees = 2; // 8 * 1/4
+ deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
+ deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
+ deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
} else {
// carbonEventKind == kEventMouseWheelMoved
// Remove acceleration, and use either -120 or 120 as delta:
@@ -986,6 +1020,8 @@
- (void)frameDidChange:(NSNotification *)note
{
Q_UNUSED(note);
+ if (!qwidget)
+ return;
if (qwidget->isWindow())
return;
NSRect newFrame = [self frame];
@@ -1009,7 +1045,7 @@
{
QMacCocoaAutoReleasePool pool;
[super setEnabled:flag];
- if (qwidget->isEnabled() != flag)
+ if (qwidget && qwidget->isEnabled() != flag)
qwidget->setEnabled(flag);
}
@@ -1020,17 +1056,39 @@
- (BOOL)acceptsFirstResponder
{
- if (qwidget->isWindow())
+ if (!qwidget)
+ return NO;
+ // disabled widget shouldn't get focus even if it's a window.
+ // hence disabled windows will not get any key or mouse events.
+ if (!qwidget->isEnabled())
+ return NO;
+ // Before accepting the focus for a window, we check that
+ // the focusWidget (if any) is not contained in the same window.
+ if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded
+ && (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) {
return YES; // Always do it, so that windows can accept key press events.
+ }
return qwidget->focusPolicy() != Qt::NoFocus;
}
- (BOOL)resignFirstResponder
{
+ if (!qwidget)
+ return NO;
// Seems like the following test only triggers if this
// view is inside a QMacNativeWidget:
if (qwidget == QApplication::focusWidget())
- QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason);
+ qwidget->clearFocus();
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ // see the comment in the acceptsFirstResponder - if the window "stole" focus
+ // let it become the responder, but don't tell Qt
+ if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded
+ && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus)
+ qwidget->setFocus(Qt::OtherFocusReason);
return YES;
}
@@ -1062,6 +1120,12 @@
return qwidget;
}
+- (void) qt_clearQWidget
+{
+ qwidget = 0;
+ qwidgetprivate = 0;
+}
+
- (BOOL)qt_leftButtonIsRightButton
{
return leftButtonIsRightButton;
@@ -1098,8 +1162,15 @@
}
if (sendKeyEvents && !composing) {
bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
- if (!keyOK && !sendToPopup)
- [super keyDown:theEvent];
+ if (!keyOK && !sendToPopup) {
+ // find the first responder that is not created by Qt and forward
+ // the event to it (for example if Qt widget is embedded into native).
+ QWidget *toplevel = qwidget->window();
+ if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
+ if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
+ [w keyDown:theEvent];
+ }
+ }
}
}
@@ -1108,16 +1179,23 @@
{
if (sendKeyEvents) {
bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget);
- if (!keyOK)
- [super keyUp:theEvent];
+ if (!keyOK) {
+ QWidget *toplevel = qwidget->window();
+ if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
+ if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
+ [w keyUp:theEvent];
+ }
+ }
}
}
- (void)viewWillMoveToWindow:(NSWindow *)window
{
+ if (qwidget == 0)
+ return;
+
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
&& (window != [self window])) { // OpenGL Widget
- // Create a stupid ClearDrawable Event
QEvent event(QEvent::MacGLClearDrawable);
qApp->sendEvent(qwidget, &event);
}
@@ -1125,6 +1203,9 @@
- (void)viewDidMoveToWindow
{
+ if (qwidget == 0)
+ return;
+
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
// call update paint event
qwidgetprivate->needWindowChange = true;
@@ -1320,6 +1401,9 @@
- (NSArray*) validAttributesForMarkedText
{
+ if (qwidget == 0)
+ return nil;
+
if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled))
return nil; // Not sure if that's correct, but it's saves a malloc.
@@ -1470,7 +1554,8 @@
qt_button_down = 0;
[dndParams.view release];
[image release];
- dragPrivate()->executed_action = Qt::IgnoreAction;
+ if (dragPrivate())
+ dragPrivate()->executed_action = Qt::IgnoreAction;
object = 0;
Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction));
// do post drag processing, if required.