summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qt_cocoa_helpers_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qt_cocoa_helpers_mac.mm')
-rw-r--r--src/gui/kernel/qt_cocoa_helpers_mac.mm714
1 files changed, 492 insertions, 222 deletions
diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm
index 8e1159e..97ed7f7 100644
--- a/src/gui/kernel/qt_cocoa_helpers_mac.mm
+++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm
@@ -79,6 +79,7 @@
#include <qdesktopwidget.h>
#include <qevent.h>
#include <qpixmapcache.h>
+#include <qvarlengtharray.h>
#include <private/qevent_p.h>
#include <private/qt_cocoa_helpers_mac_p.h>
#include <private/qt_mac_p.h>
@@ -88,9 +89,16 @@
#include <private/qcocoaview_mac_p.h>
#include <private/qkeymapper_p.h>
#include <private/qwidget_p.h>
+#include <private/qcocoawindow_mac_p.h>
QT_BEGIN_NAMESPACE
+#ifdef QT_MAC_USE_COCOA
+// Cmd + left mousebutton should produce a right button
+// press (mainly for mac users with one-button mice):
+static bool qt_leftButtonIsRightButton = false;
+#endif
+
Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader);
QMacWindowFader::QMacWindowFader()
@@ -140,6 +148,9 @@ void QMacWindowFader::performFade()
extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp;
extern QWidget * mac_mouse_grabber;
extern QWidget *qt_button_down; //qapplication_mac.cpp
+extern QPointer<QWidget> qt_last_mouse_receiver;
+extern OSViewRef qt_mac_effectiveview_for(const QWidget *w);
+extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm
void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds)
{
@@ -166,6 +177,70 @@ void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds)
}
}
}
+struct dndenum_mapper
+{
+ NSDragOperation mac_code;
+ Qt::DropAction qt_code;
+ bool Qt2Mac;
+};
+
+#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__)
+
+static dndenum_mapper dnd_enums[] = {
+ { NSDragOperationLink, Qt::LinkAction, true },
+ { NSDragOperationMove, Qt::MoveAction, true },
+ { NSDragOperationCopy, Qt::CopyAction, true },
+ { NSDragOperationGeneric, Qt::CopyAction, false },
+ { NSDragOperationEvery, Qt::ActionMask, false },
+ { NSDragOperationNone, Qt::IgnoreAction, false }
+};
+
+NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
+{
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
+ return dnd_enums[i].mac_code;
+ }
+ }
+ return NSDragOperationNone;
+}
+
+NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
+{
+ NSDragOperation nsActions = NSDragOperationNone;
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
+ nsActions |= dnd_enums[i].mac_code;
+ }
+ return nsActions;
+}
+
+Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
+{
+ Qt::DropAction action = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ return dnd_enums[i].qt_code;
+ }
+ return action;
+}
+
+Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ actions |= dnd_enums[i].qt_code;
+ }
+ return actions;
+}
+
+Q_GLOBAL_STATIC(DnDParams, currentDnDParameters);
+DnDParams *macCurrentDnDParameters()
+{
+ return currentDnDParameters();
+}
+#endif
bool macWindowIsTextured( void * /*OSWindowRef*/ window )
{
@@ -300,7 +375,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget)
HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip);
return (nativeSizeGrip != 0);
#else
- return [[reinterpret_cast<NSView *>(widget->winId()) window] showsResizeIndicator];
+ return [[reinterpret_cast<NSView *>(widget->effectiveWinId()) window] showsResizeIndicator];
#endif
}
struct qt_mac_enum_mapper
@@ -470,7 +545,6 @@ void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec)
qt_sendSpontaneousEvent(qApp, &qtabletProximity);
}
-#ifdef QT_MAC_USE_COCOA
// Use this method to keep all the information in the TextSegment. As long as it is ordered
// we are in OK shape, and we can influence that ourselves.
struct KeyPair
@@ -494,69 +568,111 @@ bool operator<(QChar qchar, const KeyPair &entry)
return qchar < entry.cocoaKey;
}
+bool operator<(const Qt::Key &key, const KeyPair &entry)
+{
+ return key < entry.qtKey;
+}
+
+bool operator<(const KeyPair &entry, const Qt::Key &key)
+{
+ return entry.qtKey < key;
+}
+
+static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
+{
+ return entry1.qtKey < entry2.qtKey;
+}
+
+static const int NumEntries = 59;
+static const KeyPair entries[NumEntries] = {
+ { NSEnterCharacter, Qt::Key_Enter },
+ { NSBackspaceCharacter, Qt::Key_Backspace },
+ { NSTabCharacter, Qt::Key_Tab },
+ { NSNewlineCharacter, Qt::Key_Return },
+ { NSCarriageReturnCharacter, Qt::Key_Return },
+ { NSBackTabCharacter, Qt::Key_Backtab },
+ { kEscapeCharCode, Qt::Key_Escape },
+ // Cocoa sends us delete when pressing backspace!
+ // (NB when we reverse this list in qtKey2CocoaKey, there
+ // will be two indices of Qt::Key_Backspace. But is seems to work
+ // ok for menu shortcuts (which uses that function):
+ { NSDeleteCharacter, Qt::Key_Backspace },
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ { NSF1FunctionKey, Qt::Key_F1 },
+ { NSF2FunctionKey, Qt::Key_F2 },
+ { NSF3FunctionKey, Qt::Key_F3 },
+ { NSF4FunctionKey, Qt::Key_F4 },
+ { NSF5FunctionKey, Qt::Key_F5 },
+ { NSF6FunctionKey, Qt::Key_F6 },
+ { NSF7FunctionKey, Qt::Key_F7 },
+ { NSF8FunctionKey, Qt::Key_F8 },
+ { NSF9FunctionKey, Qt::Key_F8 },
+ { NSF10FunctionKey, Qt::Key_F10 },
+ { NSF11FunctionKey, Qt::Key_F11 },
+ { NSF12FunctionKey, Qt::Key_F12 },
+ { NSF13FunctionKey, Qt::Key_F13 },
+ { NSF14FunctionKey, Qt::Key_F14 },
+ { NSF15FunctionKey, Qt::Key_F15 },
+ { NSF16FunctionKey, Qt::Key_F16 },
+ { NSF17FunctionKey, Qt::Key_F17 },
+ { NSF18FunctionKey, Qt::Key_F18 },
+ { NSF19FunctionKey, Qt::Key_F19 },
+ { NSF20FunctionKey, Qt::Key_F20 },
+ { NSF21FunctionKey, Qt::Key_F21 },
+ { NSF22FunctionKey, Qt::Key_F22 },
+ { NSF23FunctionKey, Qt::Key_F23 },
+ { NSF24FunctionKey, Qt::Key_F24 },
+ { NSF25FunctionKey, Qt::Key_F25 },
+ { NSF26FunctionKey, Qt::Key_F26 },
+ { NSF27FunctionKey, Qt::Key_F27 },
+ { NSF28FunctionKey, Qt::Key_F28 },
+ { NSF29FunctionKey, Qt::Key_F29 },
+ { NSF30FunctionKey, Qt::Key_F30 },
+ { NSF31FunctionKey, Qt::Key_F31 },
+ { NSF32FunctionKey, Qt::Key_F32 },
+ { NSF33FunctionKey, Qt::Key_F33 },
+ { NSF34FunctionKey, Qt::Key_F34 },
+ { NSF35FunctionKey, Qt::Key_F35 },
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSHelpFunctionKey, Qt::Key_Help },
+};
+static const KeyPair * const end = entries + NumEntries;
+
+QChar qtKey2CocoaKey(Qt::Key key)
+{
+ // The first time this function is called, create a reverse
+ // looup table sorted on Qt Key rather than Cocoa key:
+ static QVector<KeyPair> rev_entries(NumEntries);
+ static bool mustInit = true;
+ if (mustInit){
+ mustInit = false;
+ for (int i=0; i<NumEntries; ++i)
+ rev_entries[i] = entries[i];
+ qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
+ }
+ const QVector<KeyPair>::iterator i
+ = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
+ if (i == rev_entries.end())
+ return QChar();
+ return i->cocoaKey;
+}
+
+#ifdef QT_MAC_USE_COCOA
static Qt::Key cocoaKey2QtKey(QChar keyCode)
{
- static const int NumEntries = 57;
- static const KeyPair entries[NumEntries] = {
- { NSEnterCharacter, Qt::Key_Enter },
- { NSTabCharacter, Qt::Key_Tab },
- { NSCarriageReturnCharacter, Qt::Key_Return },
- { NSBackTabCharacter, Qt::Key_Backtab },
- { kEscapeCharCode, Qt::Key_Escape },
- { NSDeleteCharacter, Qt::Key_Backspace },
- { NSUpArrowFunctionKey, Qt::Key_Up },
- { NSDownArrowFunctionKey, Qt::Key_Down },
- { NSLeftArrowFunctionKey, Qt::Key_Left },
- { NSRightArrowFunctionKey, Qt::Key_Right },
- { NSF1FunctionKey, Qt::Key_F1 },
- { NSF2FunctionKey, Qt::Key_F2 },
- { NSF3FunctionKey, Qt::Key_F3 },
- { NSF4FunctionKey, Qt::Key_F4 },
- { NSF5FunctionKey, Qt::Key_F5 },
- { NSF6FunctionKey, Qt::Key_F6 },
- { NSF7FunctionKey, Qt::Key_F7 },
- { NSF8FunctionKey, Qt::Key_F8 },
- { NSF9FunctionKey, Qt::Key_F8 },
- { NSF10FunctionKey, Qt::Key_F10 },
- { NSF11FunctionKey, Qt::Key_F11 },
- { NSF12FunctionKey, Qt::Key_F12 },
- { NSF13FunctionKey, Qt::Key_F13 },
- { NSF14FunctionKey, Qt::Key_F14 },
- { NSF15FunctionKey, Qt::Key_F15 },
- { NSF16FunctionKey, Qt::Key_F16 },
- { NSF17FunctionKey, Qt::Key_F17 },
- { NSF18FunctionKey, Qt::Key_F18 },
- { NSF19FunctionKey, Qt::Key_F19 },
- { NSF20FunctionKey, Qt::Key_F20 },
- { NSF21FunctionKey, Qt::Key_F21 },
- { NSF22FunctionKey, Qt::Key_F22 },
- { NSF23FunctionKey, Qt::Key_F23 },
- { NSF24FunctionKey, Qt::Key_F24 },
- { NSF25FunctionKey, Qt::Key_F25 },
- { NSF26FunctionKey, Qt::Key_F26 },
- { NSF27FunctionKey, Qt::Key_F27 },
- { NSF28FunctionKey, Qt::Key_F28 },
- { NSF29FunctionKey, Qt::Key_F29 },
- { NSF30FunctionKey, Qt::Key_F30 },
- { NSF31FunctionKey, Qt::Key_F31 },
- { NSF32FunctionKey, Qt::Key_F32 },
- { NSF33FunctionKey, Qt::Key_F33 },
- { NSF34FunctionKey, Qt::Key_F34 },
- { NSF35FunctionKey, Qt::Key_F35 },
- { NSInsertFunctionKey, Qt::Key_Insert },
- { NSDeleteFunctionKey, Qt::Key_Delete },
- { NSHomeFunctionKey, Qt::Key_Home },
- { NSEndFunctionKey, Qt::Key_End },
- { NSPageUpFunctionKey, Qt::Key_PageUp },
- { NSPageDownFunctionKey, Qt::Key_PageDown },
- { NSPrintScreenFunctionKey, Qt::Key_Print },
- { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
- { NSPauseFunctionKey, Qt::Key_Pause },
- { NSSysReqFunctionKey, Qt::Key_SysReq },
- { NSMenuFunctionKey, Qt::Key_Menu },
- { NSHelpFunctionKey, Qt::Key_Help },
- };
- static const KeyPair * const end = entries + NumEntries;
const KeyPair *i = qBinaryFind(entries, end, keyCode);
if (i == end)
return Qt::Key(keyCode.unicode());
@@ -579,6 +695,27 @@ Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags)
return qtMods;
}
+NSString *qt_mac_removePrivateUnicode(NSString* string)
+{
+ int len = [string length];
+ if (len) {
+ QVarLengthArray <unichar, 10> characters(len);
+ bool changed = false;
+ for (int i = 0; i<len; i++) {
+ characters[i] = [string characterAtIndex:i];
+ // check if they belong to key codes in private unicode range
+ // currently we need to handle only the NSDeleteFunctionKey
+ if (characters[i] == NSDeleteFunctionKey) {
+ characters[i] = NSDeleteCharacter;
+ changed = true;
+ }
+ }
+ if (changed)
+ return [NSString stringWithCharacters:characters.data() length:len];
+ }
+ return string;
+}
+
Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations)
{
Qt::KeyboardModifiers qtMods =Qt::NoModifier;
@@ -672,7 +809,6 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
return Qt::NoButton;
}
-// Helper to share code between QCocoaWindow and QCocoaView
bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
{
#ifndef QT_MAC_USE_COCOA
@@ -684,6 +820,7 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve
EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
Q_ASSERT(key_event);
unsigned int info = 0;
+
if ([event type] == NSKeyDown) {
NSString *characters = [event characters];
if ([characters length]) {
@@ -693,19 +830,12 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve
}
}
- // Redirect keys to alien widgets.
- if (widgetToGetEvent->testAttribute(Qt::WA_NativeWindow) == false) {
- widgetToGetEvent = qApp->focusWidget();
- }
-
- if (widgetToGetEvent == 0)
- return false;
-
if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event))
return true;
if (mustUseCocoaKeyEvent())
return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent);
+
bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true);
return consumed && (info != 0);
#endif
@@ -729,7 +859,6 @@ void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget
#endif
}
-
QPointF flipPoint(const NSPoint &p)
{
return QPointF(p.x, flipYCoordinate(p.y));
@@ -745,21 +874,15 @@ NSPoint flipPoint(const QPointF &p)
return NSMakePoint(p.x(), flipYCoordinate(p.y()));
}
-void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* NSEvent* */mouseEvent,
- QWidget *widgetToGetEvent, bool &leftButtonIsRightButton)
+#if QT_MAC_USE_COCOA && __OBJC__
+
+void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event)
{
-#ifndef QT_MAC_USE_COCOA
- Q_UNUSED(eventWindow);
- Q_UNUSED(mouseEvent);
- Q_UNUSED(widgetToGetEvent);
- Q_UNUSED(leftButtonIsRightButton);
-#else
+ QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)];
if (widgetToGetEvent == 0)
return;
- NSWindow *window = static_cast<NSWindow *>(eventWindow);
- NSEvent *event = static_cast<NSEvent *>(mouseEvent);
- NSEventType evtType = [event type];
+ NSEventType evtType = [event type];
QPoint qlocalPoint;
QPoint qglobalPoint;
bool processThisEvent = false;
@@ -877,12 +1000,12 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /*
: QEvent::MouseButtonDblClick;
if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
button = Qt::RightButton;
- leftButtonIsRightButton = true;
+ qt_leftButtonIsRightButton = true;
}
} else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) {
- if (button == Qt::LeftButton && leftButtonIsRightButton) {
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton) {
button = Qt::RightButton;
- leftButtonIsRightButton = false;
+ qt_leftButtonIsRightButton = false;
}
}
@@ -902,155 +1025,265 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /*
// However we might need to unset it if the event is Release.
if (eventType == QEvent::MouseButtonRelease)
qt_button_down = 0;
-#endif
}
-bool qt_mac_handleMouseEvent(void * /* NSView * */view, void * /* NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button)
+QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent)
{
-#ifndef QT_MAC_USE_COCOA
- Q_UNUSED(view);
- Q_UNUSED(event);
- Q_UNUSED(eventType);
- Q_UNUSED(button);
- return false;
-#else
- QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
- NSEvent *theEvent = static_cast<NSEvent *>(event);
+ if (QWidget *popup = QApplication::activePopupWidget()) {
+ QWidget *focusInPopup = popup->focusWidget();
+ return focusInPopup ? focusInPopup : popup;
+ }
- // Give the Input Manager a chance to process the mouse events.
- NSInputManager *currentIManager = [NSInputManager currentInputManager];
- if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
- [currentIManager handleMouseEvent:theEvent];
- }
+ QWidget *widgetToGetKey = qApp->focusWidget();
+ if (!widgetToGetKey)
+ widgetToGetKey = widgetThatReceivedEvent;
- // Handle tablet events (if any) first.
- if (qt_mac_handleTabletEvent(theView, theEvent)) {
- // Tablet event was handled. In Qt we aren't supposed to send the mouse event.
- return true;
+ return widgetToGetKey;
+}
+
+// This function will find the widget that should receive the
+// mouse event. Because of explicit/implicit mouse grabs, popups,
+// etc, this might not end up being the same as the widget under
+// the mouse (which is more interresting when handling enter/leave
+// events
+QWidget *qt_mac_getTargetForMouseEvent(
+ // You can call this function without providing an event.
+ NSEvent *event,
+ QEvent::Type eventType,
+ QPoint &returnLocalPoint,
+ QPoint &returnGlobalPoint,
+ QWidget *nativeWidget,
+ QWidget **returnWidgetUnderMouse)
+{
+ Q_UNUSED(event);
+ NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation];
+ returnGlobalPoint = flipPoint(nsglobalpoint).toPoint();
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down);
+ QWidget *popup = QApplication::activePopupWidget();
+
+ // Resolve the widget under the mouse:
+ QWidget *widgetUnderMouse = 0;
+ if (popup || qt_button_down || !nativeWidget || !nativeWidget->isVisible()) {
+ // Using QApplication::widgetAt for finding the widget under the mouse
+ // is most safe, since it ignores cocoas own mouse down redirections (which
+ // we need to be prepared for when using nativeWidget as starting point).
+ // (the only exception is for QMacNativeWidget, where QApplication::widgetAt fails).
+ // But it is also slower (I guess), so we try to avoid it and use nativeWidget if we can:
+ widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint);
}
- NSPoint windowPoint = [theEvent locationInWindow];
- NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint];
+ if (!widgetUnderMouse && nativeWidget) {
+ // Entering here should be the common case. We
+ // also handle the QMacNativeWidget fallback case.
+ QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint);
+ widgetUnderMouse = nativeWidget->childAt(p);
+ if (!widgetUnderMouse && nativeWidget->rect().contains(p))
+ widgetUnderMouse = nativeWidget;
+ }
- // Find the widget that *should* get the event (e.g., maybe it was a pop-up,
- // they always get the mouse event).
- QWidget *qwidget = [theView qt_qwidget];
- QWidget *widgetToGetMouse = 0;
- NSView *tmpView = 0;
- QWidget *popup = qAppInstance()->activePopupWidget();
- QPoint qglobalPoint(flipPoint(globalPoint).toPoint());
+ if (widgetUnderMouse) {
+ // Check if widgetUnderMouse is blocked by a modal
+ // window, or the mouse if over the frame strut:
+ if (widgetUnderMouse == qt_button_down) {
+ // Small optimization to avoid an extra call to isBlockedByModal:
+ if (buttonDownNotBlockedByModal == false)
+ widgetUnderMouse = 0;
+ } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) {
+ widgetUnderMouse = 0;
+ }
- if (popup) {
- widgetToGetMouse = popup;
- tmpView = qt_mac_nativeview_for(popup);
- windowPoint = [[tmpView window] convertScreenToBase:globalPoint];
-
- QPoint qWindowPoint(windowPoint.x, windowPoint.y);
- if (widgetToGetMouse->rect().contains(qWindowPoint)) {
- // Keeping the mouse pressed on a combobox button will make
- // the popup pop in front of the mouse. But all mouse events
- // will be sendt to the button. Since we want mouse events
- // to be sendt to widgets inside the popup, we search for the
- // widget in front of the mouse:
- tmpView = [tmpView hitTest:windowPoint];
- if (!tmpView)
- return false;
- widgetToGetMouse =
- [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(tmpView) qt_qwidget];
+ if (widgetUnderMouse && widgetUnderMouse->isWindow()) {
+ // Exclude the titlebar (and frame strut) when finding widget under mouse:
+ QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint);
+ if (!widgetUnderMouse->rect().contains(p))
+ widgetUnderMouse = 0;
+ }
+ }
+ if (returnWidgetUnderMouse)
+ *returnWidgetUnderMouse = widgetUnderMouse;
+
+ // Resolve the target for the mouse event. Default will be
+ // widgetUnderMouse, except if there is a grab (popup/mouse/button-down):
+ if (popup && !mouseGrabber) {
+ // We special case handling of popups, since they have an implicitt mouse grab.
+ QWidget *candidate = buttonDownNotBlockedByModal ? qt_button_down : widgetUnderMouse;
+ if (!popup->isAncestorOf(candidate)) {
+ // INVARIANT: we have a popup, but the candidate is not
+ // in it. But the popup will grab the mouse anyway,
+ // except if the user scrolls:
+ if (eventType == QEvent::Wheel)
+ return 0;
+ returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint);
+ return popup;
+ } else if (popup == candidate) {
+ // INVARIANT: The candidate is the popup itself, and not a child:
+ returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint);
+ return popup;
+ } else {
+ // INVARIANT: The candidate is a child inside the popup:
+ returnLocalPoint = candidate->mapFromGlobal(returnGlobalPoint);
+ return candidate;
}
+ }
+
+ QWidget *target = mouseGrabber;
+ if (!target && buttonDownNotBlockedByModal)
+ target = qt_button_down;
+ if (!target)
+ target = widgetUnderMouse;
+ if (!target)
+ return 0;
+
+ returnLocalPoint = target->mapFromGlobal(returnGlobalPoint);
+ return target;
+}
+
+QPointer<QWidget> qt_last_native_mouse_receiver = 0;
+
+static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget)
+{
+ // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do
+ // not. This will in general be the cases when alien widgets are not involved:
+ // 1. from a native widget to another native widget or
+ // 2. from a native widget to no widget
+ // 3. from no widget to a native or alien widget
+
+ if (qt_button_down || QWidget::mouseGrabber())
+ return;
+
+ if ((maybeEnterWidget == qt_last_native_mouse_receiver) && qt_last_native_mouse_receiver)
+ return;
+ if (maybeEnterWidget) {
+ if (!qt_last_native_mouse_receiver) {
+ // case 3
+ QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0);
+ qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget();
+ } else if (maybeEnterWidget->internalWinId()) {
+ // case 1
+ QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_native_mouse_receiver);
+ qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget();
+ } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate
} else {
- extern QWidget * qt_button_down; //qapplication_mac.cpp
- QPoint pos;
- widgetToGetMouse = QApplicationPrivate::pickMouseReceiver(qwidget, qglobalPoint,
- pos, eventType,
- button, qt_button_down, 0);
- if (widgetToGetMouse)
- tmpView = qt_mac_nativeview_for(widgetToGetMouse);
+ if (qt_last_native_mouse_receiver) {
+ // case 2
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_native_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+ qt_last_native_mouse_receiver = 0;
+ }
}
+}
+
+bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget)
+{
+ // Give the Input Manager a chance to process the mouse events.
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
+ [currentIManager handleMouseEvent:event];
+ }
+
+ // Find the widget that should receive the event, and the widget under the mouse. Those
+ // can differ if an implicit or explicit mouse grab is active:
+ QWidget *widgetUnderMouse = 0;
+ QPoint localPoint, globalPoint;
+ QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse);
if (!widgetToGetMouse)
return false;
- NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil];
- QPoint qlocalPoint = QPoint(localPoint.x, localPoint.y);
+ // From here on, we let nativeWidget actually be the native widget under widgetUnderMouse. The reason
+ // for this, is that qt_mac_getTargetForMouseEvent will set cocoa's mouse event redirection aside when
+ // determining which widget is under the mouse (in other words, it will usually ignore nativeWidget).
+ // nativeWidget will be used in QApplicationPrivate::sendMouseEvent to correctly dispatch enter/leave events.
+ if (widgetUnderMouse)
+ nativeWidget = widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget();
+ if (!nativeWidget)
+ return false;
+ NSView *view = qt_mac_effectiveview_for(nativeWidget);
- // 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;
- }
+ // Handle tablet events (if any) first.
+ if (qt_mac_handleTabletEvent(view, event)) {
+ // Tablet event was handled. In Qt we aren't supposed to send the mouse event.
+ return true;
}
- EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef]));
+ EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef]));
if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent))
return true;
- // Yay! All the special cases are handled, it really is just a normal mouse event.
- Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
- NSInteger clickCount = [theEvent clickCount];
- Qt::MouseButtons buttons = 0;
+ // Keep previousButton to make sure we don't send double click
+ // events when the user double clicks using two different buttons:
static Qt::MouseButton previousButton = Qt::NoButton;
+
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ NSInteger clickCount = [event clickCount];
+ Qt::MouseButtons buttons = 0;
{
UInt32 mac_buttons;
if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0,
sizeof(mac_buttons), 0, &mac_buttons) == noErr)
buttons = qt_mac_get_buttons(mac_buttons);
}
+
+ // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not:
+ qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse);
+
switch (eventType) {
default:
qWarning("not handled! %d", eventType);
break;
case QEvent::MouseMove:
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton)
+ button = Qt::RightButton;
break;
case QEvent::MouseButtonPress:
- [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = theView;
- [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = theEvent;
-#ifndef QT_NAMESPACE
- Q_ASSERT(clickCount > 0);
-#endif
+ qt_button_down = widgetUnderMouse;
if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button))
eventType = QEvent::MouseButtonDblClick;
if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
button = Qt::RightButton;
- [theView qt_setLeftButtonIsRightButton: true];
+ qt_leftButtonIsRightButton = true;
}
break;
case QEvent::MouseButtonRelease:
- if (button == Qt::LeftButton && [theView qt_leftButtonIsRightButton]) {
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton) {
button = Qt::RightButton;
- [theView qt_setLeftButtonIsRightButton: false];
+ qt_leftButtonIsRightButton = false;
}
qt_button_down = 0;
break;
}
- [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint;
- QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods);
-#ifdef ALIEN_DEBUG
- qDebug() << "sending mouse event to" << widgetToGetMouse;
-#endif
- extern QWidget *qt_button_down;
- extern QPointer<QWidget> qt_last_mouse_receiver;
+ qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse);
- 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);
+ DnDParams *dndParams = currentDnDParameters();
+ dndParams->view = view;
+ dndParams->theEvent = event;
+ dndParams->globalPoint = globalPoint;
+
+ // Send the mouse event:
+ QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods);
+ QApplicationPrivate::sendMouseEvent(
+ widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget,
+ &qt_button_down, qt_last_mouse_receiver, true);
if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) {
- QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods);
+ QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods);
qt_sendSpontaneousEvent(widgetToGetMouse, &qcme);
}
+
+ if (eventType == QEvent::MouseButtonRelease) {
+ // A mouse button was released, which means that the implicit grab was
+ // released. We therefore need to re-check if should send (delayed) enter leave events:
+ // qt_button_down has now become NULL since the call at the top of the function. Also, since
+ // the relase might have closed a window, we dont give the nativeWidget hint
+ qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse);
+ qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse);
+ }
+
previousButton = button;
return true;
-#endif
}
+#endif
bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent)
{
@@ -1207,7 +1440,7 @@ void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widge
// We have the original method here. Proceed and swap the methods.
method_exchangeImplementations(m1, m0);
widget->originalDrawMethod = false;
- [window display];
+ [theWindow display];
}
}
@@ -1230,21 +1463,21 @@ void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivat
}
method_exchangeImplementations(m1, m0);
widget->originalDrawMethod = true;
- [window display];
+ [theWindow display];
}
#endif // QT_MAC_USE_COCOA
+#if 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];
[macToolbar setShowsBaselineSeparator:show];
-#endif // QT_MAC_USE_COCOA
}
+#endif // QT_MAC_USE_COCOA
QStringList qt_mac_NSArrayToQStringList(void *nsarray)
{
@@ -1264,6 +1497,7 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
return result;
}
+#if QT_MAC_USE_COCOA
void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow)
{
if (!widgetForWindow)
@@ -1288,6 +1522,7 @@ void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow)
[window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0];
}
+#endif // QT_MAC_USE_COCOA
// Carbon: Make sure you call QDEndContext on the context when done with it.
CGContextRef qt_mac_graphicsContextFor(QWidget *widget)
@@ -1300,7 +1535,7 @@ CGContextRef qt_mac_graphicsContextFor(QWidget *widget)
CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
QDBeginCGContext(port, &context);
#else
- CGContextRef context = reinterpret_cast<CGContextRef>([[qt_mac_window_for(widget) graphicsContext] graphicsPort]);
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext graphicsContextWithWindow:qt_mac_window_for(widget)] graphicsPort];
#endif
return context;
}
@@ -1330,11 +1565,11 @@ QString qt_mac_get_pasteboardString(OSPasteboardRef paste)
QMacCocoaAutoReleasePool pool;
NSPasteboard *pb = nil;
CFStringRef pbname;
- if (PasteboardCopyName (paste, &pbname)) {
- pb = [NSPasteboard generalPasteboard];
+ if (PasteboardCopyName(paste, &pbname) == noErr) {
+ pb = [NSPasteboard pasteboardWithName:const_cast<NSString *>(reinterpret_cast<const NSString *>(pbname))];
+ CFRelease(pbname);
} else {
- pb = [NSPasteboard pasteboardWithName:reinterpret_cast<const NSString *>(pbname)];
- CFRelease (pbname);
+ pb = [NSPasteboard generalPasteboard];
}
if (pb) {
NSString *text = [pb stringForType:NSStringPboardType];
@@ -1389,6 +1624,7 @@ void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayI
}
}
+#ifdef QT_MAC_USE_COCOA
void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse)
{
QMacCocoaAutoReleasePool pool;
@@ -1423,47 +1659,46 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse)
}
}
-#ifdef QT_MAC_USE_COCOA
-void qt_cocoaChangeOverrideCursor(const QCursor &cursor)
-{
- 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;
+class CocoaPostMessageAfterEventLoopExitHelp : public QObject
+{
+ id target;
+ SEL selector;
+ int argCount;
+ id arg1;
+ id arg2;
+public:
+ CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2)
+ : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){
+ deleteLater();
+ }
- 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];
+ ~CocoaPostMessageAfterEventLoopExitHelp()
+ {
+ qt_cocoaPostMessage(target, selector, argCount, arg1, arg2);
}
+};
- // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5!
+void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2)
+{
+ // 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);
+ QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector, argCount, arg1, arg2);
quint32 lower = quintptr(args);
quint32 upper = quintptr(args) >> 32;
NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined
- location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:windowNumber
+ location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0
context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper];
[NSApp postEvent:e atStart:NO];
- return true;
}
+
+void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2)
+{
+ if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1)
+ qt_cocoaPostMessage(target, selector, argCount, arg1, arg2);
+ else
+ new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2);
+}
+
#endif
QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool()
@@ -1487,6 +1722,12 @@ void qt_mac_post_retranslateAppMenu()
#endif
}
+QWidgetPrivate *QMacScrollOptimization::_target = 0;
+bool QMacScrollOptimization::_inWheelEvent = false;
+int QMacScrollOptimization::_dx = 0;
+int QMacScrollOptimization::_dy = 0;
+QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1);
+
#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
@@ -1501,7 +1742,7 @@ void macDrawRectOnTop(void * /*OSWindowRef */window)
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]) {
+ if([theWindow isKeyWindow] || [theWindow 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];
@@ -1521,7 +1762,7 @@ void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window)
{
OSWindowRef theWindow = static_cast<OSWindowRef>(window);
NSApplication *application = [NSApplication sharedApplication];
- NSToolbar *toolbar = [window toolbar];
+ NSToolbar *toolbar = [theWindow toolbar];
if([application isActive]) {
// Launched from finder
[toolbar setShowsBaselineSeparator:NO];
@@ -1549,6 +1790,35 @@ void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *childWidget)
}
}
+void qt_mac_display(QWidget *widget)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ [theNSView display];
+}
+
+void qt_mac_setNeedsDisplay(QWidget *widget)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ [theNSView setNeedsDisplay:YES];
+}
+
+void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ if (region.isEmpty()) {
+ [theNSView setNeedsDisplay:YES];
+ return;
+ }
+
+ QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.count(); ++i) {
+ const QRect &rect = rects.at(i);
+ NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
+ [theNSView setNeedsDisplayInRect:nsrect];
+ }
+
+}
+
#endif // QT_MAC_USE_COCOA
QT_END_NAMESPACE