diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
25 files changed, 473 insertions, 113 deletions
diff --git a/src/plugins/platforms/cocoa/messages.cpp b/src/plugins/platforms/cocoa/messages.cpp index 1fe80b28b1..8fc8b312f5 100644 --- a/src/plugins/platforms/cocoa/messages.cpp +++ b/src/plugins/platforms/cocoa/messages.cpp @@ -90,6 +90,14 @@ QPlatformMenuItem::MenuRole detectMenuRole(const QString &caption) || caption.startsWith(QCoreApplication::translate("QCocoaMenuItem", "Exit"), Qt::CaseInsensitive)) { return QPlatformMenuItem::QuitRole; } + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Cut"), Qt::CaseInsensitive)) + return QPlatformMenuItem::CutRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Copy"), Qt::CaseInsensitive)) + return QPlatformMenuItem::CopyRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Paste"), Qt::CaseInsensitive)) + return QPlatformMenuItem::PasteRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Select All"), Qt::CaseInsensitive)) + return QPlatformMenuItem::SelectAllRole; return QPlatformMenuItem::NoRole; } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index 86bb5323a7..a78901bfb1 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -79,7 +79,7 @@ namespace QCocoaAccessible { NSString *macRole(QAccessibleInterface *interface); bool shouldBeIgnored(QAccessibleInterface *interface); -NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface); +NSArray *unignoredChildren(QAccessibleInterface *interface); NSString *getTranslatedAction(const QString &qtAction); NSMutableArray *createTranslatedActionsList(const QStringList &qtActions); QString translateAction(NSString *nsAction); diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 990acd5301..72045a1bbb 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -55,19 +55,31 @@ QCocoaAccessibility::~QCocoaAccessibility() void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { - QAccessible::Id interfaceId = event->uniqueId(); - if (!interfaceId) + QCocoaAccessibleElement *element = [QCocoaAccessibleElement elementWithId: event->uniqueId()]; + if (!element) { + qWarning() << "QCocoaAccessibility::notifyAccessibilityUpdate: invalid element"; return; + } switch (event->type()) { - case QAccessible::ValueChanged: - case QAccessible::TextInserted : - case QAccessible::TextRemoved : - case QAccessible::TextUpdated : { - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId : interfaceId parent : nil]; - [element autorelease]; - NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); - break; } + case QAccessible::Focus: { + NSAccessibilityPostNotification(element, NSAccessibilityFocusedUIElementChangedNotification); + break; + } + case QAccessible::StateChanged: + case QAccessible::ValueChanged: + case QAccessible::TextInserted: + case QAccessible::TextRemoved: + case QAccessible::TextUpdated: + NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); + break; + case QAccessible::TextCaretMoved: + case QAccessible::TextSelectionChanged: + NSAccessibilityPostNotification(element, NSAccessibilitySelectedTextChangedNotification); + break; + case QAccessible::NameChanged: + NSAccessibilityPostNotification(element, NSAccessibilityTitleChangedNotification); + break; default: break; } @@ -218,7 +230,7 @@ bool shouldBeIgnored(QAccessibleInterface *interface) return false; } -NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface) +NSArray *unignoredChildren(QAccessibleInterface *interface) { int numKids = interface->childCount(); // qDebug() << "Children for: " << axid << iface << " are: " << numKids; @@ -231,9 +243,12 @@ NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface) QAccessible::Id childId = QAccessible::uniqueId(child); //qDebug() << " kid: " << childId << child; - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childId parent:parentObject]; - [kids addObject: element]; - [element release]; + + QCocoaAccessibleElement *element = [QCocoaAccessibleElement elementWithId: childId]; + if (element) + [kids addObject: element]; + else + qWarning() << "QCocoaAccessibility: invalid child"; } return NSAccessibilityUnignoredChildren(kids); } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index c207cbee2d..babaab5ae2 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -52,12 +52,11 @@ @interface QCocoaAccessibleElement : NSObject { NSString *role; - NSObject *parent; QAccessible::Id axid; } -- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent; -+ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent; +- (id)initWithId:(QAccessible::Id)anId; ++ (QCocoaAccessibleElement *)elementWithId:(QAccessible::Id)anId; @end diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 0b674b8d2f..1df4230385 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -41,6 +41,8 @@ #include "qcocoaaccessibilityelement.h" #include "qcocoaaccessibility.h" #include "qcocoahelpers.h" +#include "qcocoawindow.h" +#include "private/qaccessiblecache_p.h" #include <QtGui/qaccessible.h> @@ -48,7 +50,7 @@ @implementation QCocoaAccessibleElement -- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent +- (id)initWithId:(QAccessible::Id)anId { Q_ASSERT((int)anId < 0); self = [super init]; @@ -57,15 +59,35 @@ QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); Q_ASSERT(iface); role = QCocoaAccessible::macRole(iface); - parent = aParent; } return self; } -+ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent ++ (id)elementWithId:(QAccessible::Id)anId { - return [[self alloc] initWithId:anId parent:aParent]; + Q_ASSERT(anId); + if (!anId) + return nil; + + QAccessibleCache *cache = QAccessibleCache::instance(); + + QCocoaAccessibleElement *element = cache->elementForId(anId); + if (!element) { + QAccessibleInterface *iface = QAccessible::accessibleInterface(anId); + Q_ASSERT(iface); + if (!iface) + return nil; + element = [[self alloc] initWithId:anId]; + cache->insertElement(anId, element); + } + return element; +} + +- (void)invalidate { + axid = 0; + NSAccessibilityPostNotification(self, NSAccessibilityUIElementDestroyedNotification); + [self release]; } - (void)dealloc { @@ -98,6 +120,10 @@ return [NSNumber numberWithInt: newlines]; } +- (BOOL) accessibilityNotifiesWhenDestroyed { + return YES; +} + - (NSArray *)accessibilityAttributeNames { static NSArray *defaultAttributes = nil; @@ -116,6 +142,7 @@ NSAccessibilityTopLevelUIElementAttribute, NSAccessibilityPositionAttribute, NSAccessibilitySizeAttribute, + NSAccessibilityTitleAttribute, NSAccessibilityDescriptionAttribute, NSAccessibilityEnabledAttribute, nil]; @@ -144,6 +171,26 @@ return [attributes autorelease]; } +- (id)parentElement { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return nil; + + if (QWindow *window = iface->window()) { + QCocoaWindow *win = static_cast<QCocoaWindow*>(window->handle()); + return win->qtView(); + } + + QAccessibleInterface *parent = iface->parent(); + if (!parent) { + qWarning() << "INVALID PARENT FOR INTERFACE: " << iface; + return nil; + } + + QAccessible::Id parentId = QAccessible::uniqueId(parent); + return [QCocoaAccessibleElement elementWithId: parentId]; +} + - (id)accessibilityAttributeValue:(NSString *)attribute { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface) { @@ -156,19 +203,19 @@ } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { return NSAccessibilityRoleDescription(role, nil); } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - return QCocoaAccessible::unignoredChildren(self, iface); + return QCocoaAccessible::unignoredChildren(iface); } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { // Just check if the app thinks we're focused. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; return [NSNumber numberWithBool:[focusedElement isEqual:self]]; } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { - return NSAccessibilityUnignoredAncestor(parent); + return NSAccessibilityUnignoredAncestor([self parentElement]); } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) { // We're in the same window as our parent. - return [parent accessibilityAttributeValue:NSAccessibilityWindowAttribute]; + return [[self parentElement] accessibilityAttributeValue:NSAccessibilityWindowAttribute]; } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) { // We're in the same top level element as our parent. - return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; + return [[self parentElement] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { QPoint qtPosition = iface->rect().topLeft(); QSize qtSize = iface->rect().size(); @@ -176,8 +223,10 @@ } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) { QSize qtSize = iface->rect().size(); return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())]; - } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { + } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) { return QCFString::toNSString(iface->text(QAccessible::Name)); + } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { + return QCFString::toNSString(iface->text(QAccessible::Description)); } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) { return [NSNumber numberWithBool:!iface->state().disabled]; } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { @@ -400,20 +449,26 @@ return NSAccessibilityUnignoredAncestor(self); } - QAccessibleInterface *childInterface = iface->childAt(point.x, qt_mac_flipYCoordinate(point.y)); - + int y = qt_mac_flipYCoordinate(point.y); + QAccessibleInterface *childInterface = iface->childAt(point.x, y); // No child found, meaning we hit this element. - if (!childInterface) { -// qDebug() << "Hit test returns: " << id << iface; + if (!childInterface) return NSAccessibilityUnignoredAncestor(self); - } + + // find the deepest child at the point + QAccessibleInterface *childOfChildInterface = 0; + do { + childOfChildInterface = childInterface->childAt(point.x, y); + if (childOfChildInterface) + childInterface = childOfChildInterface; + } while (childOfChildInterface); QAccessible::Id childId = QAccessible::uniqueId(childInterface); // hit a child, forward to child accessible interface. - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childId parent:self]; - [accessibleElement autorelease]; - - return [accessibleElement accessibilityHitTest:point]; + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithId:childId]; + if (accessibleElement) + return NSAccessibilityUnignoredAncestor(accessibleElement); + return NSAccessibilityUnignoredAncestor(self); } - (id)accessibilityFocusedUIElement { @@ -423,17 +478,15 @@ qWarning() << "FocusedUIElement for INVALID"; return nil; } + QAccessibleInterface *childInterface = iface->focusChild(); if (childInterface) { QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); - // FIXME: parent could be wrong - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; - [accessibleElement autorelease]; - return accessibleElement; + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithId:childAxid]; + return NSAccessibilityUnignoredAncestor(accessibleElement); } - // no focus found - return nil; + return NSAccessibilityUnignoredAncestor(self); } @end diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index 592bfc8e50..b81b9a0b1c 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -308,8 +308,7 @@ NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap *bitmap, const QBit NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot) { - NSPoint hotSpot = NSMakePoint(hotspot.x() / pixmap.devicePixelRatio(), - hotspot.y() / pixmap.devicePixelRatio()); + NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y()); NSImage *nsimage; if (pixmap.devicePixelRatio() > 1.0) { QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio(); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 8728ab8764..2b7b8109ad 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -52,6 +52,7 @@ #include <private/qguiapplication_p.h> #include "qt_mac_p.h" #include "qcocoahelpers.h" +#include "qcocoamenubar.h" #include <qregexp.h> #include <qbuffer.h> #include <qdebug.h> @@ -225,6 +226,7 @@ static QString strippedText(QString s) || [self panel:nil shouldShowFilename:filepath]; [self updateProperties]; + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); [mOpenPanel setAllowedFileTypes:nil]; [mSavePanel setNameFieldStringValue:selectable ? QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.fileName()) : @""]; @@ -250,7 +252,9 @@ static QString strippedText(QString s) // cleanup of modal sessions. Do this before showing the native dialog, otherwise it will // close down during the cleanup. qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); mReturnCode = [mSavePanel runModal]; + QCocoaMenuBar::resetKnownMenuItemsToQt(); QAbstractEventDispatcher::instance()->interrupt(); return (mReturnCode == NSOKButton); @@ -269,6 +273,7 @@ static QString strippedText(QString s) || [self panel:nil shouldShowFilename:filepath]; [self updateProperties]; + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); [mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]]; [mSavePanel setNameFieldStringValue:selectable ? QCFString::toNSString(info.fileName()) : @""]; @@ -583,6 +588,7 @@ void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_selectionChanged(const QSt void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_panelClosed(bool accepted) { + QCocoaMenuBar::resetKnownMenuItemsToQt(); if (accepted) { emit accept(); } else { diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index a73944c07c..9850f83dea 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -74,7 +74,7 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) { NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()]; for (int i=0; i<list.size(); ++i){ - [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))]; + [result addObject:list[i].toNSString()]; } return result; } diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 24adc7a95b..9c4f86d893 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -80,6 +80,7 @@ public: qreal refreshRate() const { return m_refreshRate; } QString name() const { return m_name; } QPlatformCursor *cursor() const { return m_cursor; } + QWindow *topLevelAt(const QPoint &point) const; QList<QPlatformScreen *> virtualSiblings() const { return m_siblings; } // ---------------------------------------------------- @@ -138,6 +139,8 @@ public: void setToolbar(QWindow *window, NSToolbar *toolbar); NSToolbar *toolbar(QWindow *window) const; void clearToolbars(); + void setWindow(NSWindow* nsWindow, QCocoaWindow *window); + QCocoaWindow *window(NSWindow *window); private: static QCocoaIntegration *mInstance; @@ -156,6 +159,7 @@ private: QScopedPointer<QCocoaKeyMapper> mKeyboardMapper; QHash<QWindow *, NSToolbar *> mToolbars; + QHash<NSWindow *, QCocoaWindow*> mWindows; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 412818ae47..c4398622e8 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -81,12 +81,16 @@ QCocoaScreen::~QCocoaScreen() NSScreen *QCocoaScreen::osScreen() const { - return [[NSScreen screens] objectAtIndex:m_screenIndex]; + NSArray *screens = [NSScreen screens]; + return ((NSUInteger)m_screenIndex < [screens count]) ? [screens objectAtIndex:m_screenIndex] : nil; } void QCocoaScreen::updateGeometry() { NSScreen *nsScreen = osScreen(); + if (!nsScreen) + return; + NSRect frameRect = [nsScreen frame]; if (m_screenIndex == 0) { @@ -143,7 +147,8 @@ qreal QCocoaScreen::devicePixelRatio() const { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - return qreal([osScreen() backingScaleFactor]); + NSScreen * screen = osScreen(); + return qreal(screen ? [screen backingScaleFactor] : 1.0); } else #endif { @@ -151,6 +156,24 @@ qreal QCocoaScreen::devicePixelRatio() const } } +QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const +{ + // Get a z-ordered list of windows. Iterate through it until + // we find a window which contains the point. + for (NSWindow *nsWindow in [NSApp orderedWindows]) { + QCocoaWindow *cocoaWindow = QCocoaIntegration::instance()->window(nsWindow); + if (!cocoaWindow) + continue; + QWindow *window = cocoaWindow->window(); + if (!window->isTopLevel()) + continue; + if (window->geometry().contains(point)) + return window; + } + + return QPlatformScreen::topLevelAt(point); +} + extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const @@ -495,6 +518,16 @@ NSToolbar *QCocoaIntegration::toolbar(QWindow *window) const return mToolbars.value(window); } +void QCocoaIntegration::setWindow(NSWindow* nsWindow, QCocoaWindow *window) +{ + mWindows.insert(nsWindow, window); +} + +QCocoaWindow *QCocoaIntegration::window(NSWindow *window) +{ + return mWindows.value(window); +} + void QCocoaIntegration::clearToolbars() { QHash<QWindow *, NSToolbar *>::const_iterator it = mToolbars.constBegin(); diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 35e8fdebb4..6acc062eb9 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -269,7 +269,6 @@ void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem * QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem); QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before); - SET_COCOA_MENU_ANCESTOR(menuItem, this); cocoaItem->sync(); if (beforeItem) { int index = m_menuItems.indexOf(beforeItem); @@ -315,6 +314,7 @@ void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem) } else { [m_nativeMenu addItem: item->nsItem()]; } + SET_COCOA_MENU_ANCESTOR(item, this); } void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem) diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index 7a1bda74a4..fa02a7870b 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -67,9 +67,13 @@ public: inline NSMenu *nsMenu() const { return m_nativeMenu; } + static void redirectKnownMenuItemsToFirstResponder(); + static void resetKnownMenuItemsToQt(); static void updateMenuBarImmediately(); QList<QCocoaMenuItem*> merged() const; + NSMenuItem *itemForRole(QPlatformMenuItem::MenuRole r); + private: static QCocoaWindow *findWindowForMenubar(); static QCocoaMenuBar *findGlobalMenubar(); diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 7335deb800..ffc0fabdce 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -205,6 +205,59 @@ QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar() return NULL; } +void QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder() +{ + // QTBUG-17291: http://forums.macrumors.com/showthread.php?t=1249452 + // When a dialog is opened, shortcuts for actions inside the dialog (cut, paste, ...) + // continue to go through the same menu items which claimed those shortcuts. + // They are not keystrokes which we can intercept in any other way; the OS intercepts them. + // The menu items had to be created by the application. That's why we need roles + // to identify those "special" menu items which can be useful even when non-Qt + // native widgets are in focus. When the native widget is focused it will be the + // first responder, so the menu item needs to have its target be the first responder; + // this is done by setting it to nil. + + // This function will find all menu items on all menus which have + // "special" roles, set the target and also set the standard actions which + // apply to those roles. But afterwards it is necessary to call + // resetKnownMenuItemsToQt() to put back the target and action so that + // those menu items will go back to invoking their associated QActions. + foreach (QCocoaMenuBar *mb, static_menubars) + foreach (QCocoaMenu *m, mb->m_menus) + foreach (QCocoaMenuItem *i, m->items()) { + bool known = true; + switch (i->effectiveRole()) { + case QPlatformMenuItem::CutRole: + [i->nsItem() setAction:@selector(cut:)]; + break; + case QPlatformMenuItem::CopyRole: + [i->nsItem() setAction:@selector(copy:)]; + break; + case QPlatformMenuItem::PasteRole: + [i->nsItem() setAction:@selector(paste:)]; + break; + case QPlatformMenuItem::SelectAllRole: + [i->nsItem() setAction:@selector(selectAll:)]; + break; + // We may discover later that there are other roles/actions which + // are meaningful to standard native widgets; they can be added. + default: + known = false; + break; + } + if (known) + [i->nsItem() setTarget:nil]; + } +} + +void QCocoaMenuBar::resetKnownMenuItemsToQt() +{ + // Undo the effect of redirectKnownMenuItemsToFirstResponder(): + // set the menu items' actions to itemFired and their targets to + // the QCocoaMenuDelegate. + updateMenuBarImmediately(); +} + void QCocoaMenuBar::updateMenuBarImmediately() { QCocoaAutoReleasePool pool; @@ -321,3 +374,13 @@ QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const return 0; } + +NSMenuItem *QCocoaMenuBar::itemForRole(QPlatformMenuItem::MenuRole r) +{ + foreach (QCocoaMenu *m, m_menus) + foreach (QCocoaMenuItem *i, m->items()) + if (i->effectiveRole() == r) + return i->nsItem(); + return Q_NULLPTR; +} + diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index b0169b9746..61706c19bc 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -98,6 +98,8 @@ public: inline bool isSeparator() const { return m_isSeparator; } QCocoaMenu *menu() const { return m_menu; } + MenuRole effectiveRole() const; + private: QString mergeText(); QKeySequence mergeAccel(); @@ -112,6 +114,7 @@ private: bool m_isSeparator; QFont m_font; MenuRole m_role; + MenuRole m_detectedRole; QKeySequence m_shortcut; bool m_checked; bool m_merged; diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 2246d2ce46..58fe07bc62 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -227,7 +227,8 @@ NSMenuItem *QCocoaMenuItem::sync() if (depth == 3 || !menubar) break; // Menu item too deep in the hierarchy, or not connected to any menubar - switch (detectMenuRole(m_text)) { + m_detectedRole = detectMenuRole(m_text); + switch (m_detectedRole) { case QPlatformMenuItem::AboutRole: if (m_text.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) mergeItem = [loader aboutMenuItem]; @@ -241,6 +242,8 @@ NSMenuItem *QCocoaMenuItem::sync() mergeItem = [loader quitMenuItem]; break; default: + if (m_detectedRole >= CutRole && m_detectedRole < RoleCount && menubar) + mergeItem = menubar->itemForRole(m_detectedRole); if (!m_text.isEmpty()) m_textSynced = true; break; @@ -249,7 +252,7 @@ NSMenuItem *QCocoaMenuItem::sync() } default: - qWarning() << Q_FUNC_INFO << "unsupported role" << (int) m_role; + qWarning() << Q_FUNC_INFO << "menu item" << m_text << "has unsupported role" << (int)m_role; } if (mergeItem) { @@ -374,3 +377,11 @@ void QCocoaMenuItem::syncModalState(bool modal) else [m_native setEnabled:m_enabled]; } + +QPlatformMenuItem::MenuRole QCocoaMenuItem::effectiveRole() const +{ + if (m_role > TextHeuristicRole) + return m_role; + else + return m_detectedRole; +} diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm index 29fc0fb674..60bc3b5a55 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm @@ -115,14 +115,13 @@ QT_END_NAMESPACE showAllItem = [[appMenu itemWithTitle:@"Show All"] retain]; // Get the names in the nib to match the app name set by Qt. - const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qt_mac_applicationName())); + const NSString *appName = qt_mac_applicationName().toNSString(); [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication" withString:const_cast<NSString *>(appName)]]; [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication" withString:const_cast<NSString *>(appName)]]; [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication" withString:const_cast<NSString *>(appName)]]; - [appName release]; // Disable the items that don't do anything. If someone associates a QAction with them // They should get synced back in. [preferencesItem setEnabled:NO]; diff --git a/src/plugins/platforms/cocoa/qcocoamimetypes.mm b/src/plugins/platforms/cocoa/qcocoamimetypes.mm index 05d4c19112..8151d31449 100644 --- a/src/plugins/platforms/cocoa/qcocoamimetypes.mm +++ b/src/plugins/platforms/cocoa/qcocoamimetypes.mm @@ -136,9 +136,76 @@ QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, Q return ret; } +// This handler is special: It supports converting public.rtf top text/html, +// but not the other way around. +class QMacPasteboardMimeRtfText : public QMacInternalPasteboardMime { +public: + QMacPasteboardMimeRtfText() : QMacInternalPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeRtfText::convertorName() +{ + return QLatin1String("Rtf"); +} + +QString QMacPasteboardMimeRtfText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/html")) + return QLatin1String("public.rtf"); + return QString(); +} + +QString QMacPasteboardMimeRtfText::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.rtf")) + return QLatin1String("text/html"); + return QString(); +} + +bool QMacPasteboardMimeRtfText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimeRtfText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor) +{ + if (!canConvert(mimeType, flavor)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); + + // Convert Rtf to Html. + NSAttributedString *string = [[NSAttributedString alloc] initWithRTF:data.at(0).toNSData() documentAttributes:NULL]; + NSError *error; + NSRange range = NSMakeRange(0,[string length]); + NSDictionary *dict = [NSDictionary dictionaryWithObject:NSHTMLTextDocumentType forKey:NSDocumentTypeDocumentAttribute]; + NSData *htmldata = [string dataFromRange:range documentAttributes:dict error:&error]; + [string release]; + return QByteArray::fromNSData(htmldata); +} + +QList<QByteArray> QMacPasteboardMimeRtfText::convertFromMime(const QString &mime, QVariant data, QString flavor) +{ + Q_UNUSED(mime); + Q_UNUSED(data); + Q_UNUSED(flavor); + + qWarning("QMacPasteboardMimeRtfText: Conversion from Html to Rtf is not supported"); + QList<QByteArray> ret; + return ret; +} + void QCocoaMimeTypes::initializeMimeTypes() { new QMacPasteboardMimeTiff; + new QMacPasteboardMimeRtfText; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index efdd433d8f..54e45a1d99 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -133,16 +133,23 @@ private: // deregisters. static void registerTouchWindow(QWindow *window, bool enable); - // Request a unified title and toolbar look for the window. + // Enable the unified title and toolbar area for a window. + static void setContentBorderEnabled(QWindow *window, bool enable); + + // Set the size of the unified title and toolbar area. static void setContentBorderThickness(QWindow *window, int topThickness, int bottomThickness); - // Request a unified title and toolbar look for the window by registering - // an area. Multiple callers can register areas and the platform plugin + // Set the size for a unified toolbar content border area. + // Multiple callers can register areas and the platform plugin // will extend the "unified" area to cover them. static void registerContentBorderArea(QWindow *window, quintptr identifer, int upper, int lower); - // Enable the unified title and toolbar area. - static void enableContentBorderArea(QWindow *window, bool enable); + // Enables or disiables a content border area. + static void setContentBorderAreaEnabled(QWindow *window, quintptr identifier, bool enable); + + // Returns true if the given coordinate is inside the current + // content border. + static bool testContentBorderPosition(QWindow *window, int position); // Sets a NSToolbar instance for the given QWindow. The // toolbar will be attached to the native NSWindow when diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index d6a5be8d52..e09c31231d 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -127,10 +127,14 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setContentBorderThickness); if (resource.toLower() == "registercontentborderarea") return NativeResourceForIntegrationFunction(QCocoaNativeInterface::registerContentBorderArea); - if (resource.toLower() == "enablecontentborderarea") - return NativeResourceForIntegrationFunction(QCocoaNativeInterface::enableContentBorderArea); + if (resource.toLower() == "setcontentborderareaenabled") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setContentBorderAreaEnabled); + if (resource.toLower() == "setcontentborderenabled") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setContentBorderEnabled); if (resource.toLower() == "setnstoolbar") return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setNSToolbar); + if (resource.toLower() == "testcontentborderposition") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::testContentBorderPosition); return 0; } @@ -301,14 +305,24 @@ void QCocoaNativeInterface::registerContentBorderArea(QWindow *window, quintptr cocoaWindow->registerContentBorderArea(identifier, upper, lower); } -void QCocoaNativeInterface::enableContentBorderArea(QWindow *window, bool enable) +void QCocoaNativeInterface::setContentBorderAreaEnabled(QWindow *window, quintptr identifier, bool enable) { if (!window) return; QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); if (cocoaWindow) - cocoaWindow->enableContentBorderArea(enable); + cocoaWindow->setContentBorderAreaEnabled(identifier, enable); +} + +void QCocoaNativeInterface::setContentBorderEnabled(QWindow *window, bool enable) +{ + if (!window) + return; + + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); + if (cocoaWindow) + cocoaWindow->setContentBorderEnabled(enable); } void QCocoaNativeInterface::setNSToolbar(QWindow *window, void *nsToolbar) @@ -320,4 +334,15 @@ void QCocoaNativeInterface::setNSToolbar(QWindow *window, void *nsToolbar) cocoaWindow->updateNSToolbar(); } +bool QCocoaNativeInterface::testContentBorderPosition(QWindow *window, int position) +{ + if (!window) + return false; + + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); + if (cocoaWindow) + return cocoaWindow->testContentBorderAreaPosition(position); + return false; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm index 3061e84470..57d5c800e0 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm +++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm @@ -171,15 +171,18 @@ QPrint::DeviceState QCocoaPrintDevice::state() const QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const { - QCFString key; + CFStringRef key; double width; double height; - QCFString localizedName; + CFStringRef localizedName; if (PMPaperGetPPDPaperName(paper, &key) == noErr && PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) { - return(QPlatformPrintDevice::createPageSize(key, QSize(width, height), localizedName)); + QPageSize pageSize = QPlatformPrintDevice::createPageSize(QString::fromCFString(key),QSize(width, height), + QString::fromCFString(localizedName)); + CFRelease(localizedName); + return pageSize; } return QPageSize(); } @@ -216,10 +219,11 @@ QPageSize QCocoaPrintDevice::defaultPageSize() const QPageSize pageSize; PMPageFormat pageFormat; PMPaper paper; - if (PMCreatePageFormat(&pageFormat) == noErr - && PMSessionDefaultPageFormat(m_session, pageFormat) == noErr - && PMGetPageFormatPaper(pageFormat, &paper) == noErr) { - pageSize = createPageSize(paper); + if (PMCreatePageFormat(&pageFormat) == noErr) { + if (PMSessionDefaultPageFormat(m_session, pageFormat) == noErr + && PMGetPageFormatPaper(pageFormat, &paper) == noErr) { + pageSize = createPageSize(paper); + } PMRelease(pageFormat); } return pageSize; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index b7a6a14d4a..bb5c0c1974 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -211,7 +211,9 @@ public: void registerTouch(bool enable); void setContentBorderThickness(int topThickness, int bottomThickness); void registerContentBorderArea(quintptr identifier, int upper, int lower); - void enableContentBorderArea(bool enable); + void setContentBorderAreaEnabled(quintptr identifier, bool enable); + void setContentBorderEnabled(bool enable); + bool testContentBorderAreaPosition(int position) const; void applyContentBorderThickness(NSWindow *window); void updateNSToolbar(); @@ -270,6 +272,7 @@ public: // for QNSView bool m_geometryUpdateExposeAllowed; bool m_isExposed; QRect m_exposedGeometry; + qreal m_exposedDevicePixelRatio; int m_registerTouchCount; bool m_resizableTransientParent; bool m_hiddenByClipping; @@ -289,7 +292,8 @@ public: // for QNSView NSApplicationPresentationOptions m_presentationOptions; struct BorderRange { - BorderRange(int u, int l) : upper(u), lower(l) { } + BorderRange(quintptr i, int u, int l) : identifier(i), upper(u), lower(l) { } + quintptr identifier; int upper; int lower; bool operator<(BorderRange const& right) const { @@ -297,6 +301,7 @@ public: // for QNSView } }; QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower + QHash<quintptr, bool> m_enabledContentBorderAreas; // identifer -> enabled state (true/false) }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index b27e1b03db..5def64ee0a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -261,6 +261,8 @@ static bool isMouseEvent(NSEvent *ev) { [self close]; + QCocoaIntegration::instance()->setWindow(self, 0); + if (self.helper.grabbingMouse) { self.helper.releaseOnMouseUp = YES; } else { @@ -327,6 +329,7 @@ static bool isMouseEvent(NSEvent *ev) { [self.helper detachFromPlatformWindow]; [self close]; + QCocoaIntegration::instance()->setWindow(self, 0); [self release]; } @@ -619,11 +622,13 @@ void QCocoaWindow::setVisible(bool visible) // update the window geometry if there is a parent. setGeometry(window()->geometry()); - // Register popup windows so that the parent window can - // close them when needed. - if (window()->type() == Qt::Popup) { + // Register popup windows so that the parent window can close them when needed. + if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) { // qDebug() << "transientParent and popup" << window()->type() << Qt::Popup << (window()->type() & Qt::Popup); parentCocoaWindow->m_activePopupWindow = window(); + } + + if (window()->type() == Qt::Popup) { // QTBUG-30266: a window should not be resizable while a transient popup is open // Since this isn't a native popup, the window manager doesn't close the popup when you click outside NSUInteger parentStyleMask = [parentCocoaWindow->m_nsWindow styleMask]; @@ -1210,11 +1215,18 @@ QCocoaGLContext *QCocoaWindow::currentContext() const void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) { bool wasNSWindowChild = m_isNSWindowChild; - // TODO Set value for m_isNSWindowChild here + m_isNSWindowChild = parentWindow && (window()->property("_q_platform_MacUseNSWindow").toBool()); bool needsNSWindow = m_isNSWindowChild || !parentWindow; QCocoaWindow *oldParentCocoaWindow = m_parentCocoaWindow; m_parentCocoaWindow = const_cast<QCocoaWindow *>(static_cast<const QCocoaWindow *>(parentWindow)); + if (m_parentCocoaWindow && m_isNSWindowChild) { + QWindow *parentQWindow = m_parentCocoaWindow->window(); + if (!parentQWindow->property("_q_platform_MacUseNSWindow").toBool()) { + parentQWindow->setProperty("_q_platform_MacUseNSWindow", QVariant(true)); + m_parentCocoaWindow->recreateWindow(m_parentCocoaWindow->m_parentCocoaWindow); + } + } bool usesNSPanel = [m_nsWindow isKindOfClass:[QNSPanel class]]; @@ -1400,6 +1412,8 @@ QCocoaNSWindow * QCocoaWindow::createNSWindow() applyContentBorderThickness(createdWindow); + QCocoaIntegration::instance()->setWindow(createdWindow, this); + return createdWindow; } @@ -1466,7 +1480,7 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized) || (m_effectivelyMaximized && newState == Qt::WindowNoState)) { if ((m_synchedWindowState & Qt::WindowFullScreen) == (newState & Qt::WindowFullScreen)) { - [m_nsWindow performZoom : m_nsWindow]; // toggles + [m_nsWindow zoom : m_nsWindow]; // toggles m_effectivelyMaximized = !m_effectivelyMaximized; } else if (!(newState & Qt::WindowMaximized)) { // it would be nice to change the target geometry that toggleFullScreen will animate toward @@ -1582,28 +1596,17 @@ void QCocoaWindow::setContentBorderThickness(int topThickness, int bottomThickne void QCocoaWindow::registerContentBorderArea(quintptr identifier, int upper, int lower) { - m_contentBorderAreas.insert(identifier, BorderRange(upper, lower)); - - // Find consecutive registered border areas, starting from the top. - QList<BorderRange> ranges = m_contentBorderAreas.values(); - std::sort(ranges.begin(), ranges.end()); - m_topContentBorderThickness = 0; - foreach (BorderRange range, ranges) { - // Is this sub-range adjacent to or overlaping the - // existing total border area range? If so merge - // it into the total range, - if (range.upper <= (m_topContentBorderThickness + 1)) - m_topContentBorderThickness = qMax(m_topContentBorderThickness, range.lower); - else - break; - } + m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower)); + applyContentBorderThickness(m_nsWindow); +} - m_bottomContentBorderThickness = 0; // (not supported) - if (m_drawContentBorderGradient) - applyContentBorderThickness(m_nsWindow); +void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier, bool enable) +{ + m_enabledContentBorderAreas.insert(identifier, enable); + applyContentBorderThickness(m_nsWindow); } -void QCocoaWindow::enableContentBorderArea(bool enable) +void QCocoaWindow::setContentBorderEnabled(bool enable) { m_drawContentBorderGradient = enable; applyContentBorderThickness(m_nsWindow); @@ -1619,17 +1622,33 @@ void QCocoaWindow::applyContentBorderThickness(NSWindow *window) return; } - [window setStyleMask:[window styleMask] | NSTexturedBackgroundWindowMask]; + // Find consecutive registered border areas, starting from the top. + QList<BorderRange> ranges = m_contentBorderAreas.values(); + std::sort(ranges.begin(), ranges.end()); + int effectiveTopContentBorderThickness = m_topContentBorderThickness; + foreach (BorderRange range, ranges) { + // Skip disiabled ranges (typically hidden tool bars) + if (!m_enabledContentBorderAreas.value(range.identifier, false)) + continue; - if (m_topContentBorderThickness > 0) { - [window setContentBorderThickness:m_topContentBorderThickness forEdge:NSMaxYEdge]; - [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; + // Is this sub-range adjacent to or overlaping the + // existing total border area range? If so merge + // it into the total range, + if (range.upper <= (effectiveTopContentBorderThickness + 1)) + effectiveTopContentBorderThickness = qMax(effectiveTopContentBorderThickness, range.lower); + else + break; } - if (m_bottomContentBorderThickness > 0) { - [window setContentBorderThickness:m_topContentBorderThickness forEdge:NSMinYEdge]; - [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge]; - } + int effectiveBottomContentBorderThickness = m_bottomContentBorderThickness; + + [window setStyleMask:[window styleMask] | NSTexturedBackgroundWindowMask]; + + [window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge]; + [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; + + [window setContentBorderThickness:effectiveBottomContentBorderThickness forEdge:NSMinYEdge]; + [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge]; } void QCocoaWindow::updateNSToolbar() @@ -1646,6 +1665,12 @@ void QCocoaWindow::updateNSToolbar() [m_nsWindow setShowsToolbarButton:YES]; } +bool QCocoaWindow::testContentBorderAreaPosition(int position) const +{ + return m_nsWindow && m_drawContentBorderGradient && + 0 <= position && position < [m_nsWindow contentBorderThicknessForEdge: NSMaxYEdge]; +} + qreal QCocoaWindow::devicePixelRatio() const { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @@ -1685,6 +1710,7 @@ void QCocoaWindow::exposeWindow() if (!m_isExposed) { m_isExposed = true; m_exposedGeometry = geometry(); + m_exposedDevicePixelRatio = devicePixelRatio(); QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); } } @@ -1710,11 +1736,12 @@ void QCocoaWindow::updateExposedGeometry() if (!isWindowExposable()) return; - if (m_exposedGeometry == geometry()) + if (m_exposedGeometry == geometry() && m_exposedDevicePixelRatio == devicePixelRatio()) return; m_isExposed = true; m_exposedGeometry = geometry(); + m_exposedDevicePixelRatio = devicePixelRatio(); QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); } diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm index 9368b65866..6549f127b6 100644 --- a/src/plugins/platforms/cocoa/qmacclipboard.mm +++ b/src/plugins/platforms/cocoa/qmacclipboard.mm @@ -305,6 +305,11 @@ QMacPasteboard::setMimeData(QMimeData *mime_src) QString mimeType = formats.at(f); for (QList<QMacInternalPasteboardMime *>::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) { QMacInternalPasteboardMime *c = (*it); + // Hack: The Rtf handler converts incoming Rtf to Html. We do + // not want to convert outgoing Html to Rtf but instead keep + // posting it as Html. Skip the Rtf handler here. + if (c->convertorName() == QStringLiteral("Rtf")) + continue; QString flavor(c->flavorFor(mimeType)); if (!flavor.isEmpty()) { QVariant mimeData = static_cast<QMacMimeData*>(mime_src)->variantData(mimeType); diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 1197aa9148..0b9683a3ef 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -329,6 +329,7 @@ static QTouchDevice *touchDevice = 0; if (m_window) { NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen]; if (screenIndex != NSNotFound) { + m_platformWindow->updateExposedGeometry(); QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenAtIndex(screenIndex); QWindowSystemInterface::handleWindowScreenChanged(m_window, cocoaScreen->screen()); } @@ -371,8 +372,9 @@ static QTouchDevice *touchDevice = 0; { m_backingStore = backingStore; m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio(); - QRect br = region.boundingRect(); - [self setNeedsDisplayInRect:NSMakeRect(br.x(), br.y(), br.width(), br.height())]; + foreach (QRect rect, region.rects()) { + [self setNeedsDisplayInRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())]; + } } - (BOOL) hasMask @@ -1647,6 +1649,21 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) } } +static QWindow *findEventTargetWindow(QWindow *candidate) +{ + while (candidate) { + if (!(candidate->flags() & Qt::WindowTransparentForInput)) + return candidate; + candidate = candidate->parent(); + } + return candidate; +} + +static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point) +{ + return target->mapFromGlobal(source->mapToGlobal(point)); +} + - (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal { Q_UNUSED(isLocal); @@ -1676,16 +1693,18 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) QPoint qt_windowPoint(windowPoint.x, windowPoint.y); Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QWindow *target = findEventTargetWindow(m_window); + // update these so selecting move/copy/link works QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]]; QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); if ([sender draggingSource] != nil) { QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - response = QWindowSystemInterface::handleDrag(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed); + response = QWindowSystemInterface::handleDrag(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_window, target, qt_windowPoint), qtAllowed); } else { QCocoaDropData mimeData([sender draggingPasteboard]); - response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed); + response = QWindowSystemInterface::handleDrag(target, &mimeData, mapWindowCoordinates(m_window, target, qt_windowPoint), qtAllowed); } return qt_mac_mapDropAction(response.acceptedAction()); @@ -1693,16 +1712,20 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)draggingExited:(id <NSDraggingInfo>)sender { + QWindow *target = findEventTargetWindow(m_window); + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; QPoint qt_windowPoint(windowPoint.x, windowPoint.y); // Send 0 mime data to indicate drag exit - QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction); + QWindowSystemInterface::handleDrag(target, 0, mapWindowCoordinates(m_window, target, qt_windowPoint), Qt::IgnoreAction); } // called on drop, send the drop to Qt and return if it was accepted. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { + QWindow *target = findEventTargetWindow(m_window); + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; QPoint qt_windowPoint(windowPoint.x, windowPoint.y); Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); @@ -1710,10 +1733,10 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) QPlatformDropQtResponse response(false, Qt::IgnoreAction); if ([sender draggingSource] != nil) { QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - response = QWindowSystemInterface::handleDrop(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed); + response = QWindowSystemInterface::handleDrop(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_window, target, qt_windowPoint), qtAllowed); } else { QCocoaDropData mimeData([sender draggingPasteboard]); - response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed); + response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_window, target, qt_windowPoint), qtAllowed); } if (response.isAccepted()) { QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); @@ -1726,6 +1749,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) { Q_UNUSED(img); Q_UNUSED(operation); + QWindow *target = findEventTargetWindow(m_window); // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly @@ -1738,7 +1762,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) NSPoint screenPoint = [window convertBaseToScreen :point]; QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y)); - QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons); + QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_window, target, qtWindowPoint), qtScreenPoint, m_buttons); } @end diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 31e3e343b9..d18a01b11c 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -59,8 +59,7 @@ return nil; QAccessible::Id childId = QAccessible::uniqueId(m_window->accessibleRoot()); - QCocoaAccessibleElement *child = [QCocoaAccessibleElement createElementWithId: childId parent: self]; - return [child autorelease]; + return [QCocoaAccessibleElement elementWithId: childId]; } // The QNSView is a container that the user does not interact directly with: |