--- a/src/gui/kernel/qt_cocoa_helpers_mac.mm Wed Jun 23 19:07:03 2010 +0300
+++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm Tue Jul 06 15:10:48 2010 +0300
@@ -83,6 +83,7 @@
#include <private/qt_cocoa_helpers_mac_p.h>
#include <private/qt_mac_p.h>
#include <private/qapplication_p.h>
+#include <private/qcocoaapplication_mac_p.h>
#include <private/qcocoawindow_mac_p.h>
#include <private/qcocoaview_mac_p.h>
#include <private/qkeymapper_p.h>
@@ -137,9 +138,8 @@
}
extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp;
-extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
extern QWidget * mac_mouse_grabber;
-extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+extern QWidget *qt_button_down; //qapplication_mac.cpp
void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds)
{
@@ -369,6 +369,16 @@
}
#ifdef QT_MAC_USE_COCOA
+
+// Clears the QWidget pointer that each QCocoaView holds.
+void qt_mac_clearCocoaViewQWidgetPointers(QWidget *widget)
+{
+ QT_MANGLE_NAMESPACE(QCocoaView) *cocoaView = reinterpret_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget));
+ if (cocoaView && [cocoaView respondsToSelector:@selector(qt_qwidget)]) {
+ [cocoaView qt_clearQWidget];
+ }
+}
+
void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent)
{
NSEvent *proximityEvent = static_cast<NSEvent *>(tabletEvent);
@@ -642,11 +652,25 @@
UInt32 macScanCode = 1;
QKeyEventEx ke(cocoaEvent2QtEvent([event type]), qtKey, keyMods, text, [event isARepeat], qMax(1, keyLength),
macScanCode, [event keyCode], [event modifierFlags]);
- qt_sendSpontaneousEvent(widgetToGetEvent, &ke);
- return ke.isAccepted();
+ return qt_sendSpontaneousEvent(widgetToGetEvent, &ke) && ke.isAccepted();
}
#endif
+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;
+}
+
// Helper to share code between QCocoaWindow and QCocoaView
bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
{
@@ -659,8 +683,16 @@
EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
Q_ASSERT(key_event);
if ([event type] == NSKeyDown) {
- qt_keymapper_private()->updateKeyMap(0, key_event, 0);
+ NSString *characters = [event characters];
+ unichar value = [characters characterAtIndex:0];
+ qt_keymapper_private()->updateKeyMap(0, key_event, (void *)&value);
}
+
+ // Redirect keys to alien widgets.
+ if (widgetToGetEvent->testAttribute(Qt::WA_NativeWindow) == false) {
+ widgetToGetEvent = qApp->focusWidget();
+ }
+
if (widgetToGetEvent == 0)
return false;
@@ -670,8 +702,8 @@
if (mustUseCocoaKeyEvent())
return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent);
bool isAccepted;
- qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &isAccepted, true);
- return isAccepted;
+ bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &isAccepted, true);
+ return consumed && isAccepted;
#endif
}
@@ -850,7 +882,15 @@
}
}
- QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, button, keyMods);
+ Qt::MouseButtons buttons = 0;
+ {
+ UInt32 mac_buttons;
+ if (GetEventParameter((EventRef)[event eventRef], kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons) == noErr)
+ buttons = qt_mac_get_buttons(mac_buttons);
+ }
+
+ QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods);
qt_sendSpontaneousEvent(widgetToGetEvent, &qme);
// We don't need to set the implicit grab widget here because we won't
@@ -915,7 +955,7 @@
[static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(tmpView) qt_qwidget];
}
} else {
- extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+ extern QWidget * qt_button_down; //qapplication_mac.cpp
QPoint pos;
widgetToGetMouse = QApplicationPrivate::pickMouseReceiver(qwidget, qglobalPoint,
pos, eventType,
@@ -927,7 +967,20 @@
return false;
NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil];
- QPoint qlocalPoint(localPoint.x, localPoint.y);
+ QPoint qlocalPoint = QPoint(localPoint.x, localPoint.y);
+
+ // Search for alien child widgets (either on this qwidget or on the popup)
+ if (widgetToGetMouse->testAttribute(Qt::WA_NativeWindow) == false || qt_widget_private(widgetToGetMouse)->hasAlienChildren) {
+ QPoint qScreenPoint = flipPoint(globalPoint).toPoint();
+#ifdef ALIEN_DEBUG
+ qDebug() << "alien mouse event" << qScreenPoint << possibleAlien;
+#endif
+ QWidget *possibleAlien = widgetToGetMouse->childAt(qlocalPoint);
+ if (possibleAlien) {
+ qlocalPoint = possibleAlien->mapFromGlobal(widgetToGetMouse->mapToGlobal(qlocalPoint));
+ widgetToGetMouse = possibleAlien;
+ }
+ }
EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef]));
if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent))
@@ -955,7 +1008,7 @@
#ifndef QT_NAMESPACE
Q_ASSERT(clickCount > 0);
#endif
- if (clickCount % 2 == 0)
+ if (clickCount % 2 == 0 && buttons == button)
eventType = QEvent::MouseButtonDblClick;
if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
button = Qt::RightButton;
@@ -967,11 +1020,24 @@
button = Qt::RightButton;
[theView qt_setLeftButtonIsRightButton: false];
}
+ qt_button_down = 0;
break;
}
[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint;
QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods);
- qt_sendSpontaneousEvent(widgetToGetMouse, &qme);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "sending mouse event to" << widgetToGetMouse;
+#endif
+ extern QWidget *qt_button_down;
+ extern QPointer<QWidget> qt_last_mouse_receiver;
+
+ if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false)
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qme);
+ else
+ QApplicationPrivate::sendMouseEvent(widgetToGetMouse, &qme, widgetToGetMouse, qwidget, &qt_button_down,
+ qt_last_mouse_receiver);
+
if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) {
QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods);
qt_sendSpontaneousEvent(widgetToGetMouse, &qcme);
@@ -1097,15 +1163,81 @@
#endif
}
+#if QT_MAC_USE_COCOA
+void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget)
+{
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ if(!theWindow)
+ return;
+ id theClass = [[[theWindow contentView] superview] class];
+ // What we do here is basically to add a new selector to NSThemeFrame called
+ // "drawRectOriginal:" which will contain the original implementation of
+ // "drawRect:". After that we get the new implementation from QCocoaWindow
+ // and exchange them. The new implementation is called drawRectSpecial.
+ // We cannot just add the method because it might have been added before and since
+ // we cannot remove a method once it has been added we need to ask QCocoaWindow if
+ // we did the swap or not.
+ if(!widget->drawRectOriginalAdded) {
+ Method m2 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m2) {
+ // This case is pretty extreme, no drawRect means no drawing!
+ return;
+ }
+ class_addMethod(theClass, @selector(drawRectOriginal:), method_getImplementation(m2), method_getTypeEncoding(m2));
+ widget->drawRectOriginalAdded = true;
+ }
+ if(widget->originalDrawMethod) {
+ Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:));
+ if(!m0) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m1) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ // We have the original method here. Proceed and swap the methods.
+ method_exchangeImplementations(m1, m0);
+ widget->originalDrawMethod = false;
+ [window display];
+ }
+}
+
+void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget)
+{
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ id theClass = [[[theWindow contentView] superview] class];
+ // Now we need to revert the methods to their original state.
+ // We cannot remove the method, so we just keep track of it in QCocoaWindow.
+ Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:));
+ if(!m0) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m1) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ method_exchangeImplementations(m1, m0);
+ widget->originalDrawMethod = true;
+ [window display];
+}
+#endif // QT_MAC_USE_COCOA
+
void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show)
{
+ if(!window)
+ return;
#if QT_MAC_USE_COCOA
QMacCocoaAutoReleasePool pool;
OSWindowRef theWindow = static_cast<OSWindowRef>(window);
NSToolbar *macToolbar = [theWindow toolbar];
- if (macToolbar)
- [macToolbar setShowsBaselineSeparator: show];
-#endif
+ [macToolbar setShowsBaselineSeparator:show];
+#endif // QT_MAC_USE_COCOA
}
QStringList qt_mac_NSArrayToQStringList(void *nsarray)
@@ -1290,6 +1422,41 @@
QMacCocoaAutoReleasePool pool;
[static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set];
}
+
+// WARNING: If Qt did not create NSApplication (e.g. in case it is
+// used as a plugin), and at the same time, there is no window on
+// screen (or the window that the event is sendt to becomes hidden etc
+// before the event gets delivered), the message will not be performed.
+bool qt_cocoaPostMessage(id target, SEL selector)
+{
+ if (!target)
+ return false;
+
+ NSInteger windowNumber = 0;
+ if (![NSApp isMemberOfClass:[QNSApplication class]]) {
+ // INVARIANT: Cocoa is not using our NSApplication subclass. That means
+ // we don't control the main event handler either. So target the event
+ // for one of the windows on screen:
+ NSWindow *nswin = [NSApp mainWindow];
+ if (!nswin) {
+ nswin = [NSApp keyWindow];
+ if (!nswin)
+ return false;
+ }
+ windowNumber = [nswin windowNumber];
+ }
+
+ // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5!
+ // That is why we need to split the address in two parts:
+ QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector);
+ quint32 lower = quintptr(args);
+ quint32 upper = quintptr(args) >> 32;
+ NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined
+ location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:windowNumber
+ context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper];
+ [NSApp postEvent:e atStart:NO];
+ return true;
+}
#endif
QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool()
@@ -1305,4 +1472,60 @@
[(NSAutoreleasePool*)pool release];
}
+void qt_mac_post_retranslateAppMenu()
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ qt_cocoaPostMessage([NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)], @selector(qtTranslateApplicationMenu));
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+// This method implements the magic for the drawRectSpecial method.
+// We draw a line at the upper edge of the content view in order to
+// override the title baseline.
+void macDrawRectOnTop(void * /*OSWindowRef */window)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ NSView *contentView = [theWindow contentView];
+ if(!contentView)
+ return;
+ // Get coordinates of the content view
+ NSRect contentRect = [contentView frame];
+ // Draw a line on top of the already drawn line.
+ // We need to check if we are active or not to use the proper color.
+ if([window isKeyWindow] || [window isMainWindow]) {
+ [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
+ } else {
+ [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
+ }
+ NSPoint origin = NSMakePoint(0, contentRect.size.height);
+ NSPoint end = NSMakePoint(contentRect.size.width, contentRect.size.height);
+ [NSBezierPath strokeLineFromPoint:origin toPoint:end];
+}
+
+// This method will (or at least should) get called only once.
+// Its mission is to find out if we are active or not. If we are active
+// we assume that we were launched via finder, otherwise we assume
+// we were called from the command line. The distinction is important,
+// since in the first case we don't need to trigger a paintEvent, while
+// in the second case we do.
+void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ NSApplication *application = [NSApplication sharedApplication];
+ NSToolbar *toolbar = [window toolbar];
+ if([application isActive]) {
+ // Launched from finder
+ [toolbar setShowsBaselineSeparator:NO];
+ } else {
+ // Launched from commandline
+ [toolbar setVisible:false];
+ [toolbar setShowsBaselineSeparator:NO];
+ [toolbar setVisible:true];
+ [theWindow display];
+ }
+}
+#endif // QT_MAC_USE_COCOA
+
QT_END_NAMESPACE