diff options
author | Gabriel de Dietrich <gabriel.dietrich-de@nokia.com> | 2011-02-08 11:57:34 +0100 |
---|---|---|
committer | Gabriel de Dietrich <gabriel.dietrich-de@nokia.com> | 2011-02-08 11:57:34 +0100 |
commit | eb7958c65b09569c2531d2c443a6d0fe0b507d97 (patch) | |
tree | 91544e4a4cf5c65b3acec7c219ebe63176a14994 /src/gui | |
parent | ef998bca70a799806c5754edfa49ea625881bc3e (diff) | |
parent | 44298c848ac254fe1942eb32eed7651dec5bf0e3 (diff) | |
download | qt4-tools-eb7958c65b09569c2531d2c443a6d0fe0b507d97.tar.gz |
Merge branch 'master-upstream'
Diffstat (limited to 'src/gui')
78 files changed, 3195 insertions, 1766 deletions
diff --git a/src/gui/embedded/qkbd_defaultmap_qws_p.h b/src/gui/embedded/qkbd_defaultmap_qws_p.h index 0444093957..017f56deb6 100644 --- a/src/gui/embedded/qkbd_defaultmap_qws_p.h +++ b/src/gui/embedded/qkbd_defaultmap_qws_p.h @@ -649,6 +649,7 @@ const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = { { 119, 0xffff, 0x01000008, 0x00, 0x00, 0x0000 }, { 138, 0xffff, 0x01000058, 0x00, 0x00, 0x0000 }, { 139, 0xffff, 0x01000055, 0x00, 0x00, 0x0000 }, + { 152, 0xffff, 0x010000ba, 0x00, 0x00, 0x0000 }, }; diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index 859f7fc888..b8586cef21 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -194,7 +194,8 @@ void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo) for (int k = 0; k < span; ++k) { boxes[start + k].combine(extras[k]); - stretches[start + k] = qMax(stretches[start + k], stretch); + if (stretch != 0) + stretches[start + k] = qMax(stretches[start + k], stretch); } } multiCellMap.clear(); @@ -1481,7 +1482,7 @@ void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutSt QGridLayoutBox *box; if (effectiveRowSpan == 1) { box = &rowBox; - if (!userRowStretch) + if (!userRowStretch && itemStretch != 0) rowStretch = qMax(rowStretch, itemStretch); } else { QGridLayoutMultiCellData &multiCell = diff --git a/src/gui/gui.pro b/src/gui/gui.pro index fe1a595af3..076fe0af31 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -65,15 +65,14 @@ symbian { neon:*-g++* { DEFINES += QT_HAVE_NEON HEADERS += $$NEON_HEADERS - SOURCES += $$NEON_SOURCES DRAWHELPER_NEON_ASM_FILES = $$NEON_ASM - neon_compiler.commands = $$QMAKE_CXX -c -mfpu=neon - neon_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + neon_compiler.commands = $$QMAKE_CXX -c + neon_compiler.commands += $(CXXFLAGS) -mfpu=neon $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} neon_compiler.dependency_type = TYPE_C neon_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - neon_compiler.input = DRAWHELPER_NEON_ASM_FILES + neon_compiler.input = DRAWHELPER_NEON_ASM_FILES NEON_SOURCES neon_compiler.variable_out = OBJECTS neon_compiler.name = compiling[neon] ${QMAKE_FILE_IN} silent:neon_compiler.commands = @echo compiling[neon] ${QMAKE_FILE_IN} && $$neon_compiler.commands diff --git a/src/gui/image/qgifhandler.cpp b/src/gui/image/qgifhandler.cpp index 4dd4743f78..7cb7373b44 100644 --- a/src/gui/image/qgifhandler.cpp +++ b/src/gui/image/qgifhandler.cpp @@ -1046,7 +1046,7 @@ QGifHandler::QGifHandler() { gifFormat = new QGIFFormat; nextDelay = 100; - loopCnt = 1; + loopCnt = -1; frameNumber = -1; scanIsCached = false; } @@ -1192,7 +1192,13 @@ int QGifHandler::loopCount() const QGIFFormat::scan(device(), &imageSizes, &loopCnt); scanIsCached = true; } - return loopCnt-1; // In GIF, loop count is iteration count, so subtract one + + if (loopCnt == 0) + return -1; + else if (loopCnt == -1) + return 0; + else + return loopCnt; } int QGifHandler::currentImageNumber() const diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp index 01baa36016..8face87a92 100644 --- a/src/gui/image/qnativeimage.cpp +++ b/src/gui/image/qnativeimage.cpp @@ -181,15 +181,17 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format,bool /* if (ok) { xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0); xshminfo.shmaddr = xshmimg->data; - if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) - qWarning() << "Error while marking the shared memory segment to be destroyed"; ok = (xshminfo.shmaddr != (char*)-1); if (ok) image = QImage((uchar *)xshmimg->data, width, height, format); } xshminfo.readOnly = false; - if (ok) + if (ok) { ok = XShmAttach(X11->display, &xshminfo); + XSync(X11->display, False); + if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) + qWarning() << "Error while marking the shared memory segment to be destroyed"; + } if (!ok) { qWarning() << "QNativeImage: Unable to attach to shared memory segment."; if (xshmimg->data) { diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index ff199b14e6..1bef64de87 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -706,6 +706,7 @@ void QCoeFepInputContext::CancelFepInlineEdit() QInputMethodEvent event(QLatin1String(""), attributes); event.setCommitString(QLatin1String(""), 0, 0); m_preeditString.clear(); + m_inlinePosition = 0; sendEvent(event); } @@ -853,6 +854,7 @@ void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) QInputMethodEvent event(QLatin1String(""), attributes); event.setCommitString(m_preeditString, 0, 0); m_preeditString.clear(); + m_inlinePosition = 0; sendEvent(event); m_hasTempPreeditString = false; diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 4261e93438..0ff3d883d2 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -222,7 +222,8 @@ qpa { kernel/qplatformglcontext_qpa.h \ kernel/qdesktopwidget_qpa_p.h \ kernel/qplatformeventloopintegration_qpa.h \ - kernel/qplatformcursor_qpa.h + kernel/qplatformcursor_qpa.h \ + kernel/qplatformclipboard_qpa.h SOURCES += \ kernel/qapplication_qpa.cpp \ @@ -244,7 +245,8 @@ qpa { kernel/qplatformwindowformat_qpa.cpp \ kernel/qplatformeventloopintegration_qpa.cpp \ kernel/qplatformglcontext_qpa.cpp \ - kernel/qplatformcursor_qpa.cpp + kernel/qplatformcursor_qpa.cpp \ + kernel/qplatformclipboard_qpa.cpp contains(QT_CONFIG, glib) { SOURCES += \ diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 5c4a314770..e91fe04d50 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -2771,7 +2771,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { for (int i = 0; i < leaveList.size(); ++i) { w = leaveList.at(i); if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) { -#if defined(Q_WS_WIN) || defined(Q_WS_X11) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) if (leaveAfterRelease == w) leaveAfterRelease = 0; #endif @@ -3142,13 +3142,11 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // Dispatch enter/leave if: // 1) the mouse grabber is an alien widget // 2) the button is released on an alien widget - QWidget *enter = 0; if (nativeGuard) enter = alienGuard ? alienWidget : nativeWidget; else // The receiver is typically deleted on mouse release with drag'n'drop. enter = QApplication::widgetAt(event->globalPos()); - dispatchEnterLeave(enter, leaveAfterRelease); leaveAfterRelease = 0; lastMouseReceiver = enter; @@ -3695,15 +3693,6 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); -#ifdef QT_MAC_USE_COCOA - // We use native NSCursor stacks in Cocoa. The currentCursor is the - // top of this stack. So to avoid flickering of cursor, we have to - // change the cusor instead of pop-ing the existing OverrideCursor - // and pushing the new one. - qApp->d_func()->cursor_list.prepend(cursor); - qt_cocoaChangeOverrideCursor(cursor); - return; -#endif setOverrideCursor(cursor); } #endif @@ -4434,6 +4423,24 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; } #endif // QT_NO_GESTURES +#ifdef QT_MAC_USE_COCOA + case QEvent::Enter: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(true); + } + res = d->notify_helper(receiver, e); + break; + case QEvent::Leave: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(false); + } + res = d->notify_helper(receiver, e); + break; +#endif default: res = d->notify_helper(receiver, e); break; diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index b22a6b3c2d..f607a72e92 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -165,6 +165,7 @@ QT_BEGIN_NAMESPACE //for qt_mac.h QPaintDevice *qt_mac_safe_pdev = 0; QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0; +QPointer<QWidget> topLevelAt_cache = 0; /***************************************************************************** Internal variables and functions @@ -192,7 +193,6 @@ static bool qt_mac_previous_press_in_popup_mode = false; static bool qt_mac_no_click_through_mode = false; static int tablet_button_state = 0; #endif -QPointer<QWidget> qt_mouseover; #if defined(QT_DEBUG) static bool appNoGrab = false; // mouse/keyboard grabbing #endif @@ -216,11 +216,12 @@ extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.cpp extern QWidget *qt_mac_find_window(OSWindowRef); //qwidget_mac.cpp -extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.cpp +extern void qt_mac_set_cursor(const QCursor *); //qcursor_mac.cpp extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp extern void qt_mac_command_set_enabled(MenuRef, UInt32, bool); //qmenu_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplication.cpp +extern void qt_mac_update_cursor(); // qcursor_mac.mm // Forward Decls void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); @@ -1364,43 +1365,16 @@ void QApplication::setMainWidget(QWidget *mainWidget) /***************************************************************************** QApplication cursor stack *****************************************************************************/ -#ifdef QT_MAC_USE_COCOA -void QApplicationPrivate::disableUsageOfCursorRects(bool disable) -{ - // In Cocoa there are two competing ways of setting the cursor; either - // by using cursor rects (see qcocoaview_mac.mm), or by pushing/popping - // the cursor manually. When we use override cursors, it makes most sense - // to use the latter. But then we need to tell cocoa to stop using the - // first approach so it doesn't change the cursor back when hovering over - // a cursor rect: - QWidgetList topLevels = qApp->topLevelWidgets(); - for (int i=0; i<topLevels.size(); ++i) { - if (NSWindow *window = qt_mac_window_for(topLevels.at(i))) - disable ? [window disableCursorRects] : [window enableCursorRects]; - } -} - -void QApplicationPrivate::updateOverrideCursor() -{ - // Sometimes Cocoa forgets that we have set a Cursor - // manually. In those cases, remind it again: - if (QCursor *override = qApp->overrideCursor()) - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*override)) set]; -} -#endif void QApplication::setOverrideCursor(const QCursor &cursor) { qApp->d_func()->cursor_list.prepend(cursor); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - if (qApp->d_func()->cursor_list.size() == 1) - qApp->d_func()->disableUsageOfCursorRects(true); - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) push]; + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) - qt_mac_set_cursor(&qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(&qApp->d_func()->cursor_list.first()); #endif } @@ -1411,14 +1385,11 @@ void QApplication::restoreOverrideCursor() qApp->d_func()->cursor_list.removeFirst(); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - [NSCursor pop]; - if (qApp->d_func()->cursor_list.isEmpty()) - qApp->d_func()->disableUsageOfCursorRects(false); + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) { const QCursor def(Qt::ArrowCursor); - qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first()); } #endif } @@ -1431,30 +1402,54 @@ QWidget *QApplication::topLevelAt(const QPoint &p) qt_mac_window_at(p.x(), p.y(), &widget); return widget; #else + // Use a cache to avoid iterate through the whole list of windows for all + // calls to to topLevelAt. We e.g. do this for each and every mouse + // move since we need to find the widget under mouse: + if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p)) + return topLevelAt_cache; + + // INVARIANT: Cache miss. Go through the list if windows instead: + QMacCocoaAutoReleasePool pool; + NSPoint cocoaPoint = flipPoint(p); NSInteger windowCount; NSCountWindows(&windowCount); if (windowCount <= 0) return 0; // There's no window to find! - QMacCocoaAutoReleasePool pool; - NSPoint cocoaPoint = flipPoint(p); + QVarLengthArray<NSInteger> windowList(windowCount); NSWindowList(windowCount, windowList.data()); + int firstQtWindowFound = -1; for (int i = 0; i < windowCount; ++i) { NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]]; - if (window && NSPointInRect(cocoaPoint, [window frame])) { + if (window) { QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; - // Check to see if there's a hole in the window where the mask is. - // If there is, we should just continue to see if there is a window below. - if (candidateWindow && !candidateWindow->mask().isEmpty()) { - QPoint localPoint = candidateWindow->mapFromGlobal(p); - if (!candidateWindow->mask().contains(localPoint)) { - continue; + if (candidateWindow && firstQtWindowFound == -1) + firstQtWindowFound = i; + + if (NSPointInRect(cocoaPoint, [window frame])) { + // Check to see if there's a hole in the window where the mask is. + // If there is, we should just continue to see if there is a window below. + if (candidateWindow && !candidateWindow->mask().isEmpty()) { + QPoint localPoint = candidateWindow->mapFromGlobal(p); + if (!candidateWindow->mask().contains(localPoint)) + continue; + else + return candidateWindow; + } else { + if (i == firstQtWindowFound) { + // The cache will only work when the window under mouse is + // top most (that is, not partially obscured by other windows. + // And we only set it if no mask is present to optimize for the common case: + topLevelAt_cache = candidateWindow; + } + return candidateWindow; } } - return candidateWindow; } } - return 0; // Couldn't find a window at this point + + topLevelAt_cache = 0; + return 0; #endif } @@ -1480,8 +1475,8 @@ void QApplicationPrivate::enterModal_sys(QWidget *widget) if (!qt_modal_stack) qt_modal_stack = new QWidgetList; - dispatchEnterLeave(0, qt_mouseover); - qt_mouseover = 0; + dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; qt_modal_stack->insert(0, widget); if (!app_do_modal) @@ -1512,8 +1507,8 @@ void QApplicationPrivate::leaveModal_sys(QWidget *widget) w = grabber; else w = QApplication::widgetAt(p.x(), p.y()); - dispatchEnterLeave(w, qt_mouseover); // send synthetic enter event - qt_mouseover = w; + dispatchEnterLeave(w, qt_last_mouse_receiver); // send synthetic enter event + qt_last_mouse_receiver = w; } #ifdef QT_MAC_USE_COCOA if (!qt_mac_is_macsheet(widget)) @@ -1938,7 +1933,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event } } } - qt_mac_set_cursor(&cursor, QPoint(where.h, where.v)); + qt_mac_set_cursor(&cursor); } //This mouse button state stuff looks like this on purpose @@ -2132,20 +2127,20 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event QWidget * const enterLeaveWidget = (inPopupMode || ekind == kEventMouseUp) ? QApplication::widgetAt(where.h, where.v) : static_cast<QWidget*>(widget); - if ((QWidget *) qt_mouseover != enterLeaveWidget || inNonClientArea) { + if ((QWidget *) qt_last_mouse_receiver != enterLeaveWidget || inNonClientArea) { #ifdef DEBUG_MOUSE_MAPS qDebug("Entering: %p - %s (%s), Leaving %s (%s)", (QWidget*)enterLeaveWidget, enterLeaveWidget ? enterLeaveWidget->metaObject()->className() : "none", enterLeaveWidget ? enterLeaveWidget->objectName().toLocal8Bit().constData() : "", - qt_mouseover ? qt_mouseover->metaObject()->className() : "none", - qt_mouseover ? qt_mouseover->objectName().toLocal8Bit().constData() : ""); + qt_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none", + qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : ""); #endif QWidget * const mouseGrabber = QWidget::mouseGrabber(); if (inPopupMode) { QWidget *enter = enterLeaveWidget; - QWidget *leave = qt_mouseover; + QWidget *leave = qt_last_mouse_receiver; if (mouseGrabber) { QWidget * const popupWidget = qApp->activePopupWidget(); if (leave == popupWidget) @@ -2155,15 +2150,15 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if ((enter == mouseGrabber && leave == popupWidget) || (leave == mouseGrabber && enter == popupWidget)) { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } } else { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } - } else if ((!qt_button_down || !qt_mouseover) && !mouseGrabber && !leaveAfterRelease) { - QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_mouseover); - qt_mouseover = enterLeaveWidget; + } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) { + QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = enterLeaveWidget; } } break; } @@ -2240,7 +2235,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if (leaveAfterRelease) { QWidget *enter = QApplication::widgetAt(where.h, where.v); QApplicationPrivate::dispatchEnterLeave(enter, leaveAfterRelease); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; leaveAfterRelease = 0; } @@ -2503,7 +2498,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event void QApplicationPrivate::qt_initAfterNSAppStarted() { setupAppleEvents(); - updateOverrideCursor(); + qt_mac_update_cursor(); } void QApplicationPrivate::setupAppleEvents() @@ -3080,7 +3075,7 @@ void onApplicationWindowChangedActivation(QWidget *widget, bool activated) } QMenuBar::macUpdateMenuBar(); - QApplicationPrivate::updateOverrideCursor(); + qt_mac_update_cursor(); #else Q_UNUSED(widget); Q_UNUSED(activated); @@ -3115,6 +3110,7 @@ void onApplicationChangedActivation( bool activated ) app->setActiveWindow(tmp_w); } QMenuBar::macUpdateMenuBar(); + qt_mac_update_cursor(); } else { // de-activated QApplicationPrivate *priv = [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate]; while (priv->inPopupMode()) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index f9508a7bb4..a6815a8799 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -489,8 +489,6 @@ public: #ifdef QT_MAC_USE_COCOA static void qt_initAfterNSAppStarted(); static void setupAppleEvents(); - static void updateOverrideCursor(); - static void disableUsageOfCursorRects(bool disable); #endif static bool qt_mac_apply_settings(); #endif @@ -508,6 +506,8 @@ public: static void processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e); static void processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e); + static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); + static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e); // static void reportScreenCount(int count); diff --git a/src/gui/kernel/qapplication_qpa.cpp b/src/gui/kernel/qapplication_qpa.cpp index ece035c9c6..cd76adf37c 100644 --- a/src/gui/kernel/qapplication_qpa.cpp +++ b/src/gui/kernel/qapplication_qpa.cpp @@ -79,7 +79,6 @@ int qt_last_x = 0; int qt_last_y = 0; QPointer<QWidget> qt_last_mouse_receiver = 0; -static Qt::KeyboardModifiers modifiers = Qt::NoModifier; static Qt::MouseButtons buttons = Qt::NoButton; static ulong mousePressTime; static Qt::MouseButton mousePressButton = Qt::NoButton; @@ -111,6 +110,9 @@ void QApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate case QWindowSystemInterfacePrivate::Leave: QApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); break; + case QWindowSystemInterfacePrivate::ActivatedWindow: + QApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + break; case QWindowSystemInterfacePrivate::Close: QApplicationPrivate::processCloseEvent( static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e)); @@ -441,11 +443,11 @@ void QApplication::alert(QWidget *, int) { } -static void init_platform(const QString &name) +static void init_platform(const QString &name, const QString &platformPluginPath) { - QApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name); + QApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, platformPluginPath); if (!QApplicationPrivate::platform_integration) { - QStringList keys = QPlatformIntegrationFactory::keys(); + QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); QString fatalMessage = QString::fromLatin1("Failed to load platform plugin \"%1\". Available platforms are: \n").arg(name); foreach(QString key, keys) { @@ -510,6 +512,7 @@ void qt_init(QApplicationPrivate *priv, int type) } QList<QByteArray> pluginList; + QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); QString platformName = QLatin1String(qgetenv("QT_QPA_PLATFORM")); // Get command line params @@ -524,6 +527,9 @@ void qt_init(QApplicationPrivate *priv, int type) if (arg == "-fn" || arg == "-font") { if (++i < argc) appFont = QString::fromLocal8Bit(argv[i]); + } else if (arg == "-platformpluginpath") { + if (++i < argc) + platformPluginPath = QLatin1String(argv[i]); } else if (arg == "-platform") { if (++i < argc) platformName = QLatin1String(argv[i]); @@ -547,7 +553,7 @@ void qt_init(QApplicationPrivate *priv, int type) } #endif - init_platform(platformName); + init_platform(platformName, platformPluginPath); init_plugins(pluginList); QColormap::initialize(); @@ -724,7 +730,7 @@ void QApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mouse // qDebug() << "sending mouse ev." << ev.type() << localPoint << globalPoint << ev.button() << ev.buttons() << mouseWidget << "mouse grabber" << implicit_mouse_grabber; - QMouseEvent ev(type, localPoint, globalPoint, button, buttons, modifiers); + QMouseEvent ev(type, localPoint, globalPoint, button, buttons, QApplication::keyboardModifiers()); QList<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); foreach (QWeakPointer<QPlatformCursor> cursor, cursors) { @@ -732,7 +738,15 @@ void QApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mouse cursor.data()->pointerEvent(ev); } + int oldOpenPopupCount = openPopupCount; QApplication::sendSpontaneousEvent(mouseWidget, &ev); + +#ifndef QT_NO_CONTEXTMENU + if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { + QContextMenuEvent e(QContextMenuEvent::Mouse, localPoint, globalPoint, QApplication::keyboardModifiers()); + QApplication::sendSpontaneousEvent(mouseWidget, &e); + } +#endif // QT_NO_CONTEXTMENU } @@ -771,7 +785,7 @@ void QApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wheel p = mouseWidget->mapFromGlobal(globalPoint); } - QWheelEvent ev(p, globalPoint, e->delta, buttons, modifiers, + QWheelEvent ev(p, globalPoint, e->delta, buttons, QApplication::keyboardModifiers(), e->orient); QApplication::sendSpontaneousEvent(mouseWidget, &ev); } @@ -802,7 +816,6 @@ void QApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEven if (app_do_modal && !qt_try_modal(focusW, e->keyType)) return; - modifiers = e->modifiers; QKeyEvent ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount); QApplication::sendSpontaneousEvent(focusW, &ev); } @@ -823,6 +836,10 @@ void QApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::Leave } +void QApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e) +{ + QApplication::setActiveWindow(e->activated.data()); +} void QApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) { diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 3a70dd5ade..a9f57f430f 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -1616,7 +1616,9 @@ void qt_init(QApplicationPrivate * /* priv */, int) qRegisterMetaType<WId>("WId"); } -extern void qt_cleanup_symbianFontDatabaseExtras(); // qfontdatabase_s60.cpp +#ifdef QT_NO_FREETYPE +extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp +#endif /***************************************************************************** qt_cleanup() - cleans up when the application is finished @@ -1633,7 +1635,9 @@ void qt_cleanup() QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles QPixmapCache::clear(); // Has to happen now, since QS60PixmapData has FBS handles - qt_cleanup_symbianFontDatabaseExtras(); +#ifdef QT_NO_FREETYPE + qt_cleanup_symbianFontDatabase(); +#endif // S60 structure and window server session are freed in eventdispatcher destructor as they are needed there // It's important that this happens here, before the event dispatcher gets @@ -2019,6 +2023,9 @@ int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); } #endif +#ifdef QT_SOFTKEYS_ENABLED + QSoftKeyManager::updateSoftKeys(); +#endif break; case EEventFocusLost: if (callSymbianEventFilters(symbianEvent)) diff --git a/src/gui/kernel/qclipboard_qpa.cpp b/src/gui/kernel/qclipboard_qpa.cpp index 92b9e83255..b8ce60e00d 100644 --- a/src/gui/kernel/qclipboard_qpa.cpp +++ b/src/gui/kernel/qclipboard_qpa.cpp @@ -44,73 +44,16 @@ #ifndef QT_NO_CLIPBOARD #include "qmimedata.h" -#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qplatformclipboard_qpa.h" QT_BEGIN_NAMESPACE QT_USE_NAMESPACE - -class QClipboardData -{ -public: - QClipboardData(); - ~QClipboardData(); - - void setSource(QMimeData* s) - { - if (s == src) - return; - delete src; - src = s; - } - QMimeData* source() - { return src; } - - void clear(); - -private: - QMimeData* src; -}; - -QClipboardData::QClipboardData() -{ - src = 0; -} - -QClipboardData::~QClipboardData() -{ - delete src; -} - -void QClipboardData::clear() -{ - delete src; - src = 0; -} - - -static QClipboardData *internalCbData = 0; - -static void cleanupClipboardData() -{ - delete internalCbData; - internalCbData = 0; -} - -static QClipboardData *clipboardData() -{ - if (internalCbData == 0) { - internalCbData = new QClipboardData; - qAddPostRoutine(cleanupClipboardData); - } - return internalCbData; -} - - void QClipboard::clear(Mode mode) { - setText(QString(), mode); + setMimeData(0,mode); } @@ -121,26 +64,25 @@ bool QClipboard::event(QEvent *e) const QMimeData* QClipboard::mimeData(Mode mode) const { - if (mode != Clipboard) return 0; - - QClipboardData *d = clipboardData(); - return d->source(); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return 0; + return clipboard->mimeData(mode); } void QClipboard::setMimeData(QMimeData* src, Mode mode) { - if (mode != Clipboard) return; - - QClipboardData *d = clipboardData(); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return; - d->setSource(src); + clipboard->setMimeData(src,mode); - emitChanged(QClipboard::Clipboard); + emitChanged(mode); } bool QClipboard::supportsMode(Mode mode) const { - return (mode == Clipboard); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + return clipboard->supportsMode(mode); } bool QClipboard::ownsMode(Mode mode) const diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index bf4d9e5d49..6d7bc19af9 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -90,6 +90,9 @@ QT_BEGIN_NAMESPACE extern void onApplicationChangedActivation(bool); // qapplication_mac.mm extern void qt_release_apple_event_handler(); //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp + QT_END_NAMESPACE QT_FORWARD_DECLARE_CLASS(QDesktopWidgetImplementation) @@ -254,7 +257,18 @@ static void cleanupCocoaApplicationDelegate() if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)]) [reflectionDelegate applicationDidBecomeActive:notification]; + onApplicationChangedActivation(true); + + if (!QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0); + qt_last_mouse_receiver = widgetUnderMouse; + } } - (void)applicationDidResignActive:(NSNotification *)notification @@ -262,7 +276,13 @@ static void cleanupCocoaApplicationDelegate() if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)]) [reflectionDelegate applicationDidResignActive:notification]; + onApplicationChangedActivation(false); + + if (!QWidget::mouseGrabber()) + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_button_down = 0; } - (void)applicationDidChangeScreenParameters:(NSNotification *)notification diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm index a0f17bcb50..67a12e25f8 100644 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ b/src/gui/kernel/qcocoapanel_mac.mm @@ -47,9 +47,10 @@ #import <private/qcocoaview_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> -#include <private/qapplication_p.h> -#include <private/qbackingstore_p.h> - +#import <private/qmultitouch_mac_p.h> +#import <private/qapplication_p.h> +#import <private/qbackingstore_p.h> +#import <private/qdnd_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h index e4dd73b7cb..b41a4b5ab6 100644 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ b/src/gui/kernel/qcocoapanel_mac_p.h @@ -50,20 +50,27 @@ // We mean it. // +#ifndef QCOCOAPANEL_MAC_P +#define QCOCOAPANEL_MAC_P + #include "qmacdefines_mac.h" #ifdef QT_MAC_USE_COCOA #import <Cocoa/Cocoa.h> QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); @interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h index df0f24e96f..1c9258afa3 100644 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h @@ -62,7 +62,6 @@ extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); - QT_END_NAMESPACE - (id)initWithContentRect:(NSRect)contentRect @@ -145,66 +144,27 @@ QT_END_NAMESPACE - (void)sendEvent:(NSEvent *)event { - QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - // Cocoa can hold onto the window after we've disavowed its knowledge. So, - // if we get sent an event afterwards just have it go through the super's - // version and don't do any stuff with Qt. - if (!widget) { - [super sendEvent:event]; - return; - } - [self retain]; - QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget)); - Qt::MouseButton mouseButton = cocoaButton2QtButton([event buttonNumber]); bool handled = false; - // sometimes need to redirect mouse events to the popup. - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != widget) { - switch([event type]) - { - case NSLeftMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - // Don't call super here. This prevents us from getting the mouseUp event, - // which we need to send even if the mouseDown event was not accepted. - // (this is standard Qt behavior.) - break; - case NSRightMouseDown: - case NSOtherMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - break; - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonRelease, mouseButton); - qt_button_down = 0; - break; - case NSMouseMoved: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, Qt::NoButton); - break; - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = view; - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = event; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, mouseButton); - break; - default: - [super sendEvent:event]; - break; - } - } else { - [super sendEvent:event]; + switch([event type]) { + case NSMouseMoved: + // Cocoa sends move events to a parent and all its children under the mouse, much + // like Qt handles hover events. But we only want to handle the move event once, so + // to optimize a bit (since we subscribe for move event for all views), we handle it + // here before this logic happends. Note: it might be tempting to do this shortcut for + // all mouse events. The problem is that Cocoa does more than just find the correct view + // when sending the event, like raising windows etc. So avoid it as much as possible: + handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0); + break; + default: + break; } - if (!handled) - qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton); - + if (!handled) { + [super sendEvent:event]; + qt_mac_handleNonClientAreaMouseEvent(self, event); + } [self release]; } @@ -237,6 +197,56 @@ QT_END_NAMESPACE return [super frameViewClassForStyleMask:styleMask]; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +- (void)touchesBeganWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + -(void)registerDragTypes { // Calling registerForDraggedTypes below is slow, so only do @@ -259,21 +269,47 @@ QT_END_NAMESPACE NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; // Add custom types supported by the application. for (int i = 0; i < customTypes.size(); i++) { - [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))]; + [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])]; } [self registerForDraggedTypes:supportedTypes]; } } -- (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender +- (void)removeDropData +{ + if (dropData) { + delete dropData; + dropData = 0; + } +} + +- (void)addDropData:(id <NSDraggingInfo>)sender { - // Do a hittest to find the NSView under the - // mouse, and return the corresponding QWidget: - NSPoint windowPoint = [sender draggingLocation]; - NSView *candidateView = [[self contentView] hitTest:windowPoint]; - if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) - return 0; - return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget]; + [self removeDropData]; + CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; + dropData = new QCocoaDropData(dropPasteboard); +} + +- (void)changeDraggingCursor:(NSDragOperation)newOperation +{ + static SEL action = nil; + static bool operationSupported = false; + if (action == nil) { + action = NSSelectorFromString(@"operationNotAllowedCursor"); + if ([NSCursor respondsToSelector:action]) { + operationSupported = true; + } + } + if (operationSupported) { + NSCursor *notAllowedCursor = [NSCursor performSelector:action]; + bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); + if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { + [notAllowedCursor push]; + } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { + [notAllowedCursor pop]; + } + + } } - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender @@ -286,64 +322,211 @@ QT_END_NAMESPACE // registerForDraggedTypes on the views will severly degrade initialization time // for an application that uses a lot of drag subscribing widgets. - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + *currentDragTarget() = qwidget; + if (!qwidget) return [super draggingEntered:sender]; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) return NSDragOperationNone; - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + [self addDropData:sender]; + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if ([sender draggingSource] != nil) { + // modifier flags might have changed, update it here since we don't send any input events. + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + // when the source is from another application the above technique will not work. + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + // send the drag enter event to the widget. + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + QApplication::sendEvent(qwidget, &qDEEvent); + + if (!qDEEvent.isAccepted()) { + // The enter event was not accepted. We mark this by removing + // the drop data so we don't send subsequent drag move events: + [self removeDropData]; + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } else { + // Send a drag move event immediately after a drag enter event (as per documentation). + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + qDMEvent.setDropAction(qDEEvent.dropAction()); + qDMEvent.accept(); // accept by default, since enter event was accepted. + QApplication::sendEvent(qwidget, &qDMEvent); + + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Since we accepted the drag enter event, the widget expects + // future drage move events. + nsActions = NSDragOperationNone; + // Save as ignored in the answer rect. + qDMEvent.setDropAction(Qt::IgnoreAction); + } else { + nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); + } + + QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); + [self changeDraggingCursor:nsActions]; + return nsActions; + } } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) - return [super draggingUpdated:sender]; - - if (target == *currentDragTarget()) { - // The drag continues to move over the widget that we have sendt - // a draggingEntered message to. So just update the view: - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender]; - } else { - // The widget under the mouse has changed. - // So we need to fake enter/leave events: - if (*currentDragTarget()) - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) { - *currentDragTarget() = 0; - return NSDragOperationNone; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + if (!qwidget) + return [super draggingEntered:sender]; + + // First, check if the widget under the mouse has changed since the + // last drag move events. If so, we need to change target, and dispatch + // syntetic drag enter/leave events: + if (qwidget != *currentDragTarget()) { + if (*currentDragTarget() && dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(*currentDragTarget(), &de); + [self removeDropData]; } - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + return [self draggingEntered:sender]; } + + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + // If we have no drop data (which will be assigned inside draggingEntered), it means + // that the current drag target did not accept the enter event. If so, we ignore + // subsequent move events as well: + if (dropData == 0) { + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } + + // If the mouse is still within the accepted rect (provided by + // the application on a previous event), we follow the optimization + // and just return the answer given at that point: + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + if (qt_mac_mouse_inside_answer_rect(localPoint) + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { + NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); + [self changeDraggingCursor:operation]; + return operation; + } + + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + // Update modifiers: + if ([sender draggingSource] != nil) { + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + // Insert the same drop action on the event according to + // what the application told us it should be on the previous event: + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) + qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); + + // Now, end the drag move event to the widget: + qDMEvent.accept(); + QApplication::sendEvent(qwidget, &qDMEvent); + + NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Ignore this event (we will still receive further + // notifications), save as ignored in the answer rect: + operation = NSDragOperationNone; + qDMEvent.setDropAction(Qt::IgnoreAction); + } + + qt_mac_copy_answer_rect(qDMEvent); + [self changeDraggingCursor:operation]; + + return operation; } -- (void)draggingExited:(id < NSDraggingInfo >)sender +- (void)draggingExited:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) return [super draggingExited:sender]; - if (*currentDragTarget()) { - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - *currentDragTarget() = 0; + if (dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(qwidget, &de); + [self removeDropData]; } + + // Clean-up: + [self removeDropData]; + *currentDragTarget() = 0; + [self changeDraggingCursor:NSDragOperationEvery]; } -- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) - return [super performDragOperation:sender]; - - BOOL dropResult = NO; - if (*currentDragTarget()) { - dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender]; - *currentDragTarget() = 0; - } - return dropResult; + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) + return NO; + + *currentDragTarget() = 0; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + [self addDropData:sender]; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QMimeData *mimeData = dropData; + + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->target = qwidget; + + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDropEvent de(localPoint, qtAllowed, mimeData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(qwidget, &de); + + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); + + return de.isAccepted(); } - (void)displayIfNeeded @@ -394,8 +577,8 @@ static bool firstDrawingInvocation = true; - (void)drawRectSpecial:(NSRect)rect { // Call the original drawing method. - [self drawRectOriginal:rect]; - NSWindow *window = [self window]; + [id(self) drawRectOriginal:rect]; + NSWindow *window = [id(self) window]; NSToolbar *toolbar = [window toolbar]; if(!toolbar) { // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa. @@ -415,3 +598,11 @@ static bool firstDrawingInvocation = true; } } } + +- (void)drawRectOriginal:(NSRect)rect +{ + Q_UNUSED(rect) + // This method implementation is here to silenct the compiler. + // See drawRectSpecial for information. +} + diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 5ea893c626..f0ae886744 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -49,7 +49,6 @@ #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qdnd_p.h> #include <private/qmacinputcontext_p.h> -#include <private/qmultitouch_mac_p.h> #include <private/qevent_p.h> #include <private/qbackingstore_p.h> @@ -76,70 +75,15 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(DnDParams, qMacDnDParams); - -extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm +extern void qt_mac_update_cursor(); // qcursor_mac.mm extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); - -struct dndenum_mapper -{ - NSDragOperation mac_code; - Qt::DropAction qt_code; - bool Qt2Mac; -}; - -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 } -}; - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} +extern QWidget *mac_mouse_grabber; +extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp static QColor colorFrom(NSColor *color) { @@ -185,6 +129,7 @@ extern "C" { extern NSString *NSTextInputReplacementRangeAttributeName; } +//#define ALIEN_DEBUG 1 #ifdef ALIEN_DEBUG static int qCocoaViewCount = 0; #endif @@ -202,12 +147,14 @@ static int qCocoaViewCount = 0; #ifdef ALIEN_DEBUG ++qCocoaViewCount; - qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount; #endif composing = false; sendKeyEvents = true; fromKeyDownEvent = false; + alienTouchCount = 0; + [self setHidden:YES]; return self; } @@ -222,246 +169,15 @@ static int qCocoaViewCount = 0; object:self]; } -- (void)resetCursorRects -{ - // [NSView addCursorRect] is slow, so bail out early if we can: - if (NSIsEmptyRect([self visibleRect])) - return; - - QWidget *cursorWidget = qwidget; - - if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents)) - cursorWidget = QApplication::widgetAt(qwidget->mapToGlobal(qwidget->rect().center())); - - if (cursorWidget == 0) - return; - - if (!cursorWidget->testAttribute(Qt::WA_SetCursor)) { - [super resetCursorRects]; - return; - } - - QRegion mask = qt_widget_private(cursorWidget)->extra->mask; - NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor())); - // The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask. - if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) { - [self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor]; - } else { - const QVector<QRect> &rects = mask.rects(); - for (int i = 0; i < rects.size(); ++i) { - const QRect &rect = rects.at(i); - [self addCursorRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()) cursor:nscursor]; - } - } -} - -- (void)removeDropData -{ - if (dropData) { - delete dropData; - dropData = 0; - } -} - -- (void)addDropData:(id <NSDraggingInfo>)sender -{ - [self removeDropData]; - CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; - dropData = new QCocoaDropData(dropPasteboard); -} - -- (void)changeDraggingCursor:(NSDragOperation)newOperation -{ - static SEL action = nil; - static bool operationSupported = false; - if (action == nil) { - action = NSSelectorFromString(@"operationNotAllowedCursor"); - if ([NSCursor respondsToSelector:action]) { - operationSupported = true; - } - } - if (operationSupported) { - NSCursor *notAllowedCursor = [NSCursor performSelector:action]; - bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); - if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { - [notAllowedCursor push]; - } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { - [notAllowedCursor pop]; - } - - } -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = [sender draggingSequenceNumber]; - [self addDropData:sender]; - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrag(localPoint.x, localPoint.y); - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - // send the drag enter event to the widget. - QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - QApplication::sendEvent(qwidget, &qDEEvent); - if (!qDEEvent.isAccepted()) { - // widget is not interested in this drag, so ignore this drop data. - [self removeDropData]; - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } else { - // save the mouse position, used by draggingExited handler. - DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - dndParams->activeDragEnterPos = windowPoint; - // send a drag move event immediately after a drag enter event (as per documentation). - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - qDMEvent.setDropAction(qDEEvent.dropAction()); - qDMEvent.accept(); // accept by default, since enter event was accepted. - QApplication::sendEvent(qwidget, &qDMEvent); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // since we accepted the drag enter event, the widget expects - // future drage move events. - // ### check if we need to treat this like the drag enter event. - nsActions = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } else { - nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); - } - QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); - [self changeDraggingCursor:nsActions]; - return nsActions; - } - } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - // in cases like QFocusFrame, the view under the mouse might - // not have received the drag enter. Generate a synthetic - // drag enter event for that view. - if (dragEnterSequence != [sender draggingSequenceNumber]) - [self draggingEntered:sender]; - // drag enter event was rejected, so ignore the move event. - if (dropData == 0) { - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } - // return last value, if we are still in the answerRect. - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - QPoint posDrag(localPoint.x, localPoint.y); - if (qt_mac_mouse_inside_answer_rect(posDrag) - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { - NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); - [self changeDraggingCursor:operation]; - return operation; - } - // send drag move event to the widget - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) - qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); - qDMEvent.accept(); - QApplication::sendEvent(qwidget, &qDMEvent); - - NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // ignore this event (we will still receive further notifications) - operation = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } - qt_mac_copy_answer_rect(qDMEvent); - [self changeDraggingCursor:operation]; - return operation; -} - -- (void)draggingExited:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - Q_UNUSED(sender); - dragEnterSequence = -1; - // drag enter event was rejected, so ignore the move event. - if (dropData) { - QDragLeaveEvent de; - QApplication::sendEvent(qwidget, &de); - [self removeDropData]; - } - [self changeDraggingCursor:NSDragOperationEvery]; - -} - -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = -1; - [self addDropData:sender]; - - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrop(localPoint.x, localPoint.y); - - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - // send the drop event to the widget. - QDropEvent de(posDrop, qtAllowed, mimeData, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->target = qwidget; - QApplication::sendEvent(qwidget, &de); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); - if (!de.isAccepted()) - return NO; - else - return YES; -} - - (void)dealloc { + QMacCocoaAutoReleasePool pool; delete composingText; [[NSNotificationCenter defaultCenter] removeObserver:self]; #ifdef ALIEN_DEBUG --qCocoaViewCount; - qDebug() << "qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount; #endif [super dealloc]; @@ -529,9 +245,8 @@ static int qCocoaViewCount = 0; { if (!qwidget) return; - - // We use a different graphics system. if (QApplicationPrivate::graphicsSystem() != 0 && !qwidgetprivate->isInUnifiedToolbar) { + // INVARIANT: We use a different graphics system. // Qt handles the painting occuring inside the window. // Cocoa also keeps track of all widgets as NSView and therefore might @@ -558,7 +273,7 @@ static int qCocoaViewCount = 0; qwidgetprivate->hd = cg; // We steal the CGContext for flushing in the unified toolbar with the raster engine. - if (QApplicationPrivate::graphicsSystem() != 0 && qwidgetprivate->isInUnifiedToolbar) { + if (QApplicationPrivate::graphicsSystem() != 0 && qwidgetprivate->isInUnifiedToolbar && qwidgetprivate->unifiedSurface) { qwidgetprivate->cgContext = cg; qwidgetprivate->hasOwnContext = true; qwidgetprivate->unifiedSurface->flush(qwidget, qwidgetprivate->ut_rg, qwidgetprivate->ut_pt); @@ -574,13 +289,13 @@ static int qCocoaViewCount = 0; const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height); QRegion qrgn; - const NSRect *rects; - NSInteger count; - [self getRectsBeingDrawn:&rects count:&count]; - for (int i = 0; i < count; ++i) { - QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); - qrgn += tmpRect; - } + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + for (int i = 0; i < count; ++i) { + QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); + qrgn += tmpRect; + } if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) { const QRegion &parentMask = qwidget->window()->mask(); @@ -610,38 +325,27 @@ static int qCocoaViewCount = 0; } if (qwidget->isWindow() && !qwidgetprivate->isOpaque - && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { + && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { CGContextClearRect(cg, NSRectToCGRect(aRect)); } - // Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this - // is the case. This makes sure child widgets are drawn as well, Cocoa does not know about - // those and wont send them drawRect calls. - if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) { - if ((engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground) - && (qwidget->isWindow() || qwidget->autoFillBackground())) - || qwidget->testAttribute(Qt::WA_TintedBackground) - || qwidget->testAttribute(Qt::WA_StyledBackground)) { -#ifdef DEBUG_WIDGET_PAINT - if(doDebug) - qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(), - qwidget->objectName().local8Bit().data()); -#endif - QPainter p(qwidget); - qwidgetprivate->paintBackground(&p, qrgn, - qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0); - p.end(); - } - QPaintEvent e(qrgn); -#ifdef QT3_SUPPORT - e.setErased(true); -#endif - qt_sendSpontaneousEvent(qwidget, &e); - } else { - qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this - QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); - qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0); - } + qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); + QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); + + // We specify that we want to draw the widget itself, and + // all its children recursive. But we skip native children, because + // they will receive drawRect calls by themselves as needed: + int flags = QWidgetPrivate::DrawPaintOnScreen + | QWidgetPrivate::DrawRecursive + | QWidgetPrivate::DontDrawNativeChildren; + + if (qwidget->isWindow()) + flags |= QWidgetPrivate::DrawAsRoot; + + // Start to draw: + qt_mac_clearDirtyOnWidgetInsideDrawWidget = true; + qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0); + qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; if (!redirectionOffset.isNull()) QPainter::restoreRedirected(qwidget); @@ -650,7 +354,7 @@ static int qCocoaViewCount = 0; qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive()) qWarning("QWidget: It is dangerous to leave painters active on a" - " widget outside of the PaintEvent"); + " widget outside of the PaintEvent"); } qwidgetprivate->hd = 0; CGContextRestoreGState(cg); @@ -659,11 +363,13 @@ static int qCocoaViewCount = 0; - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { - if (!qwidget) + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) return NO; - Q_UNUSED(theEvent); - return !qwidget->testAttribute(Qt::WA_MacNoClickThrough); + return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough); } - (NSView *)hitTest:(NSPoint)aPoint @@ -714,161 +420,119 @@ static int qCocoaViewCount = 0; - (void)mouseEntered:(NSEvent *)event { - if (!qwidget) - return; - if (qwidgetprivate->data.in_destructor) - return; - - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QEvent enterEvent(QEvent::Enter); - NSPoint windowPoint = [event locationInWindow]; - NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint]; - NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; - QApplication::sendEvent(qwidget, &enterEvent); - qt_mouseover = qwidget; - - // Update cursor icon: - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - - // Send mouse move and hover events as well: - if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) { - // This mouse move event should be sendt, even when mouse - // tracking is switched off (to trigger tooltips): - NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved - location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp] - windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber] - clickCount:0 pressure:0]; - qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton); - - if (qwidget->testAttribute(Qt::WA_Hover)) { - QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); - } - } - } + // Cocoa will not send a move event on mouseEnter. But since + // Qt expect this, we fake one now. See also mouseExited below + // for info about enter/leave event handling + NSEvent *nsmoveEvent = [NSEvent + mouseEventWithType:NSMouseMoved + location:[[self window] mouseLocationOutsideOfEventStream] + modifierFlags: [event modifierFlags] + timestamp: [event timestamp] + windowNumber: [event windowNumber] + context: [event context] + eventNumber: [event eventNumber] + clickCount: 0 + pressure: 0]; + + // Important: Cocoa sends us mouseEnter on all views under the mouse + // and not just the one on top. Therefore, to we cannot use qwidget + // as native widget for this case. Instead, we let qt_mac_handleMouseEvent + // resolve it (last argument set to 0): + qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0); } - (void)mouseExited:(NSEvent *)event { - if (!qwidget) - return; - - QEvent leaveEvent(QEvent::Leave); - NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QApplication::sendEvent(qwidget, &leaveEvent); - - // ### Think about if it is necessary to update the cursor, should only be for a few cases. - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - if (qwidget->testAttribute(Qt::WA_Hover) - && (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) { - QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), - qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); + // Note: normal enter/leave handling is done from within mouseMove. This handler + // catches the case when the mouse moves out of the window (which mouseMove do not). + // Updating the mouse cursor follows the same logic as enter/leave. And we update + // neither if a grab exists (even if the grab points to this widget, it seems, ref X11) + Q_UNUSED(event); + if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) { + qt_mac_update_cursor(); + // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still + // reports a target, it means that either there is a grab involved, or the mouse + // hovered over another window in the application. In both cases, move events will + // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave. + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse); + + if (widgetUnderMouse == 0) { + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; } } } - (void)flagsChanged:(NSEvent *)theEvent { - if (!qwidget) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return; - QWidget *widgetToGetKey = qwidget; - - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != qwidget->window()) - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; qt_dispatchModifiersChanged(theEvent, widgetToGetKey); [super flagsChanged:theEvent]; } - (void)mouseMoved:(NSEvent *)theEvent { - if (!qwidget) - return; - - // We always enable mouse tracking for all QCocoaView-s. In cases where we have - // child views, we will receive mouseMoved for both parent & the child (if - // mouse is over the child). We need to ignore the parent mouseMoved in such - // cases. - NSPoint windowPoint = [theEvent locationInWindow]; - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView == self) { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); - } + // Important: this method will only be called when the view's window is _not_ inside + // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event + // before it ends up here. So, this method is added for supporting QMacNativeWidget. + // TODO: Cocoa send move events to all views under the mouse. So make sure we only + // handle the event for the widget on top when using QMacNativeWidget. + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)mouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget); // Don't call super here. This prevents us from getting the mouseUp event, // which we need to send even if the mouseDown event was not accepted. // (this is standard Qt behavior.) } - - (void)mouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget); } - (void)rightMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget); } - (void)rightMouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget); } - (void)otherMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget); } - (void)otherMouseUp:(NSEvent *)theEvent { Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget); } - (void)mouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)rightMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)otherMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)scrollWheel:(NSEvent *)theEvent @@ -879,21 +543,14 @@ static int qCocoaViewCount = 0; [currentIManager handleMouseEvent:theEvent]; } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint qlocal = QPoint(localPoint.x, localPoint.y); - QPoint qglobal = QPoint(globalPoint.x, flipYCoordinate(globalPoint.y)); Qt::MouseButtons buttons = QApplication::mouseButtons(); - bool wheelOK = false; Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - QWidget *widgetToGetMouse = qwidget; - // if popup is open it should get wheel events if the cursor is over the popup, - // otherwise the event should be ignored. - if (QWidget *popup = qAppInstance()->activePopupWidget()) { - if (!popup->geometry().contains(qglobal)) - return; - } + + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) + return; int deltaX = 0; int deltaY = 0; @@ -922,46 +579,30 @@ static int qCocoaViewCount = 0; } #ifndef QT_NO_WHEELEVENT + // ### Qt 5: Send one QWheelEvent with dx, dy and dz + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::initDelayedScroll(); + if (deltaX != 0) { QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaX, buttons, keyMods, Qt::Horizontal); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaY) { + if (deltaY != 0) { QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaY, buttons, keyMods, Qt::Vertical); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaZ) { + if (deltaZ != 0) { // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to // try to be ahead of the pack, I'm adding this extra value. QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaZ, buttons, keyMods, (Qt::Orientation)3); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::performDelayedScroll(); #endif //QT_NO_WHEELEVENT } @@ -976,35 +617,14 @@ static int qCocoaViewCount = 0; [super tabletPoint:tabletEvent]; } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -- (void)touchesBeganWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesMovedWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesEndedWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesCancelledWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - - (void)magnifyWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1013,13 +633,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = [event magnification]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)rotateWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1028,13 +653,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = -[event rotation]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)swipeWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1050,13 +680,18 @@ static int qCocoaViewCount = 0; qNGEvent.angle = 90.0f; else if ([event deltaY] == -1) qNGEvent.angle = 270.0f; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)beginGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1064,13 +699,18 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)endGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1078,9 +718,9 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); -#endif // QT_NO_GESTURES + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); } +#endif // QT_NO_GESTURES - (void)frameDidChange:(NSNotification *)note { @@ -1123,16 +763,32 @@ static int qCocoaViewCount = 0; { if (!qwidget) return NO; - // disabled widget shouldn't get focus even if it's a window. + + // Disabled widget shouldn't get focus even if it's a window. // hence disabled windows will not get any key or mouse events. if (!qwidget->isEnabled()) return NO; - // Before accepting the focus for a window, we check that - // the focusWidget (if any) is not contained in the same window. - if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded - && (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) { - return YES; // Always do it, so that windows can accept key press events. + + if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) { + QWidget *focusWidget = qApp->focusWidget(); + if (!focusWidget) { + // There is no focus widget, but we still want to receive key events + // for shortcut handling etc. So we accept first responer for the + // content view as a last resort: + return YES; + } + if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) { + // The current focus widget is alien, and hence, cannot get acceptsFirstResponder + // calls. Since the focus widget is a child of qwidget, we let this view say YES: + return YES; + } + if (focusWidget->window() != qwidget) { + // The current focus widget is in another window. Since cocoa + // suggest that this window should be key now, we accept: + return YES; + } } + return qwidget->focusPolicy() != Qt::NoFocus; } @@ -1140,10 +796,13 @@ static int qCocoaViewCount = 0; { if (!qwidget) return NO; + // Seems like the following test only triggers if this // view is inside a QMacNativeWidget: - if (qwidget == QApplication::focusWidget()) - qwidget->clearFocus(); +// if (QWidget *fw = QApplication::focusWidget()) { +// if (qwidget == fw || qwidget == fw->nativeParentWidget()) +// fw->clearFocus(); +// } return YES; } @@ -1172,11 +831,11 @@ static int qCocoaViewCount = 0; { Q_UNUSED(anImage); Q_UNUSED(aPoint); - qMacDnDParams()->performedAction = operation; + macCurrentDnDParameters()->performedAction = operation; if (QDragManager::self()->object && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) { - qMacDnDParams()->performedAction = - qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); + macCurrentDnDParameters()->performedAction = + qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); } } @@ -1191,51 +850,37 @@ static int qCocoaViewCount = 0; qwidgetprivate = 0; } -- (BOOL)qt_leftButtonIsRightButton -{ - return leftButtonIsRightButton; -} - -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped -{ - leftButtonIsRightButton = isSwapped; -} - -+ (DnDParams*)currentMouseEvent -{ - return qMacDnDParams(); -} - - (void)keyDown:(NSEvent *)theEvent { - sendKeyEvents = true; - - QWidget *widgetToGetKey = qwidget; + if (!qwidget) + return; + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; - QWidget *popup = qAppInstance()->activePopupWidget(); - bool sendToPopup = false; - if (popup && popup != qwidget->window()) { - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; - sendToPopup = true; - } + sendKeyEvents = true; if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled) && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) { fromKeyDownEvent = true; - [qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; + [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; fromKeyDownEvent = false; } + if (sendKeyEvents && !composing) { - bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey); - if (!keyOK && !sendToPopup) { - // find the first responder that is not created by Qt and forward - // the event to it (for example if Qt widget is embedded into native). + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyDown:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyDown:theEvent]; + } } } } @@ -1245,12 +890,21 @@ static int qCocoaViewCount = 0; - (void)keyUp:(NSEvent *)theEvent { if (sendKeyEvents) { - bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget); - if (!keyOK) { + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyUp:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyUp:theEvent]; + } } } } @@ -1298,13 +952,14 @@ static int qCocoaViewCount = 0; // When entering characters through Character Viewer or Keyboard Viewer, the text is passed // through this insertText method. Since we dont receive a keyDown Event in such cases, the // composing flag will be false. - if (([aString length] && composing) || !fromKeyDownEvent) { + if (([aString length] && composing) || !fromKeyDownEvent) { // Send the commit string to the widget. composing = false; sendKeyEvents = false; QInputMethodEvent e; e.setCommitString(commitText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } else { // The key sequence "`q" on a French Keyboard will generate two calls to insertText before // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept @@ -1368,8 +1023,11 @@ static int qCocoaViewCount = 0; 0, composingLength, format); } *composingText = qtText; + QInputMethodEvent e(qtText, attrs); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + if (!composingLength) composing = false; } @@ -1379,7 +1037,8 @@ static int qCocoaViewCount = 0; if (composing) { QInputMethodEvent e; e.setCommitString(*composingText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } composingText->clear(); composing = false; @@ -1452,8 +1111,12 @@ static int qCocoaViewCount = 0; { Q_UNUSED(theRange); // The returned rect is always based on the internal cursor. - QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect()); - QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft()))); + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return NSZeroRect; + + QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect()); + QPoint mp(widgetToGetKey->mapToGlobal(QPoint(mr.bottomLeft()))); NSRect rect ; rect.origin.x = mp.x(); rect.origin.y = flipYCoordinate(mp.y()); @@ -1471,10 +1134,11 @@ static int qCocoaViewCount = 0; - (NSArray*) validAttributesForMarkedText { - if (qwidget == 0) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return nil; - if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled)) + if (!widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)) return nil; // Not sure if that's correct, but it's saves a malloc. // Support only underline color/style. @@ -1488,7 +1152,7 @@ void QMacInputContext::reset() { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); @@ -1505,7 +1169,7 @@ bool QMacInputContext::isComposing() const { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing]; } @@ -1598,38 +1262,45 @@ Qt::DropAction QDragManager::drag(QDrag *o) } else { hotspot = dragPrivate()->hotspot; } - // convert the image to NSImage. + + // Convert the image to NSImage: NSImage *image = (NSImage *)qt_mac_create_nsimage(pix); [image retain]; - DnDParams dndParams = *[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - // save supported actions - [dndParams.view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; - NSPoint imageLoc = {dndParams.localPoint.x - hotspot.x(), - dndParams.localPoint.y + pix.height() - hotspot.y()}; + + DnDParams *dndParams = macCurrentDnDParameters(); + QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view); + + // Save supported actions: + [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; + QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint); + NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()}; NSSize mouseOffset = {0.0, 0.0}; NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; dragPrivate()->executed_action = Qt::ActionMask; - // do the drag - [dndParams.view retain]; - [dndParams.view dragImage:image - at:imageLoc - offset:mouseOffset - event:dndParams.theEvent - pasteboard:pboard - source:dndParams.view - slideBack:YES]; - // reset the implicit grab widget when drag ends because we will not - // receive the mouse release event when DND is active. + + // Execute the drag: + [theView retain]; + [theView dragImage:image + at:imageLoc + offset:mouseOffset + event:dndParams->theEvent + pasteboard:pboard + source:theView + slideBack:YES]; + + // Reset the implicit grab widget when drag ends because we will not + // receive the mouse release event when DND is active: qt_button_down = 0; - [dndParams.view release]; + [theView release]; [image release]; if (dragPrivate()) dragPrivate()->executed_action = Qt::IgnoreAction; object = 0; - Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction)); - // do post drag processing, if required. - if(performedAction != Qt::IgnoreAction) { - // check if the receiver points us to a file location. + Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction)); + + // Do post drag processing, if required. + if (performedAction != Qt::IgnoreAction) { + // Check if the receiver points us to a file location. // if so, we need to do the file copy/move ourselves. QCFType<CFURLRef> pasteLocation = 0; PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation); @@ -1646,6 +1317,8 @@ Qt::DropAction QDragManager::drag(QDrag *o) } } } + + // Clean-up: o->setMimeData(0); o->deleteLater(); return performedAction; diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index 8d57f951ef..cc79b6705b 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -58,57 +58,30 @@ QT_FORWARD_DECLARE_CLASS(QWidgetPrivate); QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QEvent); -QT_FORWARD_DECLARE_CLASS(QCocoaDropData); QT_FORWARD_DECLARE_CLASS(QString); QT_FORWARD_DECLARE_CLASS(QStringList); -QT_BEGIN_NAMESPACE -struct DnDParams -{ - QT_MANGLE_NAMESPACE(QCocoaView) *view; - NSEvent *theEvent; - NSPoint localPoint; - NSDragOperation performedAction; - NSPoint activeDragEnterPos; -}; - -QT_END_NAMESPACE - -QT_FORWARD_DECLARE_STRUCT(DnDParams); - Q_GUI_EXPORT @interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> { QWidget *qwidget; QWidgetPrivate *qwidgetprivate; - bool leftButtonIsRightButton; - QCocoaDropData *dropData; NSDragOperation supportedActions; bool composing; int composingLength; bool sendKeyEvents; bool fromKeyDownEvent; QString *composingText; - NSInteger dragEnterSequence; + @public int alienTouchCount; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void)frameDidChange:(NSNotification *)note; -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; -- (void)draggingExited:(id < NSDraggingInfo >)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -- (void)removeDropData; -- (void)addDropData:(id <NSDraggingInfo>)sender; - (void)setSupportedActions:(NSDragOperation)actions; - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; - (BOOL)isComposing; - (QWidget *)qt_qwidget; - (void) qt_clearQWidget; -- (BOOL)qt_leftButtonIsRightButton; -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped; -- (void)changeDraggingCursor:(NSDragOperation)newOperation; -+ (DnDParams*)currentMouseEvent; @end #endif diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm index 28f09add74..6e5023aaca 100644 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ b/src/gui/kernel/qcocoawindow_mac.mm @@ -47,6 +47,8 @@ #import <private/qt_cocoa_helpers_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> +#import <private/qdnd_p.h> +#import <private/qmultitouch_mac_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h index 81e0be1f62..e6b50f5a15 100644 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ b/src/gui/kernel/qcocoawindow_mac_p.h @@ -50,17 +50,20 @@ // We mean it. // +#ifndef QCOCOAWINDOW_MAC_P +#define QCOCOAWINDOW_MAC_P + #ifdef QT_MAC_USE_COCOA #include "qmacdefines_mac.h" #import <Cocoa/Cocoa.h> #include <private/qapplication_p.h> #include <private/qbackingstore_p.h> - enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); @interface NSWindow (QtCoverForHackWithCategory) + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; @@ -72,13 +75,16 @@ QT_FORWARD_DECLARE_CLASS(QStringList); @end @interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h index d90375da36..c993763634 100644 --- a/src/gui/kernel/qcursor.h +++ b/src/gui/kernel/qcursor.h @@ -77,7 +77,7 @@ class QBitmap; class QPixmap; #if defined(Q_WS_MAC) -void qt_mac_set_cursor(const QCursor *c, const QPoint &p); +void qt_mac_set_cursor(const QCursor *c); #endif #if defined(Q_OS_SYMBIAN) extern void qt_symbian_show_pointer_sprite(); @@ -141,7 +141,8 @@ private: QCursorData *d; #if defined(Q_WS_MAC) friend void *qt_mac_nsCursorForQCursor(const QCursor &c); - friend void qt_mac_set_cursor(const QCursor *c, const QPoint &p); + friend void qt_mac_set_cursor(const QCursor *c); + friend void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); #endif #if defined(Q_OS_SYMBIAN) friend void qt_symbian_show_pointer_sprite(); diff --git a/src/gui/kernel/qcursor_mac.mm b/src/gui/kernel/qcursor_mac.mm index 1a82a68d98..0d57b85ead 100644 --- a/src/gui/kernel/qcursor_mac.mm +++ b/src/gui/kernel/qcursor_mac.mm @@ -50,6 +50,7 @@ #include <AppKit/NSCursor.h> #include <qpainter.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qapplication_p.h> QT_BEGIN_NAMESPACE @@ -60,6 +61,7 @@ extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp /***************************************************************************** Internal QCursorData class @@ -95,18 +97,19 @@ protected: } }; -void *qt_mac_nsCursorForQCursor(const QCursor &c) +inline void *qt_mac_nsCursorForQCursor(const QCursor &c) { c.d->update(); return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease]; } static QCursorData *currentCursor = 0; //current cursor -void qt_mac_set_cursor(const QCursor *c, const QPoint &) + +void qt_mac_set_cursor(const QCursor *c) { #ifdef QT_MAC_USE_COCOA - Q_UNUSED(c); - return; + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set]; #else if (!c) { currentCursor = 0; @@ -128,35 +131,122 @@ void qt_mac_set_cursor(const QCursor *c, const QPoint &) c->d->curs.tc.anim->start(c->d->curs.tc.curs); } } + currentCursor = c->d; #endif } -void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +static QPointer<QWidget> lastWidgetUnderMouse = 0; +static QPointer<QWidget> lastMouseCursorWidget = 0; +static bool qt_button_down_on_prev_call = false; +static QCursor *grabCursor = 0; + +void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) { -#ifdef QT_MAC_USE_COCOA - Q_UNUSED(globalPos); - return; -#else QCursor cursor(Qt::ArrowCursor); + if (qt_button_down) { + // The widget that is currently pressed + // grabs the mouse cursor: + widgetUnderMouse = qt_button_down; + qt_button_down_on_prev_call = true; + } else if (qt_button_down_on_prev_call) { + // Grab has been released, so do + // a full check: + qt_button_down_on_prev_call = false; + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + } + if (QApplication::overrideCursor()) { cursor = *QApplication::overrideCursor(); - } else { - for(QWidget *w = QApplication::widgetAt(globalPos); w; w = w->parentWidget()) { - if(w->testAttribute(Qt::WA_SetCursor)) { - cursor = w->cursor(); - break; + } else if (grabCursor) { + cursor = *grabCursor; + } else if (widgetUnderMouse) { + if (widgetUnderMouse == lastWidgetUnderMouse) { + // Optimization that should hit when the widget under + // the mouse does not change as the mouse moves: + if (lastMouseCursorWidget) + cursor = lastMouseCursorWidget->cursor(); + } else { + QWidget *w = widgetUnderMouse; + for (; w; w = w->parentWidget()) { + if (w->testAttribute(Qt::WA_SetCursor)) { + cursor = w->cursor(); + break; + } + if (w->isWindow()) + break; } + // One final check in case we ran out of parents in the loop: + if (w && !w->testAttribute(Qt::WA_SetCursor)) + w = 0; + + lastWidgetUnderMouse = widgetUnderMouse; + lastMouseCursorWidget = w; } } - qt_mac_set_cursor(&cursor, globalPos); + +#ifdef QT_MAC_USE_COCOA + cursor.d->update(); + NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor); + if ([NSCursor currentCursor] != nsCursor) { + QMacCocoaAutoReleasePool pool; + [nsCursor set]; + } +#else + qt_mac_set_cursor(&cursor); #endif } void qt_mac_update_cursor() { - qt_mac_update_cursor_at_global_pos(QCursor::pos()); + // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse + // except that is clears the optimization cache, and finds the widget + // under mouse itself. Clearing the cache is useful in cases where the + // application has been deactivated/activated etc. + // NB: since we dont have any true native widget, the call to + // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. +#ifdef QT_MAC_USE_COCOA + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + QWidget *widgetUnderMouse = 0; + + if (qt_button_down) { + widgetUnderMouse = qt_button_down; + } else { + QPoint localPoint; + QPoint globalPoint = QCursor::pos(); + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); + } + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); +#else + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); +#endif +} + +void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) +{ + if (grabCursor) { + delete grabCursor; + grabCursor = 0; + } + if (set) { + if (cursor) + grabCursor = new QCursor(*cursor); + else if (lastMouseCursorWidget) + grabCursor = new QCursor(lastMouseCursorWidget->cursor()); + else + grabCursor = new QCursor(Qt::ArrowCursor); + } + qt_mac_update_cursor(); +} + +#ifndef QT_MAC_USE_COCOA +void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +{ + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); } +#endif static int nextCursorId = Qt::BitmapCursor; @@ -427,7 +517,8 @@ void QCursorData::update() break; case Qt::DragCopyCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragCopyCursor]; + if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; break; case Qt::DragMoveCursor: type = QCursorData::TYPE_ThemeCursor; @@ -435,7 +526,8 @@ void QCursorData::update() break; case Qt::DragLinkCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragLinkCursor]; + if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; break; #define QT_USE_APPROXIMATE_CURSORS #ifdef QT_USE_APPROXIMATE_CURSORS diff --git a/src/gui/kernel/qdesktopwidget_qpa_p.h b/src/gui/kernel/qdesktopwidget_qpa_p.h index 8bed09d5fd..47ccca2f14 100644 --- a/src/gui/kernel/qdesktopwidget_qpa_p.h +++ b/src/gui/kernel/qdesktopwidget_qpa_p.h @@ -59,7 +59,13 @@ class QDesktopScreenWidget : public QWidget { Q_OBJECT public: - QDesktopScreenWidget(int screenNumber = -1) { setWindowFlags(Qt::Desktop); setVisible(false); d_func()->screenNumber = screenNumber; } + QDesktopScreenWidget(int screenNumber = -1) + { + setWindowFlags(Qt::Desktop); + setVisible(false); + QTLWExtra *topData = d_func()->topData(); + topData->screenIndex = screenNumber; + } }; class QDesktopWidgetPrivate : public QWidgetPrivate { diff --git a/src/gui/kernel/qdnd_mac.mm b/src/gui/kernel/qdnd_mac.mm index d630faa6c5..3af2ba007c 100644 --- a/src/gui/kernel/qdnd_mac.mm +++ b/src/gui/kernel/qdnd_mac.mm @@ -491,7 +491,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) SetThemeCursor(cursor); } if(found_cursor) { - qt_mac_set_cursor(0, QPoint()); //just use our's + qt_mac_set_cursor(0); //just use our's } else { QCursor cursor(Qt::ArrowCursor); if(qApp && qApp->overrideCursor()) { @@ -504,7 +504,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) } } } - qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v)); + qt_mac_set_cursor(&cursor); } //idle things diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h index 9a55b18ad8..754366637c 100644 --- a/src/gui/kernel/qdnd_p.h +++ b/src/gui/kernel/qdnd_p.h @@ -76,7 +76,7 @@ class QEventLoop; #if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) -class QInternalMimeData : public QMimeData +class Q_GUI_EXPORT QInternalMimeData : public QMimeData { Q_OBJECT public: diff --git a/src/gui/kernel/qkeymapper_x11_p.cpp b/src/gui/kernel/qkeymapper_x11_p.cpp index 1fad4b030d..2dbe1e77a4 100644 --- a/src/gui/kernel/qkeymapper_x11_p.cpp +++ b/src/gui/kernel/qkeymapper_x11_p.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ // This file is auto-generated, do not edit! +// (Generated using util/xkbdatagen) static struct { const char *layout; diff --git a/src/gui/kernel/qmacgesturerecognizer_mac.mm b/src/gui/kernel/qmacgesturerecognizer_mac.mm index 0e432f36f6..6a4f0bb445 100644 --- a/src/gui/kernel/qmacgesturerecognizer_mac.mm +++ b/src/gui/kernel/qmacgesturerecognizer_mac.mm @@ -69,6 +69,7 @@ QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e case QNativeGestureEvent::Swipe: { QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); g->setSwipeAngle(ev->angle); + g->setHotSpot(ev->position); return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; break; } default: @@ -110,6 +111,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setCenterPoint(g->startCenterPoint()); g->setChangeFlags(QPinchGesture::CenterPointChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::Rotate: { g->setLastScaleFactor(g->scaleFactor()); @@ -117,6 +119,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setRotationAngle(g->rotationAngle() + ev->percentage); g->setChangeFlags(QPinchGesture::RotationAngleChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } case QNativeGestureEvent::Zoom: @@ -125,6 +128,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage)); g->setChangeFlags(QPinchGesture::ScaleFactorChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::GestureEnd: return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; @@ -221,6 +225,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * const QPointF posOffset = p - _startPos; g->setLastOffset(g->offset()); g->setOffset(QPointF(posOffset.x(), posOffset.y())); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture; } } else if (_panTimer.isActive()) { @@ -239,6 +244,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * break; // Begin new pan session! _startPos = QCursor::pos(); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } break; } diff --git a/src/gui/kernel/qplatformclipboard_qpa.cpp b/src/gui/kernel/qplatformclipboard_qpa.cpp new file mode 100644 index 0000000000..fff4e19d9d --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformclipboard_qpa.h" + +QT_BEGIN_NAMESPACE + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + +private: + QMimeData* src; +}; + +QClipboardData::QClipboardData() +{ + src = 0; +} + +QClipboardData::~QClipboardData() +{ + delete src; +} + +Q_GLOBAL_STATIC(QClipboardData,q_clipboardData); + +QPlatformClipboard::~QPlatformClipboard() +{ + +} + +const QMimeData *QPlatformClipboard::mimeData(QClipboard::Mode mode) const +{ + //we know its clipboard + Q_UNUSED(mode); + return q_clipboardData()->source(); +} + +void QPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + //we know its clipboard + Q_UNUSED(mode); + q_clipboardData()->setSource(data); +} + +bool QPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformclipboard_qpa.h b/src/gui/kernel/qplatformclipboard_qpa.h new file mode 100644 index 0000000000..3f7bfbbcfe --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMCLIPBOARD_QPA_H +#define QPLATFORMCLIPBOARD_QPA_H + +#include <qplatformdefs.h> + +#include <QtGui/QClipboard> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformClipboard +{ +public: + virtual ~QPlatformClipboard(); + + virtual const QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard ) const; + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif //QPLATFORMCLIPBOARD_QPA_H diff --git a/src/gui/kernel/qplatformglcontext_qpa.cpp b/src/gui/kernel/qplatformglcontext_qpa.cpp index 14bd71815a..1673aed309 100644 --- a/src/gui/kernel/qplatformglcontext_qpa.cpp +++ b/src/gui/kernel/qplatformglcontext_qpa.cpp @@ -138,7 +138,7 @@ void QPlatformGLContext::setDefaultSharedContext(QPlatformGLContext *sharedConte Default shared context is intended to be a globally awailable pointer to a context which can be used for sharing resources when creating new contexts. Its default value is 0; */ -const QPlatformGLContext *QPlatformGLContext::defaultSharedContext() +QPlatformGLContext *QPlatformGLContext::defaultSharedContext() { return QPlatformGLContextPrivate::staticSharedContext; } diff --git a/src/gui/kernel/qplatformglcontext_qpa.h b/src/gui/kernel/qplatformglcontext_qpa.h index a70e04620a..807ed3d66c 100644 --- a/src/gui/kernel/qplatformglcontext_qpa.h +++ b/src/gui/kernel/qplatformglcontext_qpa.h @@ -69,7 +69,7 @@ public: virtual QPlatformWindowFormat platformWindowFormat() const = 0; const static QPlatformGLContext *currentContext(); - const static QPlatformGLContext *defaultSharedContext(); + static QPlatformGLContext *defaultSharedContext(); protected: diff --git a/src/gui/kernel/qplatformintegration_qpa.cpp b/src/gui/kernel/qplatformintegration_qpa.cpp index 9b6e59085d..0cac57d3a6 100644 --- a/src/gui/kernel/qplatformintegration_qpa.cpp +++ b/src/gui/kernel/qplatformintegration_qpa.cpp @@ -42,6 +42,7 @@ #include "qplatformintegration_qpa.h" #include <QtGui/QPlatformFontDatabase> +#include <QtGui/QPlatformClipboard> QT_BEGIN_NAMESPACE @@ -94,6 +95,23 @@ QPlatformFontDatabase *QPlatformIntegration::fontDatabase() const } /*! + Accessor for the platform integrations clipboard. + + Default implementation returns a default QPlatformClipboard. + + \sa QPlatformClipboard + +*/ +QPlatformClipboard *QPlatformIntegration::clipboard() const +{ + static QPlatformClipboard *clipboard = 0; + if (!clipboard) { + clipboard = new QPlatformClipboard; + } + return clipboard; +} + +/*! \class QPlatformIntegration \since 4.8 \internal diff --git a/src/gui/kernel/qplatformintegration_qpa.h b/src/gui/kernel/qplatformintegration_qpa.h index f01b4f4c79..70502455c4 100644 --- a/src/gui/kernel/qplatformintegration_qpa.h +++ b/src/gui/kernel/qplatformintegration_qpa.h @@ -59,6 +59,7 @@ class QBlittable; class QWidget; class QPlatformEventLoopIntegration; class QPlatformFontDatabase; +class QPlatformClipboard; class Q_GUI_EXPORT QPlatformIntegration { @@ -76,8 +77,9 @@ public: virtual bool isVirtualDesktop() { return false; } virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; -//Fontdatabase integration. +//Deeper window system integrations virtual QPlatformFontDatabase *fontDatabase() const; + virtual QPlatformClipboard *clipboard() const; // Experimental in mainthread eventloop integration // This should only be used if it is only possible to do window system event processing in diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa.cpp b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp index 9122e1a300..17a130dfc0 100644 --- a/src/gui/kernel/qplatformintegrationfactory_qpa.cpp +++ b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp @@ -52,15 +52,27 @@ QT_BEGIN_NAMESPACE #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QPlatformIntegrationFactoryInterface_iid, QLatin1String("/platforms"), Qt::CaseInsensitive)) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) #endif -QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key) +QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key, const QString &platformPluginPath) { QPlatformIntegration *ret = 0; QStringList paramList = key.split(QLatin1Char(':')); QString platform = paramList.takeFirst().toLower(); #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // Try loading the plugin from platformPluginPath first: + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + if (QPlatformIntegrationFactoryInterface *factory = + qobject_cast<QPlatformIntegrationFactoryInterface*>(directLoader()->instance(platform))) + ret = factory->create(key, paramList); + + if (ret) + return ret; + } if (QPlatformIntegrationFactoryInterface *factory = qobject_cast<QPlatformIntegrationFactoryInterface*>(loader()->instance(platform))) ret = factory->create(platform, paramList); #endif @@ -74,10 +86,19 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key) \sa create() */ -QStringList QPlatformIntegrationFactory::keys() +QStringList QPlatformIntegrationFactory::keys(const QString &platformPluginPath) { #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) - QStringList list = loader()->keys(); + QStringList list; + + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + foreach (const QString &key, directLoader()->keys()) { + list += key + QString(QLatin1String(" (from %1)")).arg(platformPluginPath); + } + } + + list += loader()->keys(); #else QStringList list; #endif diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa_p.h b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h index ba02d2c6b6..77e1da13f0 100644 --- a/src/gui/kernel/qplatformintegrationfactory_qpa_p.h +++ b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h @@ -66,8 +66,8 @@ class QPlatformIntegration; class QPlatformIntegrationFactory { public: - static QStringList keys(); - static QPlatformIntegration *create(const QString&); + static QStringList keys(const QString &platformPluginPath = QString()); + static QPlatformIntegration *create(const QString &key, const QString &platformPluginPath = QString()); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreen_qpa.cpp b/src/gui/kernel/qplatformscreen_qpa.cpp index 5e80ba8d06..118835ff99 100644 --- a/src/gui/kernel/qplatformscreen_qpa.cpp +++ b/src/gui/kernel/qplatformscreen_qpa.cpp @@ -41,7 +41,11 @@ #include "qplatformscreen_qpa.h" #include <QtGui/qapplication.h> +#include <QtGui/private/qapplication_p.h> #include <QtGui/qdesktopwidget.h> +#include <QtGui/qplatformintegration_qpa.h> +#include <QtGui/qwidget.h> +#include <QtGui/private/qwidget_p.h> /*! Return the given top level widget for a given position. @@ -77,6 +81,17 @@ QSize QPlatformScreen::physicalSize() const return QSize(width,height); } +Q_GUI_EXPORT extern QWidgetPrivate *qt_widget_private(QWidget *widget); +QPlatformScreen * QPlatformScreen::platformScreenForWidget(const QWidget *widget) +{ + QWidget *window = widget->window(); + QWidgetPrivate *windowPrivate = qt_widget_private(window); + QTLWExtra * topData = windowPrivate->topData(); + QPlatformIntegration *integration = + QApplicationPrivate::platformIntegration(); + return integration->screens()[topData->screenIndex]; +} + /*! \class QPlatformScreen \since 4.8 diff --git a/src/gui/kernel/qplatformscreen_qpa.h b/src/gui/kernel/qplatformscreen_qpa.h index 908048950e..1f52764cac 100644 --- a/src/gui/kernel/qplatformscreen_qpa.h +++ b/src/gui/kernel/qplatformscreen_qpa.h @@ -73,6 +73,10 @@ public: //jl: should setDirty be removed. virtual void setDirty(const QRect &) {} virtual QWidget *topLevelAt(const QPoint &point) const; + + //jl: should this function be in QPlatformIntegration + //jl: maybe screenForWidget is a better name? + static QPlatformScreen *platformScreenForWidget(const QWidget *widget); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformwindow_qpa.cpp b/src/gui/kernel/qplatformwindow_qpa.cpp index 29eaa8277c..b6b6693d77 100644 --- a/src/gui/kernel/qplatformwindow_qpa.cpp +++ b/src/gui/kernel/qplatformwindow_qpa.cpp @@ -41,6 +41,7 @@ #include "qplatformwindow_qpa.h" +#include <QtGui/qwindowsysteminterface_qpa.h> #include <QtGui/qwidget.h> class QPlatformWindowPrivate @@ -171,6 +172,24 @@ void QPlatformWindow::setOpacity(qreal level) } /*! + Reimplement to let Qt be able to request activation/focus for a window + + Some window systems will probably not have callbacks for this functionality, + and then calling QWindowSystemInterface::handleWindowActivated(QWidget *w) + would be sufficient. + + If the window system has some event handling/callbacks then call + QWindowSystemInterface::handleWindowActivated(QWidget *w) when the window system + gives the notification. + + Default implementation calls QWindowSystem::handleWindowActivated(QWidget *w) +*/ +void QPlatformWindow::requestActivateWindow() +{ + QWindowSystemInterface::handleWindowActivated(widget()); +} + +/*! Reimplement to return the glContext associated with the window. */ QPlatformGLContext *QPlatformWindow::glContext() const diff --git a/src/gui/kernel/qplatformwindow_qpa.h b/src/gui/kernel/qplatformwindow_qpa.h index 4f6fedc80e..abc35d1c25 100644 --- a/src/gui/kernel/qplatformwindow_qpa.h +++ b/src/gui/kernel/qplatformwindow_qpa.h @@ -80,6 +80,7 @@ public: virtual void lower(); virtual void setOpacity(qreal level); + virtual void requestActivateWindow(); virtual QPlatformGLContext *glContext() const; protected: diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index b2d9ca6e80..ddbf53f71d 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -89,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() @@ -141,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) { @@ -167,6 +177,70 @@ void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) } } } +struct dndenum_mapper +{ + NSDragOperation mac_code; + Qt::DropAction qt_code; + bool Qt2Mac; +}; + +#ifdef QT_MAC_USE_COCOA && __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 ) { @@ -301,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 @@ -517,8 +591,12 @@ static const KeyPair entries[NumEntries] = { { NSNewlineCharacter, Qt::Key_Return }, { NSCarriageReturnCharacter, Qt::Key_Return }, { NSBackTabCharacter, Qt::Key_Backtab }, - { NSDeleteCharacter, Qt::Key_Delete }, { 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 }, @@ -731,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 @@ -743,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]) { @@ -752,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 @@ -788,7 +859,6 @@ void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget #endif } - QPointF flipPoint(const NSPoint &p) { return QPointF(p.x, flipYCoordinate(p.y)); @@ -804,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; @@ -936,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; } } @@ -961,155 +1025,268 @@ 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) +{ + if (QWidget *popup = QApplication::activePopupWidget()) { + QWidget *focusInPopup = popup->focusWidget(); + return focusInPopup ? focusInPopup : popup; + } + + QWidget *widgetToGetKey = qApp->focusWidget(); + if (!widgetToGetKey) + widgetToGetKey = widgetThatReceivedEvent; + + 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. + // If so, set returnGlobalPoint before the call. + NSEvent *event, + QEvent::Type eventType, + QPoint &returnLocalPoint, + QPoint &returnGlobalPoint, + QWidget *nativeWidget, + QWidget **returnWidgetUnderMouse) { -#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); + returnGlobalPoint = flipPoint([NSEvent mouseLocation]).toPoint(); + QWidget *mouseGrabber = QWidget::mouseGrabber(); + bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); + + // Resolve the widget under the mouse: + QWidget *widgetUnderMouse = 0; + if (nativeWidget) { + QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint); + widgetUnderMouse = nativeWidget->childAt(p); + if (!widgetUnderMouse){ + // Cocoa will redirct mouse event to the current + // mouse down widget, which is not what we want for our + // widgetUnderMouse assignment. So we need to check + // if we are actually inside nativeView: + if (nativeWidget->rect().contains(p)) { + widgetUnderMouse = nativeWidget; + } else { + // Ok, fallback to find the widget under mouse ourselves. + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); + } + } + } else { + // Calling QApplication::widgetAt is potentially slow, hence the + // reason we avoid it if we can. So supplying a nativeWidget to + // this function is mostly an optimization. But at the same time, + // calling QApplication::widgetAt fails for QMacNativeWidget... + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); + } - // Give the Input Manager a chance to process the mouse events. - NSInputManager *currentIManager = [NSInputManager currentInputManager]; - if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { - [currentIManager handleMouseEvent:theEvent]; - } + 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; + } - // 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; + 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 popup-"grab", mousegrab, or button-down-"grab": + QWidget *popup = QApplication::activePopupWidget(); + if (popup && !mouseGrabber) { + if (!popup->isAncestorOf(widgetUnderMouse)) { + // The popup will always grab the mouse unless the + // mouse is over a child, or the user scrolls: + if (eventType == QEvent::Wheel) + return 0; + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else if (popup == widgetUnderMouse) { + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else { + returnLocalPoint = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); + return widgetUnderMouse; + } } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; + QWidget *target = mouseGrabber; + if (!target && buttonDownNotBlockedByModal) + target = qt_button_down; + if (!target) + target = widgetUnderMouse; + if (!target) + return 0; - // 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()); + returnLocalPoint = target->mapFromGlobal(returnGlobalPoint); + return target; +} - 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]; - } +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_mouse_receiver) && qt_last_mouse_receiver) + return; + + if (maybeEnterWidget) { + if (!qt_last_mouse_receiver) { + // case 3 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0); + qt_last_mouse_receiver = maybeEnterWidget; + } else if (qt_last_mouse_receiver->internalWinId() && maybeEnterWidget->internalWinId()) { + // case 1 + if (qt_last_mouse_receiver->isVisible()) { + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = maybeEnterWidget; + } + } // 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_mouse_receiver && qt_last_mouse_receiver->internalWinId()) { + // case 2 + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + // This seems to be the only case where we need to update qt_last_mouse_receiver + // from the mac specific code. Otherwise, QApplicationPrivate::sendMouseEvent + // will handle it: + qt_last_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); + if (!nativeWidget) { + // Path typically taken for mouse moves (send + // directly from [QCocoaWindow sendEvent] + if (!widgetUnderMouse) + return false; + 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. + widgetToGetMouse = 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) { @@ -1293,17 +1470,17 @@ void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivat } #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) { @@ -1323,6 +1500,7 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) return result; } +#if QT_MAC_USE_COCOA void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) { if (!widgetForWindow) @@ -1347,6 +1525,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) @@ -1448,6 +1627,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) { OSMenuRef menu = static_cast<OSMenuRef>(theMenu); @@ -1481,12 +1661,24 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } -#ifdef QT_MAC_USE_COCOA -void qt_cocoaChangeOverrideCursor(const QCursor &cursor) +class CocoaPostMessageAfterEventLoopExitHelp : public QObject { - QMacCocoaAutoReleasePool pool; - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; -} + 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(); + } + + ~CocoaPostMessageAfterEventLoopExitHelp() + { + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + } +}; void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2) { @@ -1500,6 +1692,15 @@ void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2 context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; [NSApp postEvent:e atStart:NO]; } + +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() @@ -1523,6 +1724,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 diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index d61c00f70f..334d9e8c2f 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -103,6 +103,7 @@ #include <qtimer.h> #include <qtooltip.h> #include <private/qeffects_p.h> +#include <private/qwidget_p.h> #include <qtextdocument.h> #include <qdebug.h> #include <qpoint.h> @@ -144,18 +145,14 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); -void qt_cocoaChangeOverrideCursor(const QCursor &cursor); // These methods exists only for supporting unified mode. void macDrawRectOnTop(void * /*OSWindowRef */ window); void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window); void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget); -#endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); +#endif bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent); -void qt_mac_dispatchNCMouseMessage(void */* NSWindow* */eventWindow, void */* NSEvent* */mouseEvent, - QWidget *widgetToGetEvent, bool &leftButtonIsRightButton); -bool qt_mac_handleMouseEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button); bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event); inline QApplication *qAppInstance() { return static_cast<QApplication *>(QCoreApplication::instance()); } struct ::TabletProximityRec; @@ -165,6 +162,29 @@ Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations); QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height); void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase); + +#if QT_MAC_USE_COCOA && __OBJC__ +struct DnDParams +{ + NSView *view; + NSEvent *theEvent; + QPoint globalPoint; + NSDragOperation performedAction; +}; + +DnDParams *macCurrentDnDParameters(); +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); + +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent); +QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType, + QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse); +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget); +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event); +#endif + inline int flipYCoordinate(int y) { return QApplication::desktop()->screenGeometry(0).height() - y; @@ -221,10 +241,64 @@ public: } }; void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); #endif #endif +class QMacScrollOptimization { + // This class is made to optimize for the case when the user + // scrolls both horizontally and vertically at the same + // time. This will result in two QWheelEvents (one for each + // direction), which will typically result in two calls to + // QWidget::_scroll_sys. Rather than copying pixels twize on + // screen because of this, we add this helper class to try to + // get away with only one blit. + static QWidgetPrivate *_target; + static bool _inWheelEvent; + static int _dx; + static int _dy; + static QRect _scrollRect; + +public: + static void initDelayedScroll() + { + _inWheelEvent = true; + } + + static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect) + { + if (!_inWheelEvent) + return false; + if (_target && _target != target) + return false; + if (_scrollRect.width() != -1 && _scrollRect != scrollRect) + return false; + + _target = target; + _dx += dx; + _dy += dy; + _scrollRect = scrollRect; + return true; + } + + static void performDelayedScroll() + { + if (!_inWheelEvent) + return; + _inWheelEvent = false; + if (!_target) + return; + + _target->scroll_sys(_dx, _dy, _scrollRect); + + _target = 0; + _dx = 0; + _dy = 0; + _scrollRect = QRect(0, 0, -1, -1); + } +}; + void qt_mac_post_retranslateAppMenu(); void qt_mac_display(QWidget *widget); diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index fdb35d54be..40697bfac8 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -141,6 +141,7 @@ public: int supportsPremultipliedAlpha : 1; int avkonComponentsSupportTransparency : 1; int menuBeingConstructed : 1; + int orientationSet : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type static CEikButtonGroupContainer *cba; @@ -295,6 +296,7 @@ inline QS60Data::QS60Data() supportsPremultipliedAlpha(0), avkonComponentsSupportTransparency(0), menuBeingConstructed(0), + orientationSet(0), s60ApplicationFactory(0) #ifdef Q_OS_SYMBIAN ,s60InstalledTrapHandler(0) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index f35afb0ef1..475d4da4da 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -127,6 +127,10 @@ #include "qtabwidget.h" // Needed in inTabWidget() #endif // QT_KEYPAD_NAVIGATION +#ifdef Q_WS_S60 +#include <aknappui.h> +#endif + // widget/widget data creation count //#define QWIDGET_EXTRA_DEBUG //#define ALIEN_DEBUG @@ -144,6 +148,10 @@ Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable) } #endif +#if defined(QT_MAC_USE_COCOA) +bool qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; +#endif + static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) && @@ -300,11 +308,8 @@ QWidgetPrivate::QWidgetPrivate(int version) #endif #elif defined(Q_WS_MAC) , needWindowChange(0) - , hasAlienChildren(0) , window_event(0) , qd_hd(0) -#elif defined (Q_WS_QPA) - , screenNumber(0) #endif { if (!qApp) { @@ -324,6 +329,7 @@ QWidgetPrivate::QWidgetPrivate(int version) hasOwnContext = false; isInUnifiedToolbar = false; unifiedSurface = 0; + touchEventsEnabled = false; #endif // QT_MAC_USE_COCOA #ifdef QWIDGET_EXTRA_DEBUG static int count = 0; @@ -1277,7 +1283,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) } #elif defined(Q_WS_QPA) if (desktopWidget) { - int screen = desktopWidget->d_func()->screenNumber; + int screen = desktopWidget->d_func()->topData()->screenIndex; QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); platform->moveToScreen(q, screen); } @@ -1305,9 +1311,9 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (f & Qt::MSWindowsOwnDC) q->setAttribute(Qt::WA_NativeWindow); -#ifdef Q_WS_MAC - q->setAttribute(Qt::WA_NativeWindow); -#endif +//#ifdef Q_WS_MAC +// q->setAttribute(Qt::WA_NativeWindow); +//#endif q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute() adjustQuitOnCloseAttribute(); @@ -1414,10 +1420,6 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) #ifndef Q_WS_QPA if (QWidget *parent = parentWidget()) { -#ifdef Q_WS_MAC - if (testAttribute(Qt::WA_NativeWindow) == false) - parent->d_func()->hasAlienChildren = true; -#endif if (type & Qt::Window) { if (!parent->testAttribute(Qt::WA_WState_Created)) parent->createWinId(); @@ -1729,6 +1731,7 @@ void QWidgetPrivate::createTLExtra() #if defined(Q_WS_QPA) x->platformWindow = 0; x->platformWindowFormat = QPlatformWindowFormat::defaultFormat(); + x->screenIndex = 0; #endif } } @@ -5400,6 +5403,9 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP return; #if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + if (qt_mac_clearDirtyOnWidgetInsideDrawWidget) + dirtyOnWidget = QRegion(); + // We disable the rendering of QToolBar in the backingStore if // it's supposed to be in the unified toolbar on Mac OS X. if (backingStore && isInUnifiedToolbar) @@ -5487,7 +5493,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { - QPainter p(q); paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); } @@ -5669,10 +5674,12 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QRect boundingRect; bool dirtyBoundingRect = true; const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren); + const bool excludeNativeChildren = (flags & DontDrawNativeChildren); do { QWidget *x = qobject_cast<QWidget*>(siblings.at(index)); - if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow()) { + if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow() + && !(excludeNativeChildren && x->internalWinId())) { if (dirtyBoundingRect) { boundingRect = rgn.boundingRect(); dirtyBoundingRect = false; @@ -10076,7 +10083,13 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) #if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN) if (newParent && parent && !desktopWidget) { - if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) + if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget() && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parent->d_func()->enforceNativeChildren(); else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen)) setAttribute(Qt::WA_NativeWindow); @@ -10723,7 +10736,13 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) ic->setFocusWidget(0); } } - if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) + if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget() +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); @@ -10856,6 +10875,42 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->registerTouchWindow(); #endif break; + case Qt::WA_LockPortraitOrientation: + case Qt::WA_LockLandscapeOrientation: + case Qt::WA_AutoOrientation: { + const Qt::WidgetAttribute orientations[3] = { + Qt::WA_LockPortraitOrientation, + Qt::WA_LockLandscapeOrientation, + Qt::WA_AutoOrientation + }; + + if (on) { + // We can only have one of these set at a time + for (int i = 0; i < 3; ++i) { + if (orientations[i] != attribute) + setAttribute_internal(orientations[i], false, data, d); + } + } + +#ifdef Q_WS_S60 + CAknAppUiBase* appUi = static_cast<CAknAppUiBase*>(CEikonEnv::Static()->EikAppUi()); + const CAknAppUiBase::TAppUiOrientation s60orientations[] = { + CAknAppUiBase::EAppUiOrientationPortrait, + CAknAppUiBase::EAppUiOrientationLandscape, + CAknAppUiBase::EAppUiOrientationAutomatic + }; + CAknAppUiBase::TAppUiOrientation s60orientation = CAknAppUiBase::EAppUiOrientationUnspecified; + for (int i = 0; i < 3; ++i) { + if (testAttribute(orientations[i])) { + s60orientation = s60orientations[i]; + break; + } + } + QT_TRAP_THROWING(appUi->SetOrientationL(s60orientation)); + S60->orientationSet = true; +#endif + break; + } default: break; } diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 376999e41b..3ba12cdd2d 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -155,7 +155,6 @@ static bool qt_mac_raise_process = true; static OSWindowRef qt_root_win = 0; QWidget *mac_mouse_grabber = 0; QWidget *mac_keyboard_grabber = 0; -extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp #ifndef QT_MAC_USE_COCOA #ifdef QT_NAMESPACE @@ -179,13 +178,14 @@ static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget"); /***************************************************************************** Externals *****************************************************************************/ +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm extern bool qt_event_remove_activate(); //qapplication_mac.mm extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; //qapplication_mac.mm extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm extern void qt_mac_update_cursor(); //qcursor_mac.mm @@ -193,7 +193,8 @@ extern bool qt_nograb(); extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp - +extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm +extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm /***************************************************************************** QWidget utility functions *****************************************************************************/ @@ -217,22 +218,13 @@ static QSize qt_mac_desktopSize() #ifdef QT_MAC_USE_COCOA static NSDrawer *qt_mac_drawer_for(const QWidget *widget) { - // This only goes one level below the content view so start with the window. - // This works fine for straight Qt stuff, but runs into problems if we are - // embedding, but if that's the case, they probably want to be using - // NSDrawer directly. - NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->winId()); + NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId()); NSArray *windows = [NSApp windows]; for (NSWindow *window in windows) { NSArray *drawers = [window drawers]; for (NSDrawer *drawer in drawers) { if ([drawer contentView] == widgetView) return drawer; - NSArray *views = [[drawer contentView] subviews]; - for (NSView *view in views) { - if (view == widgetView) - return drawer; - } } } return 0; @@ -314,7 +306,7 @@ bool qt_mac_is_macdrawer(const QWidget *w) bool qt_mac_insideKeyWindow(const QWidget *w) { #ifdef QT_MAC_USE_COCOA - return [[reinterpret_cast<NSView *>(w->winId()) window] isKeyWindow]; + return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow]; #else Q_UNUSED(w); #endif @@ -421,7 +413,14 @@ inline static void qt_mac_set_fullscreen_mode(bool b) Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) { - return reinterpret_cast<OSViewRef>(w->data->winid); + return reinterpret_cast<OSViewRef>(w->internalWinId()); +} + +Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w) +{ + // Get the first non-alien (parent) widget for + // w, and return its NSView (if it has one): + return reinterpret_cast<OSViewRef>(w->effectiveWinId()); } Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w) @@ -479,11 +478,12 @@ bool qt_isGenuineQWidget(const QWidget *window) Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) { - OSViewRef hiview = qt_mac_nativeview_for(w); - if (hiview){ + if (OSViewRef hiview = qt_mac_effectiveview_for(w)) { OSWindowRef window = qt_mac_window_for(hiview); - if (!window && qt_isGenuineQWidget(hiview)) { - QWidget *myWindow = w->window(); + if (window) + return window; + + if (qt_isGenuineQWidget(hiview)) { // This is a workaround for NSToolbar. When a widget is hidden // by clicking the toolbar button, Cocoa reparents the widgets // to another window (but Qt doesn't know about it). @@ -491,18 +491,22 @@ Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) // but at this point it's window is nil, but the window it's being brought // into (the Qt one) is for sure created. // This stops the hierarchy moving under our feet. - if (myWindow != w && qt_mac_window_for(qt_mac_nativeview_for(myWindow))) - return qt_mac_window_for(qt_mac_nativeview_for(myWindow)); + QWidget *toplevel = w->window(); + if (toplevel != w) { + hiview = qt_mac_nativeview_for(toplevel); + if (OSWindowRef w = qt_mac_window_for(hiview)) + return w; + } - myWindow->d_func()->createWindow_sys(); - // Reget the hiview since the "create window could potentially move the view (I guess). - hiview = qt_mac_nativeview_for(w); - window = qt_mac_window_for(hiview); + toplevel->d_func()->createWindow_sys(); + // Reget the hiview since "create window" could potentially move the view (I guess). + hiview = qt_mac_nativeview_for(toplevel); + return qt_mac_window_for(hiview); } - return window; } return 0; } + #ifndef QT_MAC_USE_COCOA /* Checks if the current group is a 'stay on top' group. If so, the group gets removed from the hash table */ @@ -580,25 +584,6 @@ inline static void qt_mac_set_window_group_to_popup(OSWindowRef window) } #endif -#ifdef QT_MAC_USE_COCOA -void qt_mac_set_needs_display(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 - inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect) { if (!widget) @@ -636,6 +621,51 @@ inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const return false; } +void QWidgetPrivate::macSetNeedsDisplay(QRegion region) +{ + Q_Q(QWidget); +#ifndef QT_MAC_USE_COCOA + if (region.isEmpty()) + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); + else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys()) + HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); + else + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. +#else + if (NSView *nativeView = qt_mac_nativeview_for(q)) { + // INVARIANT: q is _not_ alien. So we can optimize a little: + if (region.isEmpty()) { + [nativeView setNeedsDisplay:YES]; + } else { + 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()); + [nativeView setNeedsDisplayInRect:nsrect]; + } + } + } else if (QWidget *effectiveWidget = q->nativeParentWidget()) { + // INVARIANT: q is alien, and effectiveWidget is native. + if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) { + if (region.isEmpty()) { + const QRect &rect = q->rect(); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } + } + } + } +#endif +} + void QWidgetPrivate::macUpdateIsOpaque() { Q_Q(QWidget); @@ -1569,6 +1599,11 @@ OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, O #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate]; + +#ifdef ALIEN_DEBUG + qDebug() << "Creating NSView for" << widget; +#endif + if (view && parent) [parent addSubview:view]; return view; @@ -1635,7 +1670,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) // to happen, prevent that here (you really want the thing hidden). if (up >= 0 || topData->resizer != 0) topData->resizer += up; - OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId())); + OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId())); { #ifndef QT_MAC_USE_COCOA WindowClass wclass; @@ -1670,6 +1705,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) void QWidgetPrivate::qt_clean_root_win() { #ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; [qt_root_win release]; #else if(!qt_root_win) @@ -2310,15 +2346,12 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin } [windowRef setContentView:nsview]; [nsview setHidden:NO]; - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); transferChildren(); // Tell Cocoa explicit that we wan't the view to receive key events // (regardless of focus policy) because this is how it works on other // platforms (and in the carbon port): - if (!qApp->focusWidget()) - [windowRef makeFirstResponder:nsview]; + [windowRef makeFirstResponder:nsview]; if (topExtra->posFromMove) { updateFrameStrut(); @@ -2349,8 +2382,9 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); } - if (qApp->overrideCursor()) - [windowRef disableCursorRects]; + // Its more performant to handle the mouse cursor + // ourselves, expecially when using alien widgets: + [windowRef disableCursorRects]; setWindowLevel(); macUpdateHideOnSuspend(); @@ -2468,8 +2502,6 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.crect.setRect(0, 0, desktopSize.width(), desktopSize.height()); dialog = popup = false; // force these flags off } else { - q->setAttribute(Qt::WA_WState_Visible, false); - if (topLevel && (type != Qt::Drawer)) { if (QDesktopWidget *dsk = QApplication::desktop()) { // calc pos/size from screen const bool wasResized = q->testAttribute(Qt::WA_Resized); @@ -2559,6 +2591,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO transfer = true; } else if (parentWidget) { // I need to be added to my parent, therefore my parent needs an NSView + // Alien note: a 'window' was supplied as argument, meaning this widget + // is not alien. So therefore the parent cannot be alien either. parentWidget->createWinId(); parent = qt_mac_nativeview_for(parentWidget); } @@ -2630,11 +2664,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.fstrut_dirty = false; // non-toplevel widgets don't have a frame, so no need to update the strut #ifdef QT_MAC_USE_COCOA - if (q->testAttribute(Qt::WA_NativeWindow) == false || - q->internalWinId() != 0) { -#ifdef ALIEN_DEBUG - qDebug() << "Skipping native widget creation for" << this; -#endif + if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) { + // INVARIANT: q is Alien, and we should not create an NSView to back it up. } else #endif if (OSViewRef osview = qt_mac_create_widget(q, this, qt_mac_nativeview_for(parentWidget))) { @@ -2646,21 +2677,26 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height()); [osview setFrame:bounds]; setWinId((WId)osview); + if (q->isVisible()) { + // If q were Alien before, but now became native (e.g. if a call to + // winId was done from somewhere), we need to show the view immidiatly: + QMacCocoaAutoReleasePool pool; + [osview setHidden:NO]; + } #endif - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); } } updateIsOpaque(); + + if (q->testAttribute(Qt::WA_DropSiteRegistered)) + registerDropSite(true); if (q->hasFocus()) setFocus_sys(); if (!topLevel && initializeWindow) setWSGeometry(); if (destroyid) qt_mac_destructView(destroyid); - if (q->testAttribute(Qt::WA_AcceptTouchEvents)) - registerTouchWindow(); } /*! @@ -2694,16 +2730,29 @@ QWidget::macCGHandle() const return handle(); } +void qt_mac_repaintParentUnderAlienWidget(QWidget *alienWidget) +{ + QWidget *nativeParent = alienWidget->nativeParentWidget(); + if (!nativeParent) + return; + + QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0)); + QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size()); + nativeParent->repaint(dirtyRect); +} + void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); + QMacCocoaAutoReleasePool pool; d->aboutToDestroy(); if (!isWindow() && parentWidget()) parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + if (!internalWinId()) + qt_mac_repaintParentUnderAlienWidget(this); d->deactivateWidgetCleanup(); qt_mac_event_release(this); if(testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; setAttribute(Qt::WA_WState_Created, false); QObjectList chldrn = children(); for(int i = 0; i < chldrn.size(); i++) { // destroy all widget children @@ -2759,7 +2808,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) void QWidgetPrivate::transferChildren() { Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (!q->internalWinId()) return; // Can't add any views anyway QObjectList chlist = q->children(); @@ -2771,7 +2820,7 @@ void QWidgetPrivate::transferChildren() // This seems weird, no need to call it in a loop right? if (!topData()->caption.isEmpty()) setWindowTitle_helper(extra->topextra->caption); - if (w->testAttribute(Qt::WA_WState_Created)) { + if (w->internalWinId()) { #ifndef QT_MAC_USE_COCOA HIViewAddSubview(qt_mac_nativeview_for(q), qt_mac_nativeview_for(w)); #else @@ -2900,11 +2949,11 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) #ifndef QT_MAC_USE_COCOA old_window_event = window_event; #else - OSWindowRef oldWindow = qt_mac_window_for(old_id); if (qt_mac_is_macdrawer(q)) { oldDrawer = qt_mac_drawer_for(q); } if (wasWindow) { + OSWindowRef oldWindow = qt_mac_window_for(old_id); oldToolbar = [oldWindow toolbar]; if (oldToolbar) { [oldToolbar retain]; @@ -3026,7 +3075,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) QPoint QWidget::mapToGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = pos + data->crect.topLeft(); return isWindow() ? p : parentWidget()->mapToGlobal(p); } @@ -3053,7 +3102,7 @@ QPoint QWidget::mapToGlobal(const QPoint &pos) const QPoint QWidget::mapFromGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = isWindow() ? pos : parentWidget()->mapFromGlobal(pos); return p - data->crect.topLeft(); } @@ -3080,28 +3129,12 @@ void QWidgetPrivate::updateSystemBackground() void QWidgetPrivate::setCursor_sys(const QCursor &) { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::unsetCursor_sys() { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::setWindowTitle_sys(const QString &caption) @@ -3243,24 +3276,28 @@ void QWidget::grabMouse() if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true); } } #ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &) +void QWidget::grabMouse(const QCursor &cursor) { if(isVisible() && !qt_nograb()) { if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor)); } } #endif void QWidget::releaseMouse() { - if(!qt_nograb() && mac_mouse_grabber == this) + if(!qt_nograb() && mac_mouse_grabber == this) { mac_mouse_grabber = 0; + qt_mac_setMouseGrabCursor(false); + } } void QWidget::grabKeyboard() @@ -3345,35 +3382,10 @@ QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() void QWidgetPrivate::update_sys(const QRect &r) { Q_Q(QWidget); - if (r == q->rect()) { - if (updateRedirectedToGraphicsProxyWidget(q, r)) - return; - dirtyOnWidget += r; -#ifndef QT_MAC_USE_COCOA - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); -#else - qt_mac_set_needs_display(q, QRegion()); -#endif + if (updateRedirectedToGraphicsProxyWidget(q, r)) return; - } - - int x = r.x(), y = r.y(), w = r.width(), h = r.height(); - if (w < 0) - w = q->data->crect.width() - x; - if (h < 0) - h = q->data->crect.height() - y; - if (w && h) { - const QRect updateRect = QRect(x, y, w, h); - if (updateRedirectedToGraphicsProxyWidget(q, updateRect)) - return; -#ifndef QT_MAC_USE_COCOA - dirtyOnWidget += updateRect; - HIRect r = CGRectMake(x, y, w, h); - HIViewSetNeedsDisplayInRect(qt_mac_nativeview_for(q), &r, true); -#else - [qt_mac_nativeview_for(q) setNeedsDisplayInRect:NSMakeRect(x, y, w, h)]; -#endif - } + dirtyOnWidget += r; + macSetNeedsDisplay(r != q->rect() ? r : QRegion()); } void QWidgetPrivate::update_sys(const QRegion &rgn) @@ -3382,33 +3394,7 @@ void QWidgetPrivate::update_sys(const QRegion &rgn) if (updateRedirectedToGraphicsProxyWidget(q, rgn)) return; dirtyOnWidget += rgn; -#ifndef QT_MAC_USE_COCOA - RgnHandle rgnHandle = rgn.toQDRgnForUpdate_sys(); - if (rgnHandle) - HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); - else { - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. - } -#else - // Alien support: get the first native ancestor widget (will be q itself in the non-alien case), - // map the coordinates from q space to NSView space and invalidate the rect. - QWidget *nativeParent = q->internalWinId() ? q : q->nativeParentWidget(); - if (nativeParent == 0) - return; - - QVector<QRect> rects = rgn.rects(); - for (int i = 0; i < rects.count(); ++i) { - const QRect &rect = rects.at(i); - - const QRect nativeBoundingRect = QRect( - QPoint(q->mapTo(nativeParent, rect.topLeft())), - QSize(rect.size())); - - [qt_mac_nativeview_for(nativeParent) setNeedsDisplayInRect:NSMakeRect(nativeBoundingRect.x(), - nativeBoundingRect.y(), nativeBoundingRect.width(), - nativeBoundingRect.height())]; - } -#endif + macSetNeedsDisplay(rgn); } bool QWidgetPrivate::isRealWindow() const @@ -3447,7 +3433,6 @@ void QWidgetPrivate::show_sys() data.fstrut_dirty = true; if (realWindow) { - // Delegates can change window state, so record some things earlier. bool isCurrentlyMinimized = (q->windowState() & Qt::WindowMinimized); setModal_sys(); OSWindowRef window = qt_mac_window_for(q); @@ -3494,9 +3479,11 @@ void QWidgetPrivate::show_sys() } } setSubWindowStacking(true); + qt_mac_update_cursor(); #endif if (q->windowType() == Qt::Popup) { - if (q->focusWidget()) + qt_button_down = 0; + if (q->focusWidget()) q->focusWidget()->d_func()->setFocus_sys(); else setFocus_sys(); @@ -3518,21 +3505,32 @@ void QWidgetPrivate::show_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; - + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just show the view: + [view setHidden:NO]; + } else { + // INVARIANT: q is alien. Repaint q instead: + q->repaint(); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; } +#endif + topLevelAt_cache = 0; qt_event_request_window_change(q); } - QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) { #ifndef QT_MAC_USE_COCOA @@ -3613,6 +3611,7 @@ void QWidgetPrivate::hide_sys() } #endif toggleDrawers(false); + qt_mac_update_cursor(); #ifndef QT_MAC_USE_COCOA // Clear modality (because it seems something that we've always done). if (data.window_modality != Qt::NonModal) { @@ -3660,18 +3659,29 @@ void QWidgetPrivate::hide_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just hide the view: + [view setHidden:YES]; + } else { + // INVARIANT: q is alien. Repaint where q is placed instead: + qt_mac_repaintParentUnderAlienWidget(q); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - if (enterWidget && enterWidget->data->in_destructor) - enterWidget = 0; - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; - } - +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + } +#endif + + topLevelAt_cache = 0; qt_event_request_window_change(q); deactivateWidgetCleanup(); qt_mac_event_release(q); @@ -3894,12 +3904,14 @@ void QWidgetPrivate::raise_sys() QWidget *parentWidget = q->parentWidget(); if(parentWidget) { OSWindowRef parentWindow = qt_mac_window_for(parentWidget); - if(parentWindow && [parentWindow isOnActiveSpace]) { - // The window was created in a different space. Therefore if we want - // to show it in the current space we need to recreate it in the new - // space. - recreateMacWindow(); - window = qt_mac_window_for(q); + if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) { + if ([parentWindow performSelector:@selector(isOnActiveSpace)]) { + // The window was created in a different space. Therefore if we want + // to show it in the current space we need to recreate it in the new + // space. + recreateMacWindow(); + window = qt_mac_window_for(q); + } } } } @@ -3916,6 +3928,7 @@ void QWidgetPrivate::raise_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { //raise this window @@ -3956,6 +3969,7 @@ void QWidgetPrivate::lower_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { SendBehind(qt_mac_window_for(q), 0); @@ -4012,6 +4026,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) #endif } +#ifndef QT_MAC_USE_COCOA /* Modifies the bounds for a widgets backing HIView during moves and resizes. Also updates the widget, either by scrolling its contents or repainting, depending on the WA_StaticContents @@ -4019,7 +4034,6 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) */ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect) { -#ifndef QT_MAC_USE_COCOA HIRect bounds = CGRectMake(newRect.x(), newRect.y(), newRect.width(), newRect.height()); @@ -4111,13 +4125,8 @@ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRe HIViewSetNeedsDisplayInRect(view, &verticalSlice, true); const HIRect horizontalSlice = CGRectMake(0, starty, startx, stopy); HIViewSetNeedsDisplayInRect(view, &horizontalSlice, true); -#else - Q_UNUSED(oldRect); - NSRect bounds = NSMakeRect(newRect.x(), newRect.y(), - newRect.width(), newRect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; -#endif } +#endif /* Helper function for non-toplevel widgets. Helps to map Qt's 32bit @@ -4138,7 +4147,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) { Q_Q(QWidget); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - Q_UNUSED(oldRect); + + if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) { + // We have no view to move, and no paint engine that + // we can update dirty regions on. So just return: + return; + } + + QMacCocoaAutoReleasePool pool; + /* There are up to four different coordinate systems here: Qt coordinate system for this widget. @@ -4146,13 +4163,31 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) Qt coordinate system for parent X coordinate system for parent (relative to parent's wrect). */ + + // wrect is the same as crect, except that it is + // clipped to fit inside parent (and screen): QRect wrect; - //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys) - QRect xrect = data.crect; + // wrectInParentCoordSys will be the same as wrect, except that it is + // originated in q's parent rather than q itself. It starts out in + // parent's Qt coord system, and ends up in parent's coordinate system: + QRect wrectInParentCoordSys = data.crect; + + // If q's parent has been clipped, parentWRect will + // be filled with the parents clipped crect: QRect parentWRect; + + // Embedded have different meaning on each platform, and on + // Mac, it means that q is a QMacNativeWidget. bool isEmbeddedWindow = (q->isWindow() && topData()->embedded); - if (isEmbeddedWindow) { +#ifdef QT_MAC_USE_COCOA + NSView *nsview = qt_mac_nativeview_for(q); +#endif + if (!isEmbeddedWindow) { + parentWRect = q->parentWidget()->data->wrect; + } else { + // INVARIANT: q's parent view is not owned by Qt. So we need to + // do some extra calls to get the clipped rect of the parent view: #ifndef QT_MAC_USE_COCOA HIViewRef parentView = HIViewGetSuperview(qt_mac_nativeview_for(q)); #else @@ -4171,43 +4206,57 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX); parentWRect = wrectRange; } - } else { - parentWRect = q->parentWidget()->data->wrect; } if (parentWRect.isValid()) { - // parent is clipped, and we have to clip to the same limit as parent - if (!parentWRect.contains(xrect) && !isEmbeddedWindow) { - xrect &= parentWRect; - wrect = xrect; - //translate from parent's to my Qt coord sys + // INVARIANT: q's parent has been clipped. + // So we fit our own wrects inside it: + if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) { + wrectInParentCoordSys &= parentWRect; + wrect = wrectInParentCoordSys; + // Make sure wrect is originated in q's coordinate system: wrect.translate(-data.crect.topLeft()); } - //translate from parent's Qt coords to parent's X coords - xrect.translate(-parentWRect.topLeft()); - + // // Make sure wrectInParentCoordSys originated in q's parent coordinate system: + wrectInParentCoordSys.translate(-parentWRect.topLeft()); } else { - // parent is not clipped, we may or may not have to clip + // INVARIANT: we dont know yet the clipping rect of q's parent. + // So we may or may not have to adjust our wrects: if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) { - // This is where the main optimization is: we are already - // clipped, and if our clip is still valid, we can just - // move our window, and do not need to move or clip - // children + // This is where the main optimization is: we have an old wrect from an earlier + // setGeometry call, and the new crect is smaller than it. If the final wrect is + // also inside the old wrect, we can just move q and its children to the new + // location without any clipping: + + // vrect will be the part of q that's will be visible inside + // q's parent. If it inside the old wrect, then we can just move: + QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect(); + vrect.translate(-data.crect.topLeft()); - QRect vrect = xrect & q->parentWidget()->rect(); - vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords if (data.wrect.contains(vrect)) { - xrect = data.wrect; - xrect.translate(data.crect.topLeft()); + wrectInParentCoordSys = data.wrect; + wrectInParentCoordSys.translate(data.crect.topLeft()); #ifndef QT_MAC_USE_COCOA - HIRect bounds = CGRectMake(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); + HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); HIViewSetFrame(qt_mac_nativeview_for(q), &bounds); #else - NSRect bounds = NSMakeRect(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; + if (nsview) { + // INVARIANT: q is native. Set view frame: + NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + } else { + // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect): + QWidget *parent = q->parentWidget(); + QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size()); + + nativeParent->update(dirtyWRect); + } #endif if (q->testAttribute(Qt::WA_OutsideWSRange)) { q->setAttribute(Qt::WA_OutsideWSRange, false); @@ -4216,7 +4265,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4226,8 +4276,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX); - if (!validRange.contains(xrect)) { - // we are too big, and must clip + if (!validRange.contains(wrectInParentCoordSys)) { + // We're too big, and must clip: QPoint screenOffset(0, 0); // offset of the part being on screen const QWidget *parentWidget = q->parentWidget(); while (parentWidget && !parentWidget->isWindow()) { @@ -4239,15 +4289,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) 2*WRECT_MAX, 2*WRECT_MAX); - xrect &=cropRect; - wrect = xrect; - wrect.translate(-data.crect.topLeft()); // translate wrect in my Qt coordinates + wrectInParentCoordSys &=cropRect; + wrect = wrectInParentCoordSys; + wrect.translate(-data.crect.topLeft()); } #endif //QT_MAC_USE_COCOA } // unmap if we are outside the valid window system coord system - bool outsideRange = !xrect.isValid(); + bool outsideRange = !wrectInParentCoordSys.isValid(); bool mapWindow = false; if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) { q->setAttribute(Qt::WA_OutsideWSRange, outsideRange); @@ -4255,7 +4305,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + // If q is Alien, the following call does nothing: + [nsview setHidden:YES]; #endif q->setAttribute(Qt::WA_Mapped, false); } else if (!q->isHidden()) { @@ -4266,10 +4317,10 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) if (outsideRange) return; + // Store the new clipped rect: bool jump = (data.wrect != wrect); data.wrect = wrect; - // and now recursively for all children... // ### can be optimized for (int i = 0; i < children.size(); ++i) { @@ -4281,17 +4332,56 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) } } - qt_mac_update_widget_position(q, oldRect, xrect); - - if (jump) +#ifndef QT_MAC_USE_COCOA + // Move the actual HIView: + qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys); + if (jump) q->update(); +#else + if (nsview) { + // INVARIANT: q is native. Move the actual NSView: + NSRect bounds = NSMakeRect( + wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + if (jump) + q->update(); + } else if (QApplicationPrivate::graphicsSystem() == 0){ + // INVARIANT: q is alien and we use native paint engine. + // Schedule updates where q is moved from and to: + const QWidget *parent = q->parentWidget(); + const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft()); + const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size()); + const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size()); + + const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size(); + const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft(); + + // Resolve/minimize the region that needs to update: + if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) { + // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only + // need to blit pixels, and skip a repaint. But we can only make this work if we + // had access to the backbuffer, so we need to update all: + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) { + // We only need to redraw exposed areas: + nativeParent->update(dirtyNewWRect - dirtyOldWRect); + } else { + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } + } +#endif if (mapWindow && !dontShow) { q->setAttribute(Qt::WA_Mapped); #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4353,7 +4443,6 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) QMacCocoaAutoReleasePool pool; bool realWindow = isRealWindow(); - BOOL needDisplay = realWindow ? YES : NO; if (realWindow && !q->testAttribute(Qt::WA_DontShowOnScreen)){ adjustWithinMaxAndMinSize(w, h); @@ -4401,7 +4490,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (currTopLeft.x() == x && currTopLeft.y() == y && cocoaFrameRect.size.width != 0 && cocoaFrameRect.size.height != 0) { - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; } else { // The window is moved and resized (or resized to zero). // Since Cocoa usually only sends us a resize callback after @@ -4410,7 +4499,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) // would have the same origin as the setFrame call) we shift the // window back and forth inbetween. cocoaFrameRect.origin.y += 1; - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; cocoaFrameRect.origin.y -= 1; [window setFrameOrigin:cocoaFrameRect.origin]; } @@ -4418,6 +4507,8 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) } else { setGeometry_sys_helper(x, y, w, h, isMove); } + + topLevelAt_cache = 0; } void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove) @@ -4468,17 +4559,11 @@ void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isM const QRect oldRect(oldp, olds); if (!isResize && QApplicationPrivate::graphicsSystem()) moveRect(oldRect, x - oldp.x(), y - oldp.y()); + setWSGeometry(false, oldRect); - if (isResize && QApplicationPrivate::graphicsSystem()) { - invalidateBuffer(q->rect()); - if (extra && !graphicsEffect && !extra->mask.isEmpty()) { - QRegion oldRegion(extra->mask.translated(oldp)); - oldRegion &= oldRect; - q->parentWidget()->d_func()->invalidateBuffer(oldRegion); - } else { - q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(oldRect)); - } - } + + if (isResize && QApplicationPrivate::graphicsSystem()) + invalidateBuffer_resizeHelper(oldp, olds); } if(isMove || isResize) { @@ -4558,6 +4643,7 @@ void QWidgetPrivate::updateMaximizeButton_sys() void QWidgetPrivate::scroll_sys(int dx, int dy) { if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { + // INVARIANT: Alien paint engine scrollChildren(dx, dy); scrollRect(q_func()->rect(), dx, dy); } else { @@ -4565,37 +4651,81 @@ void QWidgetPrivate::scroll_sys(int dx, int dy) } } -void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &qscrollRect) { - Q_Q(QWidget); + if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect)) + return; + Q_Q(QWidget); if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { - scrollRect(r, dx, dy); + // INVARIANT: Alien paint engine + scrollRect(qscrollRect, dx, dy); return; } - const bool valid_rect = r.isValid(); - if (!q->updatesEnabled() && (valid_rect || q->children().isEmpty())) - return; + static int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; + } - qt_event_request_window_change(q); + // Scroll the whole widget if qscrollRect is not valid: + QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect(); + validScrollRect &= clipRect(); + + // If q is overlapped by other widgets, we cannot just blit pixels since + // this will move overlapping widgets as well. In case we just update: + const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft())); + const bool accelerateScroll = accelEnv && isOpaque && !overlapped; + const bool isAlien = (q->internalWinId() == 0); + const QPoint scrollDelta(dx, dy); + + // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented). + // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is + // documented as undefined, but we exploit it to help factor our code into one function. + const bool scrollChildren = !qscrollRect.isValid(); + + if (!q->updatesEnabled()) { + // We are told not to update anything on q at this point. So unless + // we are supposed to scroll children, we bail out early: + if (!scrollChildren || q->children().isEmpty()) + return; + } + + if (!accelerateScroll) { + if (overlapped) { + QRegion region(validScrollRect); + subtractOpaqueSiblings(region); + update_sys(region); + }else { + update_sys(qscrollRect); + } + return; + } #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; +#else + Q_UNUSED(isAlien); + // We're not sure what the following call is supposed to achive + // but until we see what it breaks, we don't bring it into the + // Cocoa port: + qt_event_request_window_change(q); #endif - if(!valid_rect) { // scroll children - QPoint pd(dx, dy); - QWidgetList moved; - QObjectList chldrn = q->children(); - for(int i = 0; i < chldrn.size(); i++) { //first move all children - QObject *obj = chldrn.at(i); - if(obj->isWidgetType()) { - QWidget *w = (QWidget*)obj; - if(!w->isWindow()) { - w->data->crect = QRect(w->pos() + pd, w->size()); - if (w->testAttribute(Qt::WA_WState_Created)) { + // First move all native children. Alien children will indirectly be + // moved when the parent is scrolled. All directly or indirectly moved + // children will receive a move event before the function call returns. + QWidgetList movedChildren; + if (scrollChildren) { + QObjectList children = q->children(); + + for (int i=0; i<children.size(); i++) { + QObject *obj = children.at(i); + if (QWidget *w = qobject_cast<QWidget*>(obj)) { + if (!w->isWindow()) { + w->data->crect = QRect(w->pos() + scrollDelta, w->size()); #ifndef QT_MAC_USE_COCOA + if (w->testAttribute(Qt::WA_WState_Created)) { HIRect bounds = CGRectMake(w->data->crect.x(), w->data->crect.y(), w->data->crect.width(), w->data->crect.height()); HIViewRef hiview = qt_mac_nativeview_for(w); @@ -4606,83 +4736,118 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) HIViewSetFrame(hiview, &bounds); if (opaque) HIViewSetDrawingEnabled(hiview, true); + } #else - [qt_mac_nativeview_for(w) - setFrame:NSMakeRect(w->data->crect.x(), w->data->crect.y(), - w->data->crect.width(), w->data->crect.height())]; -#endif + if (NSView *view = qt_mac_nativeview_for(w)) { + // INVARIANT: w is not alien + [view setFrame:NSMakeRect( + w->data->crect.x(), w->data->crect.y(), + w->data->crect.width(), w->data->crect.height())]; } - moved.append(w); +#endif + movedChildren.append(w); } } } - //now send move events (do not do this in the above loop, breaks QAquaFocusWidget) - for(int i = 0; i < moved.size(); i++) { - QWidget *w = moved.at(i); - QMoveEvent e(w->pos(), w->pos() - pd); - QApplication::sendEvent(w, &e); - } } - if (!q->testAttribute(Qt::WA_WState_Created) || !q->isVisible()) - return; + if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) { + // Scroll q itself according to the qscrollRect, and + // call update on any exposed areas so that they get redrawn: - OSViewRef view = qt_mac_nativeview_for(q); #ifndef QT_MAC_USE_COCOA - HIRect scrollrect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - OSStatus err = _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - if (err) { - // The only parameter that can go wrong, is the rect. - qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); - scrollrect = CGRectMake(qMax(r.x(), 0), qMax(r.y(), 0), - qMin(r.width(), q->width()), qMin(r.height(), q->height())); - _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - } + OSViewRef view = qt_mac_nativeview_for(q); + HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height()); + OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + if (err) { + // The only parameter that can go wrong, is the rect. + qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); + scrollrect = CGRectMake(qMax(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0), + qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height())); + _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + } #else - NSRect scrollRect = valid_rect ? NSMakeRect(r.x(), r.y(), r.width(), r.height()) - : NSMakeRect(0, 0, q->width(), q->height()); + QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q; + if (!nativeWidget) + return; + OSViewRef view = qt_mac_nativeview_for(nativeWidget); + if (!view) + return; - // calc the updateRect - NSRect deltaXRect = { {0, 0}, {0, 0} }; - NSRect deltaYRect = { {0, 0}, {0, 0} }; - if (dy != 0) { - deltaYRect.size.width = scrollRect.size.width; - if (dy > 0) { - deltaYRect.size.height = dy; - } else { - deltaYRect.size.height = -dy; - deltaYRect.origin.y = scrollRect.size.height + dy; + // Calculate the rectangles that needs to be redrawn + // after the scroll. This will be source rect minus destination rect: + QRect deltaXRect; + if (dx != 0) { + deltaXRect.setY(validScrollRect.y()); + deltaXRect.setHeight(validScrollRect.height()); + if (dx > 0) { + deltaXRect.setX(validScrollRect.x()); + deltaXRect.setWidth(dx); + } else { + deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx); + deltaXRect.setWidth(-dx); + } } - } - if (dx != 0) { - deltaXRect.size.height = scrollRect.size.height; - if (dx > 0) { - deltaXRect.size.width = dx; - } else { - deltaXRect.size.width = -dx; - deltaXRect.origin.x = scrollRect.size.width + dx; + + QRect deltaYRect; + if (dy != 0) { + deltaYRect.setX(validScrollRect.x()); + deltaYRect.setWidth(validScrollRect.width()); + if (dy > 0) { + deltaYRect.setY(validScrollRect.y()); + deltaYRect.setHeight(dy); + } else { + deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy); + deltaYRect.setHeight(-dy); + } } - } - // ### Scroll the dirty regions as well, the following is not correct. - QRegion displayRegion = r.isNull() ? dirtyOnWidget : (dirtyOnWidget & r); - const QVector<QRect> &rects = dirtyOnWidget.rects(); - const QVector<QRect>::const_iterator end = rects.end(); - QVector<QRect>::const_iterator it = rects.begin(); - while (it != end) { - const QRect rect = *it; - const NSRect dirtyRect = NSMakeRect(rect.x() + dx, rect.y() + dy, - rect.width(), rect.height()); - [view setNeedsDisplayInRect:dirtyRect]; - ++it; - } + if (isAlien) { + // Adjust the scroll rect to the location as seen from the native parent: + QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft())); + validScrollRect.moveTo(scrollTopLeftInsideNative); + } + + // Make the pixel copy rect within the validScrollRect bounds: + NSRect nsscrollRect = NSMakeRect( + validScrollRect.x() + (dx < 0 ? -dx : 0), + validScrollRect.y() + (dy < 0 ? -dy : 0), + validScrollRect.width() + (dx > 0 ? -dx : 0), + validScrollRect.height() + (dy > 0 ? -dy : 0)); + + NSSize deltaSize = NSMakeSize(dx, dy); + [view scrollRect:nsscrollRect by:deltaSize]; + + // Some areas inside the scroll rect might have been marked as dirty from before, which + // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects + // should also move along to ensure that q receives repaints on the correct places. + // Since some of the dirty rects might lay outside, or only intersect with, the scroll + // rect, the old calls to setNeedsDisplay still makes sense. + // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have + // so far not been proven fruitful to solve this problem. + const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects(); + for (int i=0; i<dirtyRectsToScroll.size(); ++i) { + QRect qdirtyRect = dirtyRectsToScroll[i]; + qdirtyRect.translate(dx, dy); + update_sys(qdirtyRect); + } + + // Update newly exposed areas. This will generate new dirty areas on + // q, and therefore, we do it after updating the old dirty rects above: + if (dx != 0) + update_sys(deltaXRect); + if (dy != 0) + update_sys(deltaYRect); - NSSize deltaSize = NSMakeSize(dx, dy); - [view scrollRect:scrollRect by:deltaSize]; - [view setNeedsDisplayInRect:deltaXRect]; - [view setNeedsDisplayInRect:deltaYRect]; #endif // QT_MAC_USE_COCOA + } + + for (int i=0; i<movedChildren.size(); i++) { + QWidget *w = movedChildren.at(i); + QMoveEvent e(w->pos(), w->pos() - scrollDelta); + QApplication::sendEvent(w, &e); + } } int QWidget::metric(PaintDeviceMetric m) const @@ -4693,16 +4858,19 @@ int QWidget::metric(PaintDeviceMetric m) const case PdmWidthMM: return qRound(metric(PdmWidth) * 25.4 / qreal(metric(PdmDpiX))); case PdmHeight: - case PdmWidth: { + case PdmWidth: #ifndef QT_MAC_USE_COCOA - HIRect rect; + { HIRect rect; HIViewGetFrame(qt_mac_nativeview_for(this), &rect); -#else - NSRect rect = [qt_mac_nativeview_for(this) frame]; -#endif if(m == PdmWidth) return (int)rect.size.width; return (int)rect.size.height; } +#else + if (m == PdmWidth) + return data->crect.width(); + else + return data->crect.height(); +#endif case PdmDepth: return 32; case PdmNumColors: @@ -4817,19 +4985,35 @@ void QWidgetPrivate::registerDropSite(bool on) #endif } -void QWidgetPrivate::registerTouchWindow() +void QWidgetPrivate::registerTouchWindow(bool enable) { + Q_UNUSED(enable); +#ifdef QT_MAC_USE_COCOA #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) return; + Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (enable == touchEventsEnabled) return; -#ifndef QT_MAC_USE_COCOA - // Needs implementation! -#else - NSView *view = qt_mac_nativeview_for(q); - [view setAcceptsTouchEvents:YES]; + + QCocoaView *view = static_cast<QCocoaView *>(qt_mac_effectiveview_for(q)); + if (!view) + return; + + if (enable) { + ++view->alienTouchCount; + if (view->alienTouchCount == 1) { + touchEventsEnabled = true; + [view setAcceptsTouchEvents:YES]; + } + } else { + --view->alienTouchCount; + if (view->alienTouchCount == 0) { + touchEventsEnabled = false; + [view setAcceptsTouchEvents:NO]; + } + } #endif #endif } @@ -4837,13 +5021,17 @@ void QWidgetPrivate::registerTouchWindow() void QWidgetPrivate::setMask_sys(const QRegion ®ion) { Q_UNUSED(region); -#ifndef QT_MAC_USE_COCOA Q_Q(QWidget); + +#ifndef QT_MAC_USE_COCOA if (q->isWindow()) ReshapeCustomWindow(qt_mac_window_for(q)); else HIViewReshapeStructure(qt_mac_nativeview_for(q)); #else + if (!q->internalWinId()) + return; + if (extra->mask.isEmpty()) { extra->maskBits = QImage(); finishCocoaMaskSetup(); @@ -4851,6 +5039,7 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) syncCocoaMask(); } + topLevelAt_cache = 0; #endif } @@ -4928,7 +5117,7 @@ void QWidgetPrivate::finishCocoaMaskSetup() [window setOpaque:(extra->imageMask == 0)]; [window invalidateShadow]; } - qt_mac_set_needs_display(q, QRegion()); + macSetNeedsDisplay(QRegion()); } #endif diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index e37e06db89..0ac66cb5f7 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -233,6 +233,7 @@ struct QTLWExtra { #elif defined(Q_WS_QPA) QPlatformWindow *platformWindow; QPlatformWindowFormat platformWindowFormat; + quint32 screenIndex; // index in qplatformscreenlist #endif }; @@ -360,7 +361,8 @@ public: DrawInvisible = 0x08, DontSubtractOpaqueChildren = 0x10, DontSetCompositionMode = 0x20, - DontDrawOpaqueChildren = 0x40 + DontDrawOpaqueChildren = 0x40, + DontDrawNativeChildren = 0x80 }; enum CloseMode { @@ -774,6 +776,8 @@ public: void x11UpdateIsOpaque(); bool isBackgroundInherited() const; void updateX11AcceptFocus(); + QPoint mapToGlobal(const QPoint &pos) const; + QPoint mapFromGlobal(const QPoint &pos) const; #elif defined(Q_WS_WIN) // <--------------------------------------------------------- WIN uint noPaintOnScreen : 1; // see qwidget_win.cpp ::paintEngine() #ifndef QT_NO_GESTURES @@ -792,7 +796,6 @@ public: #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff uint needWindowChange : 1; - uint hasAlienChildren : 1; // Each wiget keeps a list of all its child and grandchild OpenGL widgets. // This list is used to update the gl context whenever a parent and a granparent @@ -823,6 +826,7 @@ public: void macUpdateIgnoreMouseEvents(); void macUpdateMetalAttribute(); void macUpdateIsOpaque(); + void macSetNeedsDisplay(QRegion region); void setEnabled_helper_sys(bool enable); bool isRealWindow() const; void adjustWithinMaxAndMinSize(int &w, int &h); @@ -858,6 +862,7 @@ public: bool isInUnifiedToolbar; QWindowSurface *unifiedSurface; QPoint toolbar_offset; + bool touchEventsEnabled; #endif void determineWindowClass(); void transferChildren(); @@ -870,7 +875,7 @@ public: static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *); static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); - void registerTouchWindow(); + void registerTouchWindow(bool enable = true); #elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS void setMaxWindowState_helper(); void setFullScreenSize_helper(); @@ -885,11 +890,9 @@ public: void updateCursor() const; #endif QScreen* getScreen() const; -#elif defined(Q_WS_QPA) +#elif defined(Q_WS_QPA) // <--------------------------------------------------------- QPA void setMaxWindowState_helper(); void setFullScreenSize_helper(); - - int screenNumber; // screen the widget should be displayed on #ifndef QT_NO_CURSOR void updateCursor() const; #endif diff --git a/src/gui/kernel/qwidget_qpa.cpp b/src/gui/kernel/qwidget_qpa.cpp index 617d984bbd..001810e0f4 100644 --- a/src/gui/kernel/qwidget_qpa.cpp +++ b/src/gui/kernel/qwidget_qpa.cpp @@ -53,7 +53,6 @@ #include <QtGui/QPlatformCursor> QT_BEGIN_NAMESPACE -static QPlatformScreen *qt_screenForWidget(const QWidget *w); void q_createNativeChildrenAndSetParent(QPlatformWindow *parentWindow, const QWidget *parentWidget) { @@ -122,7 +121,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } } - QApplicationPrivate::platformIntegration()->moveToScreen(q, screenNumber); + QApplicationPrivate::platformIntegration()->moveToScreen(q, topData()->screenIndex); // qDebug() << "create_sys" << q << q->internalWinId(); } @@ -163,7 +162,6 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) { Q_Q(QWidget); - // QWidget *oldParent = q->parentWidget(); Qt::WindowFlags oldFlags = data.window_flags; int targetScreen = -1; @@ -173,7 +171,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) // programmer specified desktop widget // get the desktop's screen number - targetScreen = newparent->d_func()->screenNumber; + targetScreen = newparent->window()->d_func()->topData()->screenIndex; newparent = 0; } @@ -192,7 +190,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) f |= Qt::Window; if (targetScreen == -1) { if (parent) - targetScreen = qobject_cast<QWidget *>(parent)->d_func()->screenNumber; + targetScreen = q->parentWidget()->window()->d_func()->topData()->screenIndex; } } @@ -215,20 +213,14 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) data.window_flags = window->setWindowFlags(data.window_flags); } - // Reparenting child to toplevel - if ((f&Qt::Window) && !(oldFlags&Qt::Window)) { - //qDebug() << "setParent_sys() change to toplevel"; - q->create(); //### too early: this ought to happen at show() time - } - - if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden) q->setAttribute(Qt::WA_WState_Hidden); q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); // move the window to the selected screen if (!newparent && targetScreen != -1) { - screenNumber = targetScreen; + if (maybeTopData()) + maybeTopData()->screenIndex = targetScreen; // only if it is already created if (q->testAttribute(Qt::WA_WState_Created)) { QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); @@ -379,9 +371,8 @@ QWidget *QWidget::keyboardGrabber() void QWidget::activateWindow() { - // XXX -// qDebug() << "QWidget::activateWindow" << this; - QApplication::setActiveWindow(this); //##### + if (platformWindow()) + platformWindow()->requestActivateWindow(); } void QWidgetPrivate::show_sys() @@ -397,7 +388,11 @@ void QWidgetPrivate::show_sys() QPlatformWindow *window = q->platformWindow(); if (window) { - const QRect geomRect = q->geometry(); + QRect geomRect = q->geometry(); + if (!q->isWindow()) { + QPoint topLeftOfWindow = q->mapTo(q->nativeParentWidget(),QPoint()); + geomRect.moveTopLeft(topLeftOfWindow); + } const QRect windowRect = window->geometry(); if (windowRect != geomRect) { window->setGeometry(geomRect); @@ -409,9 +404,6 @@ void QWidgetPrivate::show_sys() } if (window) window->setVisible(true); - - if (q->isWindow() && q->windowType() != Qt::Popup && q->windowType() != Qt::ToolTip && !(q->windowFlags() & Qt::X11BypassWindowManagerHint)) - q->activateWindow(); //### QWindowSystemInterface should have callback function for when WS actually activates window. } } @@ -595,7 +587,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (q->isVisible()) { if (q->platformWindow()) { if (q->isWindow()) { - q->platformWindow()->setGeometry(q->frameGeometry()); + q->platformWindow()->setGeometry(q->geometry()); } else { QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); q->platformWindow()->setGeometry(QRect(posInNativeParent,r.size())); @@ -647,37 +639,11 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) scrollRect(r, dx, dy); } -static QPlatformScreen *qt_screenForWidget(const QWidget *w) -{ - if (!w) - return 0; - QRect frame = w->frameGeometry(); - if (!w->isWindow()) - frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); - const QPoint p = (frame.topLeft() + frame.bottomRight()) / 2; - - QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); - QList<QPlatformScreen *> screens = pi->screens(); - - for (int i = 0; i < screens.size(); ++i) { - if (screens[i]->geometry().contains(p)) - return screens[i]; - } - - // Assume screen zero if we have it. - if (!screens.isEmpty()) - return screens[0]; - else - qWarning("qt_screenForWidget: no screens"); - - return 0; -} - int QWidget::metric(PaintDeviceMetric m) const { Q_D(const QWidget); - QPlatformScreen *screen = qt_screenForWidget(this); + QPlatformScreen *screen = QPlatformScreen::platformScreenForWidget(this); if (!screen) { if (m == PdmDpiX || m == PdmDpiY) return 72; @@ -798,9 +764,9 @@ void QWidgetPrivate::deleteTLSysExtra() context->deleteQGLContext(); } } + setWinId(0); delete extra->topextra->platformWindow; extra->topextra->platformWindow = 0; - extra->topextra->backingStore.destroy(); } } diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index cf23981272..1a5417d668 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -444,6 +444,7 @@ static QVector<Atom> getNetWmState(QWidget *w) && actualType == XA_ATOM && actualFormat == 32) { returnValue.resize(bytesLeft / 4); XFree((char*) propertyData); + propertyData = 0; // fetch all data if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0, @@ -458,7 +459,8 @@ static QVector<Atom> getNetWmState(QWidget *w) if (!returnValue.isEmpty()) { memcpy(returnValue.data(), propertyData, returnValue.size() * sizeof(Atom)); } - XFree((char*) propertyData); + if (propertyData) + XFree((char*) propertyData); } return returnValue; @@ -1289,39 +1291,77 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) #endif } - -QPoint QWidget::mapToGlobal(const QPoint &pos) const +QPoint QWidgetPrivate::mapToGlobal(const QPoint &pos) const { - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { - QPoint p = pos + data->crect.topLeft(); + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { + QPoint p = pos + q->data->crect.topLeft(); //cannot trust that !isWindow() implies parentWidget() before create - return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p); + return (q->isWindow() || !q->parentWidget()) ? p : q->parentWidget()->d_func()->mapToGlobal(p); } - int x, y; + int x, y; Window child; - QPoint p = d->mapToWS(pos); - XTranslateCoordinates(X11->display, internalWinId(), - QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), + QPoint p = mapToWS(pos); + XTranslateCoordinates(X11->display, q->internalWinId(), + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), p.x(), p.y(), &x, &y, &child); return QPoint(x, y); } - -QPoint QWidget::mapFromGlobal(const QPoint &pos) const +QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const { - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { //cannot trust that !isWindow() implies parentWidget() before create - QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos); - return p - data->crect.topLeft(); + QPoint p = (q->isWindow() || !q->parentWidget()) ? pos : q->parentWidget()->d_func()->mapFromGlobal(pos); + return p - q->data->crect.topLeft(); } - int x, y; + int x, y; Window child; XTranslateCoordinates(X11->display, - QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), - internalWinId(), pos.x(), pos.y(), &x, &y, &child); - return d->mapFromWS(QPoint(x, y)); + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), + q->internalWinId(), pos.x(), pos.y(), &x, &y, &child); + return mapFromWS(QPoint(x, y)); +} + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos + offset; + + return d->mapToGlobal(pos); +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos - offset; + + return d->mapFromGlobal(pos); } void QWidgetPrivate::updateSystemBackground() diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.cpp b/src/gui/kernel/qwindowsysteminterface_qpa.cpp index bb29cbf228..b6177b023f 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.cpp +++ b/src/gui/kernel/qwindowsysteminterface_qpa.cpp @@ -82,6 +82,12 @@ void QWindowSystemInterface::handleLeaveEvent(QWidget *tlw) QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); } +void QWindowSystemInterface::handleWindowActivated(QWidget *tlw) +{ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + void QWindowSystemInterface::handleGeometryChange(QWidget *tlw, const QRect &newRect) { if (tlw) { diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.h b/src/gui/kernel/qwindowsysteminterface_qpa.h index 1c79f2a257..39c2f79a5c 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa.h @@ -83,6 +83,7 @@ public: static void handleCloseEvent(QWidget *w); static void handleEnterEvent(QWidget *w); static void handleLeaveEvent(QWidget *w); + static void handleWindowActivated(QWidget *w); // Changes to the screen static void handleScreenGeometryChange(int screenIndex); diff --git a/src/gui/kernel/qwindowsysteminterface_qpa_p.h b/src/gui/kernel/qwindowsysteminterface_qpa_p.h index 78e1f33339..3491a1ae1e 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa_p.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa_p.h @@ -54,6 +54,7 @@ public: GeometryChange, Enter, Leave, + ActivatedWindow, Mouse, Wheel, Key, @@ -102,6 +103,14 @@ public: QWeakPointer<QWidget> leave; }; + class ActivatedWindowEvent : public WindowSystemEvent { + public: + ActivatedWindowEvent(QWidget *activatedWindow) + : WindowSystemEvent(ActivatedWindow), activated(activatedWindow) + { } + QWeakPointer<QWidget> activated; + }; + class UserEvent : public WindowSystemEvent { public: UserEvent(QWidget * w, ulong time, EventType t) diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index 47cefe4f59..49a819469e 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -415,8 +415,9 @@ static Bool functor(Display *display, XEvent *event, XPointer arg) status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE), &ret, &format, &nitems, &after, &retval ); if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) { - long *state = (long *)retval; - if (state[0] == WithdrawnState) { + long state = *(long *)retval; + XFree(retval); + if (state == WithdrawnState) { data->clearedWmState = true; return true; } @@ -833,6 +834,8 @@ bool QX11EmbedWidget::x11Event(XEvent *event) XUnmapWindow(x11Info().display(), internalWinId()); } } + if (prop_return) + XFree(prop_return); } } diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index dea3a72805..173fb71c8a 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -1781,7 +1781,7 @@ QColor QColor::toHsl() const color.ct.ahsl.lightness = qRound(lightness * USHRT_MAX); if (qFuzzyIsNull(delta)) { // achromatic case, hue is undefined - color.ct.ahsl.hue = 0; + color.ct.ahsl.hue = USHRT_MAX; color.ct.ahsl.saturation = 0; } else { // chromatic case diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 9928dea781..59042963b0 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,7 +43,9 @@ #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> #include <private/qdrawhelper_x86_p.h> +#ifdef QT_HAVE_ARM_SIMD #include <private/qdrawhelper_arm_simd_p.h> +#endif #include <private/qdrawhelper_neon_p.h> #include <private/qmath_p.h> #include <qmath.h> diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index fddc4d07fc..3d532d2524 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -636,10 +636,14 @@ void QPainterPath::moveTo(const QPointF &p) #ifdef QPP_DEBUG printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif + + if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(p.x()) || qt_is_nan(p.y())) - qWarning("QPainterPath::moveTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -682,10 +686,14 @@ void QPainterPath::lineTo(const QPointF &p) #ifdef QPP_DEBUG printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif + + if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(p.x()) || qt_is_nan(p.y())) - qWarning("QPainterPath::lineTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -737,11 +745,15 @@ void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF & printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n", c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); #endif + + if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y()) + || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(c1.x()) || qt_is_nan(c1.y()) || qt_is_nan(c2.x()) || qt_is_nan(c2.y()) - || qt_is_nan(e.x()) || qt_is_nan(e.y())) - qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -790,10 +802,14 @@ void QPainterPath::quadTo(const QPointF &c, const QPointF &e) printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n", c.x(), c.y(), e.x(), e.y()); #endif + + if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(c.x()) || qt_is_nan(c.y()) || qt_is_nan(e.x()) || qt_is_nan(e.y())) - qWarning("QPainterPath::quadTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -857,11 +873,15 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n", rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength); #endif + + if (!qt_is_finite(rect.x()) && !qt_is_finite(rect.y()) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height()) + || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) { #ifndef QT_NO_DEBUG - if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height()) - || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) - qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (rect.isNull()) return; @@ -960,10 +980,13 @@ QPointF QPainterPath::currentPosition() const */ void QPainterPath::addRect(const QRectF &r) { + if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(r.x()) || qt_is_nan(r.y()) || qt_is_nan(r.width()) || qt_is_nan(r.height())) - qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (r.isNull()) return; @@ -1040,11 +1063,14 @@ void QPainterPath::addPolygon(const QPolygonF &polygon) */ void QPainterPath::addEllipse(const QRectF &boundingRect) { + if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y()) + || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(boundingRect.x()) || qt_is_nan(boundingRect.y()) - || qt_is_nan(boundingRect.width()) || qt_is_nan(boundingRect.height())) - qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (boundingRect.isNull()) return; @@ -2366,10 +2392,12 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p) s >> x; s >> y; Q_ASSERT(type >= 0 && type <= 3); + if (!qt_is_finite(x) || !qt_is_finite(y)) { #ifndef QT_NO_DEBUG - if (qt_is_nan(x) || qt_is_nan(y)) - qWarning("QDataStream::operator>>: Adding a NaN element to path, results are undefined"); + qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it"); #endif + continue; + } QPainterPath::Element elm = { x, y, QPainterPath::ElementType(type) }; p.d_func()->elements.append(elm); } @@ -2895,9 +2923,12 @@ QPointF QPainterPath::pointAtPercent(qreal t) const return QPointF(); } - if (isEmpty()) + if (!d_ptr || d_ptr->elements.size() == 0) return QPointF(); + if (d_ptr->elements.size() == 1) + return d_ptr->elements.at(0); + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp index d0b9239a48..419518ac53 100644 --- a/src/gui/painting/qwindowsurface_raster.cpp +++ b/src/gui/painting/qwindowsurface_raster.cpp @@ -78,6 +78,9 @@ public: #ifdef Q_WS_X11 GC gc; +#ifndef QT_NO_MITSHM + uint needsSync : 1; +#endif #ifndef QT_NO_XRENDER uint translucentBackground : 1; #endif @@ -94,6 +97,9 @@ QRasterWindowSurface::QRasterWindowSurface(QWidget *window, bool setDefaultSurfa d_ptr->translucentBackground = X11->use_xrender && window->x11Info().depth() == 32; #endif +#ifndef QT_NO_MITHSM + d_ptr->needsSync = false; +#endif #endif d_ptr->image = 0; d_ptr->inSetGeometry = false; @@ -116,8 +122,23 @@ QPaintDevice *QRasterWindowSurface::paintDevice() return &d_ptr->image->image; } +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) +void QRasterWindowSurface::syncX() +{ + // delay writing to the backbuffer until we know for sure X is done reading from it + if (d_ptr->needsSync) { + XSync(X11->display, false); + d_ptr->needsSync = false; + } +} +#endif + void QRasterWindowSurface::beginPaint(const QRegion &rgn) { +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + #if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) { #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) @@ -217,13 +238,13 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi if (d_ptr->image->xshmpm) { XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc, br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y()); - XSync(X11->display, False); + d_ptr->needsSync = true; } else if (d_ptr->image->xshmimg) { const QImage &src = d->image->image; br = br.intersected(src.rect()); XShmPutImage(X11->display, widget->handle(), d_ptr->gc, d_ptr->image->xshmimg, br.x(), br.y(), wbr.x(), wbr.y(), br.width(), br.height(), False); - XSync(X11->display, False); + d_ptr->needsSync = true; } else #endif { @@ -282,6 +303,7 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi CGrafPtr port = GetWindowPort(qt_mac_window_for(widget)); QDBeginCGContext(port, &context); #else + QMacCocoaAutoReleasePool pool; extern CGContextRef qt_mac_graphicsContextFor(QWidget *); CGContextRef context = qt_mac_graphicsContextFor(widget); #endif @@ -391,6 +413,10 @@ bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy) if (!d->image || d->image->image.isNull()) return false; +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + const QVector<QRect> rects = area.rects(); for (int i = 0; i < rects.size(); ++i) qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy)); diff --git a/src/gui/painting/qwindowsurface_raster_p.h b/src/gui/painting/qwindowsurface_raster_p.h index b6e61e1435..903810b936 100644 --- a/src/gui/painting/qwindowsurface_raster_p.h +++ b/src/gui/painting/qwindowsurface_raster_p.h @@ -107,6 +107,9 @@ public: bool scroll(const QRegion &area, int dx, int dy); private: +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + void syncX(); +#endif void prepareBuffer(QImage::Format format, QWidget *widget); Q_DECLARE_PRIVATE(QRasterWindowSurface) QScopedPointer<QRasterWindowSurfacePrivate> d_ptr; diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index c5e95f2e29..ecc2539f85 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -96,7 +96,8 @@ const layoutHeader QS60StylePrivate::m_layoutHeaders[] = { {320,240,1,19,"QVGA Portrait"}, {360,640,1,19,"NHD Landscape"}, {640,360,1,19,"NHD Portrait"}, -{352,800,1,12,"E90 Landscape"} +{352,800,1,12,"E90 Landscape"}, +{480,640,1,19,"VGA Landscape"} // *** End of generated data *** }; const int QS60StylePrivate::m_numberOfLayouts = @@ -108,7 +109,8 @@ const short QS60StylePrivate::data[][MAX_PIXELMETRICS] = { {5,0,-909,0,0,1,0,2,-1,8,14,22,15,15,7,164,-909,-909,-909,19,15,2,0,0,21,8,27,28,4,4,1,-909,-909,0,7,6,0,13,23,17,17,21,21,7,115,21,0,-909,-909,-909,-909,0,0,15,1,-909,0,0,-909,15,-909,-909,-909,-909,32,21,65,27,65,3,3,5,10,15,-909,5,58,13,5,0,4,4,7,9,4,4,-909,2,-909,-909,-909,-909,6,6,3,1,106}, {7,0,-909,0,0,2,0,5,-1,25,69,46,37,37,9,258,-909,-909,-909,23,19,26,0,0,32,25,72,44,5,5,2,-909,-909,0,7,21,0,17,29,22,22,27,27,7,173,29,0,-909,-909,-909,-909,0,0,25,2,-909,0,0,-909,25,-909,-909,-909,-909,87,27,77,35,77,13,3,6,8,19,-909,7,74,19,7,0,5,5,8,12,5,5,-909,3,-909,-909,-909,-909,7,7,3,1,135}, {7,0,-909,0,0,2,0,5,-1,25,68,46,37,37,9,258,-909,-909,-909,31,19,6,0,0,32,25,60,52,5,5,2,-909,-909,0,7,32,0,17,29,22,22,27,27,7,173,29,0,-909,-909,-909,-909,0,0,26,2,-909,0,0,-909,26,-909,-909,-909,-909,87,27,96,35,96,12,3,6,8,19,-909,7,74,22,7,0,5,5,8,12,5,5,-909,3,-909,-909,-909,-909,7,7,3,1,135}, -{7,0,-909,0,0,2,0,2,-1,10,20,27,18,18,9,301,-909,-909,-909,29,18,5,0,0,35,7,32,30,5,5,2,-909,-909,0,2,8,0,16,28,21,21,26,26,2,170,26,0,-909,-909,-909,-909,0,0,21,6,-909,0,0,-909,-909,-909,-909,-909,-909,54,26,265,34,265,5,5,6,3,18,-909,7,72,19,7,0,5,6,8,11,6,5,-909,2,-909,-909,-909,-909,5,5,3,1,106} +{7,0,-909,0,0,2,0,2,-1,10,20,27,18,18,9,301,-909,-909,-909,29,18,5,0,0,35,7,32,30,5,5,2,-909,-909,0,2,8,0,16,28,21,21,26,26,2,170,26,0,-909,-909,-909,-909,0,0,21,6,-909,0,0,-909,-909,-909,-909,-909,-909,54,26,265,34,265,5,5,6,3,18,-909,7,72,19,7,0,5,6,8,11,6,5,-909,2,-909,-909,-909,-909,5,5,3,1,106}, +{9,0,-909,0,0,2,0,5,-1,34,99,76,51,51,25,352,-909,-909,-909,29,25,7,0,0,43,34,42,76,7,7,2,-909,-909,0,9,14,0,23,39,30,30,37,37,9,391,40,0,-909,-909,-909,-909,0,0,29,2,-909,0,0,-909,29,-909,-909,-909,-909,115,37,96,48,96,19,19,9,1,25,-909,9,101,24,9,0,7,7,7,16,7,7,-909,3,-909,-909,-909,-909,9,9,3,1,184} // *** End of generated data *** }; diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 04c40aa8c1..605872ee46 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -1378,12 +1378,13 @@ QPixmap QS60StylePrivate::frame(SkinFrameElements frame, const QSize &size, Skin QPixmap QS60StylePrivate::backgroundTexture() { bool createNewBackground = false; + TRect applicationRect = (static_cast<CEikAppUi*>(S60->appUi())->ApplicationRect()); if (!m_background) { createNewBackground = true; } else { //if background brush does not match screensize, re-create it - if (m_background->width() != S60->screenWidthInPixels || - m_background->height() != S60->screenHeightInPixels) { + if (m_background->width() != applicationRect.Width() || + m_background->height() != applicationRect.Height()) { delete m_background; createNewBackground = true; } @@ -1391,7 +1392,7 @@ QPixmap QS60StylePrivate::backgroundTexture() if (createNewBackground) { QPixmap background = part(QS60StyleEnums::SP_QsnBgScreen, - QSize(S60->screenWidthInPixels, S60->screenHeightInPixels), 0, SkinElementFlags()); + QSize(applicationRect.Width(), applicationRect.Height()), 0, SkinElementFlags()); m_background = new QPixmap(background); } return *m_background; @@ -1411,7 +1412,6 @@ QS60Style::QS60Style() void QS60StylePrivate::handleDynamicLayoutVariantSwitch() { clearCaches(QS60StylePrivate::CC_LayoutChange); - setBackgroundTexture(qApp); setActiveLayout(); refreshUI(); foreach (QWidget *widget, QApplication::allWidgets()) diff --git a/src/gui/text/qfont_qpa.cpp b/src/gui/text/qfont_qpa.cpp index 5fed18bc5e..7b09b59981 100644 --- a/src/gui/text/qfont_qpa.cpp +++ b/src/gui/text/qfont_qpa.cpp @@ -90,7 +90,7 @@ QString QFont::defaultFamily() const familyName = QString::fromLatin1("helvetica"); } - QStringList list = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(familyName,QFont::StyleNormal,QUnicodeTables::Common); + QStringList list = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(familyName,QFont::StyleNormal,QFont::StyleHint(d->request.styleHint),QUnicodeTables::Common); if (list.size()) { familyName = list.at(0); } diff --git a/src/gui/text/qfont_s60.cpp b/src/gui/text/qfont_s60.cpp index c7d36efdd5..114191d765 100644 --- a/src/gui/text/qfont_s60.cpp +++ b/src/gui/text/qfont_s60.cpp @@ -49,13 +49,14 @@ QT_BEGIN_NAMESPACE #ifdef QT_NO_FREETYPE Q_GLOBAL_STATIC(QMutex, lastResortFamilyMutex); +#endif // QT_NO_FREETYPE + extern QStringList qt_symbian_fontFamiliesOnFontServer(); // qfontdatabase_s60.cpp Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, fontFamiliesOnFontServer, { // We are only interested in the initial font families. No Application fonts. // Therefore, we are allowed to cache the list. x->append(qt_symbian_fontFamiliesOnFontServer()); }); -#endif // QT_NO_FREETYPE QString QFont::lastResortFont() const { @@ -95,7 +96,20 @@ QString QFont::lastResortFamily() const const bool isJapaneseOrChineseSystem = User::Language() == ELangJapanese || User::Language() == ELangPrcChinese; - return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":"Series 60 Sans"); + static QString family; + if (family.isEmpty()) { + QStringList families = qt_symbian_fontFamiliesOnFontServer(); + const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"}; + for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) { + const QString preferredFamily = QLatin1String(preferredFamilies[i]); + if (families.contains(preferredFamily)) { + family = preferredFamily; + break; + } + } + } + + return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1()); #endif // QT_NO_FREETYPE } diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 20c72dde6b..8e92b1a23f 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -646,6 +646,10 @@ public: { } ~QFontDatabasePrivate() { free(); +#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) + if (symbianExtras) + delete symbianExtras; +#endif } QtFontFamily *family(const QString &f, bool = false); void free() { @@ -654,12 +658,6 @@ public: ::free(families); families = 0; count = 0; -#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) - if (symbianExtras) { - delete symbianExtras; - symbianExtras = 0; - } -#endif // don't clear the memory fonts! } @@ -678,6 +676,10 @@ public: QVector<FONTSIGNATURE> signatures; #elif defined(Q_WS_MAC) ATSFontContainerRef handle; +#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) + QString temporaryFileName; + TInt screenDeviceFontFileId; + TUid fontStoreFontFileUid; #endif QStringList families; }; @@ -704,7 +706,7 @@ public: #if defined(Q_WS_QWS) QDataStream *stream; #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) - const QSymbianFontDatabaseExtras *symbianExtras; + QSymbianFontDatabaseExtras *symbianExtras; #endif #if defined(Q_WS_QWS) || defined(Q_WS_QPA) QStringList fallbackFamilies; @@ -2573,6 +2575,8 @@ bool QFontDatabasePrivate::isApplicationFont(const QString &fileName) \note Adding application fonts on Unix/X11 platforms without fontconfig is currently not supported. + \note On Symbian, the font family names get truncated to a length of 20 characters. + \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont() */ int QFontDatabase::addApplicationFont(const QString &fileName) @@ -2603,6 +2607,8 @@ int QFontDatabase::addApplicationFont(const QString &fileName) \bold{Note:} Adding application fonts on Unix/X11 platforms without fontconfig is currently not supported. + \note On Symbian, the font family names get truncated to a length of 20 characters. + \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont() */ int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData) diff --git a/src/gui/text/qfontdatabase_qpa.cpp b/src/gui/text/qfontdatabase_qpa.cpp index e54093ce2e..e6d99c667c 100644 --- a/src/gui/text/qfontdatabase_qpa.cpp +++ b/src/gui/text/qfontdatabase_qpa.cpp @@ -80,9 +80,9 @@ Q_GUI_EXPORT void qt_registerFont(const QString &familyName, const QString &fou size->handle = handle; } -static QStringList fallbackFamilies(const QString &family, const QFont::Style &style, const QUnicodeTables::Script &script) +static QStringList fallbackFamilies(const QString &family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) { - QStringList retList = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,script); + QStringList retList = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script); QFontDatabasePrivate *db = privateDb(); QStringList::iterator i; @@ -177,7 +177,11 @@ QFontEngine *loadEngine(int script, const QFontDef &request, && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol ) { if (family && !family->askedForFallback) { - family->fallbackFamilies = fallbackFamilies(family->name,QFont::Style(style->key.style),QUnicodeTables::Script(script)); + QFont::Style fontStyle = QFont::Style(style->key.style); + QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint); + if (styleHint == QFont::AnyStyle && request.fixedPitch) + styleHint = QFont::TypeWriter; + family->fallbackFamilies = fallbackFamilies(family->name,fontStyle,styleHint,QUnicodeTables::Script(script)); family->askedForFallback = true; } @@ -287,7 +291,7 @@ QFontDatabase::findFont(int script, const QFontPrivate *fp, if (!engine) { if (!request.family.isEmpty()) { - QStringList fallbacks = fallbackFamilies(request.family,QFont::Style(request.style),QUnicodeTables::Script(script)); + QStringList fallbacks = fallbackFamilies(request.family,QFont::Style(request.style),QFont::StyleHint(request.styleHint),QUnicodeTables::Script(script)); for (int i = 0; i < fallbacks.size(); i++) { QFontDef def = request; def.family = fallbacks.at(i); diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index 6ba035e91f..6d3970eb8a 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -45,6 +45,8 @@ #include "qfontengine_s60_p.h" #include "qabstractfileengine.h" #include "qdesktopservices.h" +#include "qtemporaryfile.h" +#include "qtextcodec.h" #include <private/qpixmap_s60_p.h> #include <private/qt_s60_p.h> #include "qendian.h" @@ -114,7 +116,14 @@ public: ~QSymbianFontDatabaseExtrasImplementation(); const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const; - void addFontFileToFontStore(const QFileInfo &fontFileInfo); + void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt); + static inline bool appFontLimitReached(); + TUid addFontFileToFontStore(const QFileInfo &fontFileInfo); + static void clear(); + + static inline QString tempAppFontFolder(); + static const QString appFontMarkerPrefix; + static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>' struct CFontFromFontStoreReleaser { static inline void cleanup(CFont *font) @@ -144,8 +153,75 @@ public: mutable QList<const QSymbianTypeFaceExtras *> m_extras; mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash; + mutable QSet<QString> m_applicationFontFamilies; }; +const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix = + QLatin1String("Q"); + +inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() +{ + return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\'); +} + +QString QSymbianFontDatabaseExtrasImplementation::appFontMarker() +{ + static QString result; + if (result.isEmpty()) { + quint16 id = 0; + if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + // We are allowed to load app fonts even from previous, crashed runs + // of this application, since we can access the font tables. + const quint32 uid = RProcess().Type().MostDerived().iUid; + id = static_cast<quint16>(uid + (uid >> 16)); + } else { + // If no font table Api is available, we must not even load a font + // from a previous (crashed) run of this application. Reason: we + // won't get the font tables, they are not in the CFontStore. + // So, we use the pid, for more uniqueness. + id = static_cast<quint16>(RProcess().Id().Id()); + } + result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0')); + Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4); + } + return result; +} + +static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName) +{ + const int idLength = 3; // Keep in sync with id length in appFontMarker(). + const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix; + if (fontName.length() < prefix.length() + idLength + || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix) + return false; + // Testing if the the id is base32 data + for (int i = fontName.length() - idLength; i < fontName.length(); ++i) { + const QChar &c = fontName.at(i); + if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9') + || c >= QLatin1Char('a') && c <= QLatin1Char('v'))) + return false; + } + return true; +} + +// If fontName is an application font of this app, prepend the app font marker +QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName) +{ + QFontDatabasePrivate *db = privateDb(); + Q_ASSERT(db); + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + return dbExtras->m_applicationFontFamilies.contains(fontName) ? + fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + : fontName; +} + +static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName) +{ + return markedFontName.left(markedFontName.length() + - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length()); +} + QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation() { if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { @@ -170,10 +246,13 @@ QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementati } } -void qt_cleanup_symbianFontDatabaseExtras() +void QSymbianFontDatabaseExtrasImplementation::clear() { + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras); + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); if (!dbExtras) return; // initializeDb() has never been called if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { @@ -189,9 +268,32 @@ void qt_cleanup_symbianFontDatabaseExtras() dbExtras->m_extrasHash.clear(); } +void qt_cleanup_symbianFontDatabase() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!db->applicationFonts.isEmpty()) { + QFontDatabase::removeAllApplicationFonts(); + // We remove the left over temporary font files of Qt application. + // Active fonts are undeletable since the font server holds a handle + // on them, so we do not need to worry to delete other running + // applications' fonts. + const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()); + const QStringList filter( + QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf")); + foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter)) + QFile(ttfFile.absoluteFilePath()).remove(); + db->applicationFonts.clear(); + } +} + QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation() { - qt_cleanup_symbianFontDatabaseExtras(); + qt_cleanup_symbianFontDatabase(); if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { delete m_store; m_heap->Close(); @@ -215,9 +317,10 @@ COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont) } #endif // FNTSTORE_H_INLINES_SUPPORT_FMM -const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &typeface, +const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface, bool bold, bool italic) const { + const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface); const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic)); if (!m_extrasHash.contains(searchKey)) { TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1); @@ -263,12 +366,42 @@ const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(c return m_extrasHash.value(searchKey); } -void QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) +void QSymbianFontDatabaseExtrasImplementation::removeAppFontData( + QFontDatabasePrivate::ApplicationFont *fnt) +{ + clear(); + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable() + && fnt->fontStoreFontFileUid.iUid != 0) + m_store->RemoveFile(fnt->fontStoreFontFileUid); + if (!fnt->families.isEmpty()) + m_applicationFontFamilies.remove(fnt->families.first()); + if (fnt->screenDeviceFontFileId != 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); +} + +bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return false; + const int maxAppFonts = 5; + int registeredAppFonts = 0; + foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts) + if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts) + return true; + return false; +} + +TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) { Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()); const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath()); - TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); - QT_TRAP_THROWING(m_store->AddFileL(fontFilePtr)); + const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); + TUid fontUid = {0}; + TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr)); + return fontUid; } #else // QT_NO_FREETYPE @@ -331,11 +464,24 @@ void QFontEngineMultiS60::loadEngine(int at) Q_ASSERT(engines[at]); } -static bool addFontToScreenDevice(int screenDeviceFontIndex, - const QSymbianFontDatabaseExtrasImplementation *dbExtras) -{ +#ifdef QT_NO_FREETYPE +static bool registerScreenDeviceFont(int screenDeviceFontIndex, + const QSymbianFontDatabaseExtrasImplementation *dbExtras) +{ TTypefaceSupport typefaceSupport; S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex); + + QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); + if (qt_symbian_fontNameHasAppFontMarker(familyName)) { + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + if (familyName.endsWith(marker)) { + familyName = qt_symbian_appFontNameWithoutMarker(familyName); + dbExtras->m_applicationFontFamilies.insert(familyName); + } else { + return false; // This was somebody else's application font. Skip it. + } + } + CFont *font; // We have to get a font instance in order to know all the details TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11); if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone) @@ -351,7 +497,6 @@ static bool addFontToScreenDevice(int screenDeviceFontIndex, styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal; styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal; - QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); QtFontFamily *family = privateDb()->family(familyName, true); family->fixedPitch = faceAttrib.IsMonoWidth(); QtFontFoundry *foundry = family->foundry(QString(), true); @@ -381,6 +526,7 @@ static bool addFontToScreenDevice(int screenDeviceFontIndex, family->writingSystems[system] = QtFontFamily::Supported; return true; } +#endif static void initializeDb() { @@ -398,7 +544,12 @@ static void initializeDb() const QSymbianFontDatabaseExtrasImplementation *dbExtras = static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); for (int i = 0; i < numTypeFaces; i++) - addFontToScreenDevice(i, dbExtras); + registerScreenDeviceFont(i, dbExtras); + + // We have to clear/release all CFonts, here, in case one of the fonts is + // an application font of another running Qt app. Otherwise the other Qt app + // cannot remove it's application font, anymore -> "Zombie Font". + QSymbianFontDatabaseExtrasImplementation::clear(); lock.relock(); @@ -421,20 +572,386 @@ static inline void load(const QString &family = QString(), int script = -1) initializeDb(); } +struct OffsetTable { + quint32 sfntVersion; + quint16 numTables, searchRange, entrySelector, rangeShift; +}; + +struct TableRecord { + quint32 tag, checkSum, offset, length; +}; + +struct NameTableHead { + quint16 format, count, stringOffset; +}; + +struct NameRecord { + quint16 platformID, encodingID, languageID, nameID, length, offset; +}; + +static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount) +{ + quint32 result = 0; + const quint32 *ptr = reinterpret_cast<const quint32*>(data); + const quint32 *endPtr = + ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32); + while (ptr < endPtr) { + const quint32 unit32Value = *ptr++; + result += qFromBigEndian(unit32Value); + } + return result; +} + +static inline quint32 toDWordBoundary(quint32 value) +{ + return (value + 3) & ~3; +} + +static inline quint32 dWordPadding(quint32 value) +{ + return (4 - (value & 3)) & 3; +} + +static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker) +{ + const quint32 tableLength = static_cast<quint32>(table.size()); + + if (tableLength > 50000 // hard limit + || tableLength < sizeof(NameTableHead)) // corrupt name table + return false; + + const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData()); + const quint16 count = qFromBigEndian(head->count); + const quint16 stringOffset = qFromBigEndian(head->stringOffset); + if (count > 200 // hard limit + || stringOffset >= tableLength // corrupt name table + || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table + return false; + + QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader); + const QByteArray markerUtf16BE = encoder.fromUnicode(marker); + const QByteArray markerAscii = marker.toAscii(); + + QByteArray markedTable; + markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra + markedTable.append(table, stringOffset); + QByteArray markedStrings; + quint32 stringDataCount = stringOffset; + for (quint16 i = 0; i < count; ++i) { + const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i; + NameRecord *nameRecord = + reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset); + const quint16 nameID = qFromBigEndian(nameRecord->nameID); + const quint16 platformID = qFromBigEndian(nameRecord->platformID); + const quint16 encodingID = qFromBigEndian(nameRecord->encodingID); + const quint16 offset = qFromBigEndian(nameRecord->offset); + const quint16 length = qFromBigEndian(nameRecord->length); + stringDataCount += length; + if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string. + || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds + return false; + const bool needsMarker = + nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21; + const bool isUnicode = + platformID == 0 || platformID == 3 && encodingID == 1; + const QByteArray originalString = + QByteArray::fromRawData(table.constData() + stringOffset + offset, length); + QByteArray markedString; + if (needsMarker) { + const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1); + markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii); + } else { + markedString = originalString; + } + nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length())); + nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length())); + markedStrings.append(markedString); + } + markedTable.append(markedStrings); + table = markedTable; + return true; +} + +const quint32 ttfMaxFileSize = 3500000; + +static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker) +{ + const quint32 ttfChecksumNumber = 0xb1b0afba; + const quint32 alignment = 4; + const quint32 ttfLength = static_cast<quint32>(ttf.size()); + if (ttfLength > ttfMaxFileSize // hard limit + || ttfLength % alignment != 0 // ttf sizes are always factors of 4 + || ttfLength <= sizeof(OffsetTable) // ttf too short + || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid + return false; + + const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData()); + const quint16 numTables = qFromBigEndian(offsetTable->numTables); + const quint32 recordsLength = + toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord)); + if (numTables > 30 // hard limit + || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty. + return false; + + QByteArray markedTtf; + markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra + markedTtf.append(ttf.constData(), recordsLength); + + const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head' + int indexOfHeadTable = -1; + quint32 ttfDataSize = recordsLength; + typedef QPair<quint32, quint32> Range; + QList<Range> memoryRanges; + memoryRanges.reserve(numTables); + for (int i = 0; i < numTables; ++i) { + TableRecord *tableRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + const quint32 lengthAligned = toDWordBoundary(length); + ttfDataSize += lengthAligned; + if (offset < recordsLength // must not intersect ttf header/records + || offset % alignment != 0 // must be aligned + || offset > ttfLength - alignment // table out of bounds + || offset + lengthAligned > ttfLength // table out of bounds + || ttfDataSize > ttfLength) // tables would not fit into the ttf + return false; + + foreach (const Range &range, memoryRanges) + if (offset < range.first + range.second && offset + lengthAligned > range.first) + return false; // Overlaps with another table + memoryRanges.append(Range(offset, lengthAligned)); + + quint32 checkSum = qFromBigEndian(tableRecord->checkSum); + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) { + if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32)) + return false; // Invalid 'head' table + const quint32 *checkSumAdjustmentTag = + reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset); + const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag); + checkSum += checkSumAdjustment; + indexOfHeadTable = i; // For the ttf checksum re-calculation, later + } + if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length)) + return false; // Table checksum is invalid + + bool updateTableChecksum = false; + QByteArray table; + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) { + table = QByteArray(ttf.constData() + offset, length); + if (!ttfMarkNameTable(table, marker)) + return false; // Name table was not markable. + updateTableChecksum = true; + } else { + table = QByteArray::fromRawData(ttf.constData() + offset, length); + } + + tableRecord->offset = qToBigEndian(markedTtf.size()); + tableRecord->length = qToBigEndian(table.size()); + markedTtf.append(table); + markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding + if (updateTableChecksum) { + TableRecord *tableRecord = // Need to recalculate, since markedTtf changed + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length)); + } + } + if (indexOfHeadTable == -1 // 'head' table is mandatory + || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian. + return false; + TableRecord *headRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord)); + quint32 *checkSumAdjustmentTag = + reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset); + *checkSumAdjustmentTag = 0; + const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count()); + *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum); + ttf = markedTtf; + return true; +} + +static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName) +{ + bool result = false; + QString ttfFileName; + QFile tempFileGuard; + QFileInfo info(fileName); + if (!data.isEmpty()) { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open() || tempfile.write(data) == -1) + return false; + ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath()); + tempfile.setAutoRemove(false); + tempfile.close(); + tempFileGuard.setFileName(ttfFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return false; + } else if (info.isFile()) { + ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath()); + } else { + return false; + } + + CFontStore *store = 0; + RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000); + if (heap) { + QT_TRAP_THROWING( + CleanupClosePushL(*heap); + store = CFontStore::NewL(heap); + CleanupStack::PushL(store); + COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); + CleanupStack::PushL(rasterizer); + store->InstallRasterizerL(rasterizer); + CleanupStack::Pop(rasterizer); + TUid fontUid = {-1}; + TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName))); + if (fontUid.iUid != -1) + result = true; + CleanupStack::PopAndDestroy(2, heap); // heap, store + ); + } + + if (tempFileGuard.isOpen()) + tempFileGuard.remove(); + + return result; +} + static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) { - Q_UNUSED(fnt); + if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() + || fnt->data.size() > ttfMaxFileSize // hard limit + || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf + || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit + return; + +// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower). +// Therefore, not using it for now, but eventually in a later version. +// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName)) +// return; + + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + if (!db->count) + initializeDb(); + + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return; + + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + + // The QTemporaryFile object being used in the following section must be + // destructed before letting Symbian load the TTF file. Symbian would not + // load it otherwise, because QTemporaryFile will still keep some handle + // on it. The scope is used to reduce the life time of the QTemporaryFile. + // In order to prevent other processes from modifying the file between the + // moment where the QTemporaryFile is destructed and the file is loaded by + // Symbian, we have a QFile "tempFileGuard" outside the scope which opens + // the file in ReadOnly mode while the QTemporaryFile is still alive. + QFile tempFileGuard; + { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + marker + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open()) + return; + const QString tempFileName = QFileInfo(tempfile).canonicalFilePath(); + if (fnt->data.isEmpty()) { + QFile sourceFile(fnt->fileName); + if (!sourceFile.open(QIODevice::ReadOnly)) + return; + fnt->data = sourceFile.readAll(); + } + if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1) + return; + tempfile.setAutoRemove(false); + tempfile.close(); // Tempfile still keeps a file handle, forbidding write access + fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore. + tempFileGuard.setFileName(tempFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return; + fnt->temporaryFileName = tempFileName; + } + + const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName); + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer(); + const TInt err = + S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId); + tempFileGuard.close(); // Did its job + const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer(); + if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device? + int fontOnServerIndex = fontsOnServerAfter.count() - 1; + for (int i = 0; i < fontsOnServerBefore.count(); i++) { + if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) { + fontOnServerIndex = i; + break; + } + } + + // Must remove all font engines with their CFonts, first. + QFontCache::instance()->clear(); + db->free(); + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) + fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName)); + + const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex); + fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName)); + if (!qt_symbian_fontNameHasAppFontMarker(appFontName) + || !registerScreenDeviceFont(fontOnServerIndex, dbExtras)) + dbExtras->removeAppFontData(fnt); + } else { + if (fnt->screenDeviceFontFileId > 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open! + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); + } + lock.relock(); } bool QFontDatabase::removeApplicationFont(int handle) { - Q_UNUSED(handle); - return false; + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (!db || handle < 0 || handle >= db->applicationFonts.count()) + return false; + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return false; + + QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle]; + if (fnt->families.isEmpty()) + return true; // Nothing to remove. Return peacefully. + + // Must remove all font engines with their CFonts, first + QFontCache::instance()->clear(); + db->free(); + dbExtras->removeAppFontData(fnt); + + db->invalidate(); // This will just emit 'fontDatabaseChanged()' + return true; } bool QFontDatabase::removeAllApplicationFonts() { - return false; + QMutexLocker locker(fontDatabaseMutex()); + + const int applicationFontsCount = privateDb()->applicationFonts.count(); + for (int i = 0; i < applicationFontsCount; ++i) + if (!removeApplicationFont(i)) + return false; + return true; } bool QFontDatabase::supportsThreadedFontRendering() @@ -467,7 +984,7 @@ QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QF QFontDatabasePrivate *db = privateDb(); QtFontDesc desc; QList<int> blacklistedFamilies; - match(script, req, req.family, QString(), -1, &desc, blacklistedFamilies); + match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies); if (!desc.family) // falling back to application font desc.family = db->family(QApplication::font().defaultFamily()); Q_ASSERT(desc.family); diff --git a/src/gui/text/qfontengine_s60.cpp b/src/gui/text/qfontengine_s60.cpp index 4317ee6a3c..e9b54e350f 100644 --- a/src/gui/text/qfontengine_s60.cpp +++ b/src/gui/text/qfontengine_s60.cpp @@ -244,10 +244,13 @@ static inline unsigned int getChar(const QChar *str, int &i, const int len) return uc; } +extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp + CFont *QFontEngineS60::fontWithSize(qreal size) const { CFont *result = 0; - TFontSpec fontSpec(qt_QString2TPtrC(QFontEngine::fontDef.family), TInt(size)); + const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family); + TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size)); fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap); fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic); fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal); diff --git a/src/gui/text/qplatformfontdatabase_qpa.cpp b/src/gui/text/qplatformfontdatabase_qpa.cpp index d6dff413c0..afe762a109 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.cpp +++ b/src/gui/text/qplatformfontdatabase_qpa.cpp @@ -221,10 +221,11 @@ QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, QUnicode /*! */ -QStringList QPlatformFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const +QStringList QPlatformFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const { Q_UNUSED(family); Q_UNUSED(style); + Q_UNUSED(styleHint); Q_UNUSED(script); return QStringList(); } diff --git a/src/gui/text/qplatformfontdatabase_qpa.h b/src/gui/text/qplatformfontdatabase_qpa.h index aa465ab9ca..a1faea928f 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.h +++ b/src/gui/text/qplatformfontdatabase_qpa.h @@ -88,7 +88,7 @@ class Q_GUI_EXPORT QPlatformFontDatabase public: virtual void populateFontDatabase(); virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); - virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const; + virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual void releaseHandle(void *handle); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 498a43276e..2172f74751 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -663,7 +663,8 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O Q_ASSERT(blocks.length() == fragments.length()); - finishEdit(); + if (!blockCursorAdjustment) + finishEdit(); } void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op) @@ -678,6 +679,7 @@ void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operati curs->changed = true; } } + finishEdit(); } void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode) diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index daafdd9db4..d3e8f2d26d 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -147,8 +147,8 @@ symbian { text/qfontengine_s60.cpp HEADERS += \ text/qfontengine_s60_p.h - LIBS += -lfntstr -lecom } + LIBS += -lfntstr -lecom } !qpa { diff --git a/src/gui/util/qscroller.cpp b/src/gui/util/qscroller.cpp index 2ca4a6e4b8..d60f44e96d 100644 --- a/src/gui/util/qscroller.cpp +++ b/src/gui/util/qscroller.cpp @@ -83,9 +83,9 @@ bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); QDebug &operator<<(QDebug &dbg, const QScrollerPrivate::ScrollSegment &s) { - dbg << "\n Time: start:" << s.startTime << " duration:" << s.deltaTime; - dbg << "\n Pos: start:" << s.startPos << " delta:" << s.deltaPos; - dbg << "\n Curve: type:" << s.curve.type() << " max progress:" << s.maxProgress << "\n"; + dbg << "\n Time: start:" << s.startTime << " duration:" << s.deltaTime << " stop progress:" << s.stopProgress; + dbg << "\n Pos: start:" << s.startPos << " delta:" << s.deltaPos << " stop:" << s.stopPos; + dbg << "\n Curve: type:" << s.curve.type() << "\n"; return dbg; } @@ -243,7 +243,7 @@ private: intuitive user experience. The QScroller object is the object that stores the current position and - speed of the scrolling and takes care of updates. + scrolling speed and takes care of updates. QScroller can be triggered by a flick gesture \code @@ -259,20 +259,20 @@ private: scroller->scrollTo(QPointF(100, 100)); \endcode - The scrolled QObjects will be receive a QScrollPrepareEvent whenever the scroller needs to + The scrolled QObjects receive a QScrollPrepareEvent whenever the scroller needs to update its geometry information and a QScrollEvent whenever the content of the object should actually be scrolled. - The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents, but this - can be slowed down with QScrollerProperties::FrameRate on a per-QScroller basis. + The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents. This + can be changed with QScrollerProperties::FrameRate on a per-QScroller basis. Several examples in the \c scroller examples directory show how QScroller, QScrollEvent and the scroller gesture can be used. - Even though this kinetic scroller has a huge number of settings available via + Even though this kinetic scroller has a large number of settings available via QScrollerProperties, we recommend that you leave them all at their default, platform optimized - values. In case you really want to change them you can experiment with the \c plot example in - the \c scroller examples directory first. + values. Before changing them you can experiment with the \c plot example in + the \c scroller examples directory. \sa QScrollEvent, QScrollPrepareEvent, QScrollerProperties */ @@ -293,9 +293,9 @@ bool QScroller::hasScroller(QObject *target) /*! Returns the scroller for the given \a target. - As long as the object exist this function will always return the same QScroller. - If a QScroller does not exist yet for the \a target, it will implicitly be created. - At no point will two QScrollers be active on one object. + As long as the object exists this function will always return the same QScroller instance. + If no QScroller exists for the \a target, one will implicitly be created. + At no point more than one QScroller will be active on an object. \sa hasScroller(), target() */ @@ -324,9 +324,9 @@ const QScroller *QScroller::scroller(const QObject *target) } /*! - Returns an application wide list of currently active, i.e. state() != - QScroller::Inactive, QScroller objects. - This routine is mostly useful when writing your own gesture recognizer. + Returns an application wide list of currently active QScroller objects. + Active QScroller objects are in a state() that is not QScroller::Inactive. + This function is useful when writing your own gesture recognizer. */ QList<QScroller *> QScroller::activeScrollers() { @@ -346,8 +346,8 @@ QObject *QScroller::target() const /*! \fn QScroller::scrollerPropertiesChanged(const QScrollerProperties &newProperties); - QScroller emits this signal whenever its scroller properties have been - changed. \a newProperties are the new scroller properties. + QScroller emits this signal whenever its scroller properties change. + \a newProperties are the new scroller properties. \sa scrollerProperties */ @@ -355,7 +355,7 @@ QObject *QScroller::target() const /*! \property QScroller::scrollerProperties \brief The scroller properties of this scroller. - The properties will be used by the QScroller to determine its scrolling behaviour. + The properties are used by the QScroller to determine its scrolling behavior. */ QScrollerProperties QScroller::scrollerProperties() const { @@ -378,24 +378,25 @@ void QScroller::setScrollerProperties(const QScrollerProperties &sp) /*! - Registers a custom scroll gesture recognizer and grabs it for the \a + Registers a custom scroll gesture recognizer, grabs it for the \a target and returns the resulting gesture type. If \a scrollGestureType is - set to TouchGesture the gesture will trigger on touch events - if set to + set to TouchGesture the gesture triggers on touch events. If it is set to one of LeftMouseButtonGesture, RightMouseButtonGesture or - MiddleMouseButtonGesture it will trigger on mouse events of the + MiddleMouseButtonGesture it triggers on mouse events of the corresponding button. Only one scroll gesture can be active on a single object at the same - time, so if you call this function twice on the same object, it will + time. If you call this function twice on the same object, it will ungrab the existing gesture before grabbing the new one. - Please note: To avoid nasty side-effects, all mouse events will be - consumed while the gesture is triggered. Since the mouse press event is - not consumed, the gesture needs to also send a fake mouse release event - at the global position \c{(INT_MIN, INT_MIN)} to make sure that it - doesn't mess with the internal states of the widget that received the - mouse press in the first place (which could e.g. be a QPushButton - inside a QScrollArea). + \note To avoid unwanted side-effects, mouse events are consumed while + the gesture is triggered. Since the initial mouse press event is + not consumed, the gesture sends a fake mouse release event + at the global position \c{(INT_MIN, INT_MIN)}. This ensures that + internal states of the widget that received the original mouse press + are consistent. + + \sa ungrabGesture, grabbedGesture */ Qt::GestureType QScroller::grabGesture(QObject *target, ScrollerGestureType scrollGestureType) { @@ -437,6 +438,8 @@ Qt::GestureType QScroller::grabGesture(QObject *target, ScrollerGestureType scro /*! Returns the gesture type currently grabbed for the \a target or 0 if no gesture is grabbed. + + \sa grabGesture, ungrabGesture */ Qt::GestureType QScroller::grabbedGesture(QObject *target) { @@ -449,6 +452,9 @@ Qt::GestureType QScroller::grabbedGesture(QObject *target) /*! Ungrabs the gesture for the \a target. + Does nothing if no gesture is grabbed. + + \sa grabGesture, grabbedGesture */ void QScroller::ungrabGesture(QObject *target) { @@ -521,7 +527,7 @@ QScroller::State QScroller::state() const } /*! - Stops the scroller and resets the state back to Inactive. + Stops the scroller and resets its state back to Inactive. */ void QScroller::stop() { @@ -543,13 +549,13 @@ void QScroller::stop() } /*! - \brief Returns the pixel per meter metric for the scrolled widget. + Returns the pixel per meter metric for the scrolled widget. The value is reported for both the x and y axis separately by using a QPointF. - \note Please note that this value should be physically correct, while the actual DPI settings - that Qt returns for the display may be reported wrongly (on purpose) by the underlying - windowing system (e.g. Mac OS X or Maemo 5). + \note Please note that this value should be physically correct. The actual DPI settings + that Qt returns for the display may be reported wrongly on purpose by the underlying + windowing system, for example on Mac OS X or Maemo 5. */ QPointF QScroller::pixelPerMeter() const { @@ -574,10 +580,8 @@ QPointF QScroller::pixelPerMeter() const } /*! - \brief Returns the current velocity of the scroller. - - Returns the current scrolling velocity in meter per second when in the state Scrolling. - Returns a null velocity otherwise. + Returns the current scrolling velocity in meter per second when the state is Scrolling or Dragging. + Returns a zero velocity otherwise. The velocity is reported for both the x and y axis separately by using a QPointF. @@ -597,18 +601,17 @@ QPointF QScroller::velocity() const if (!d->xSegments.isEmpty()) { const QScrollerPrivate::ScrollSegment &s = d->xSegments.head(); - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / s.maxProgress / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); vel.setX(v); } if (!d->ySegments.isEmpty()) { const QScrollerPrivate::ScrollSegment &s = d->ySegments.head(); - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / s.maxProgress / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); vel.setY(v); } - //qScrollerDebug() << "Velocity: " << vel; return vel; } default: @@ -617,11 +620,9 @@ QPointF QScroller::velocity() const } /*! - \brief Returns the target position for the scroll movement. - - Returns the planned final position for the current scroll movement or the current - position if the scroller is not in the scrolling state. - The result is undefined when the scroller is in the inactive state. + Returns the estimated final position for the current scroll movement. + Returns the current position if the scroller state is not Scrolling. + The result is undefined when the scroller state is Inactive. The target position is in pixel. @@ -642,7 +643,7 @@ QPointF QScroller::finalPosition() const In this case the scroller might or might not overshoot. The scrolling speed will be calculated so that the given position will - be reached after a platform-defined time span (e.g. 1 second for Maemo 5). + be reached after a platform-defined time span. \a pos is given in viewport coordinates. @@ -656,7 +657,7 @@ void QScroller::scrollTo(const QPointF &pos) /*! \overload - This version will reach its destination position in \a scrollTime milli seconds. + This version will reach its destination position in \a scrollTime milliseconds. */ void QScroller::scrollTo(const QPointF &pos, int scrollTime) { @@ -704,10 +705,12 @@ void QScroller::scrollTo(const QPointF &pos, int scrollTime) In cases where it is not possible to fit the rect plus margins inside the viewport the contents are scrolled so that as much as possible is visible from \a rect. - The scrolling speed will be calculated so that the given position will - be reached after a platform-defined time span (e.g. 1 second for Maemo 5). + The scrolling speed is calculated so that the given position is reached after a platform-defined + time span. This function performs the actual scrolling by calling scrollTo(). + + \sa scrollTo */ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin) { @@ -717,7 +720,7 @@ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin) /*! \overload - This version will reach its destination position in \a scrollTime milli seconds. + This version will reach its destination position in \a scrollTime milliseconds. */ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, int scrollTime) { @@ -787,11 +790,11 @@ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, } /*! This function resends the QScrollPrepareEvent. - * Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller. - * This will allow the receiver to re-set content position and content size while - * scrolling. - * Calling this function while in the Inactive state is useless as the prepare event - * is send again right before scrolling starts. + Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller. + This allows the receiver to re-set content position and content size while + scrolling. + Calling this function while in the Inactive state is useless as the prepare event + is sent again before scrolling starts. */ void QScroller::resendPrepareEvent() { @@ -799,11 +802,10 @@ void QScroller::resendPrepareEvent() d->prepareScrolling(d->pressPosition); } -/*! Set the snap positions for the horizontal axis. - * Set the snap positions to a list of \a positions. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an empty list of positions. +/*! Set the snap positions for the horizontal axis to a list of \a positions. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an empty list of positions. */ void QScroller::setSnapPositionsX(const QList<qreal> &positions) { @@ -814,13 +816,12 @@ void QScroller::setSnapPositionsX(const QList<qreal> &positions) d->recalcScrollingSegments(); } -/*! Set the snap positions for the horizontal axis. - * Set the snap positions to regular spaced intervals. - * The first snap position will be at \a first from the beginning of the list. The next at \a first + \a interval and so on. - * This can be used to implement a list header. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an interval of 0.0 +/*! Set the snap positions for the horizontal axis to regular spaced intervals. + The first snap position is at \a first. The next at \a first + \a interval. + This can be used to implement a list header. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an interval of 0.0 */ void QScroller::setSnapPositionsX(qreal first, qreal interval) { @@ -832,11 +833,10 @@ void QScroller::setSnapPositionsX(qreal first, qreal interval) d->recalcScrollingSegments(); } -/*! Set the snap positions for the vertical axis. - * Set the snap positions to a list of \a positions. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an empty list of positions. +/*! Set the snap positions for the vertical axis to a list of \a positions. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an empty list of positions. */ void QScroller::setSnapPositionsY(const QList<qreal> &positions) { @@ -847,12 +847,11 @@ void QScroller::setSnapPositionsY(const QList<qreal> &positions) d->recalcScrollingSegments(); } -/*! Set the snap positions for the vertical axis. - * Set the snap positions to regular spaced intervals. - * The first snap position will be at \a first. The next at \a first + \a interval and so on. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an interval of 0.0 +/*! Set the snap positions for the vertical axis to regular spaced intervals. + The first snap position is at \a first. The next at \a first + \a interval. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an interval of 0.0 */ void QScroller::setSnapPositionsY(qreal first, qreal interval) { @@ -951,14 +950,15 @@ void QScrollerPrivate::timerTick() /*! This function is used by gesture recognizers to inform the scroller about a new input event. - The scroller will change its internal state() according to the input event and its attached - scroller properties. Since the scroller doesn't care about the actual kind of input device the - event came from, you need to decompose the event into the \a input type, a \a position and a + The scroller changes its internal state() according to the input event and its attached + scroller properties. The scroller doesn't distinguish between the kind of input device the + event came from. Therefore the event needs to be split into the \a input type, a \a position and a milli-second \a timestamp. The \a position needs to be in the target's coordinate system. + The return value is \c true if the event should be consumed by the calling filter or \c false if the event should be forwarded to the control. - \note Using grabGesture() should be sufficient for most use cases though. + \note Using grabGesture() should be sufficient for most use cases. */ bool QScroller::handleInput(Input input, const QPointF &position, qint64 timestamp) { @@ -1005,9 +1005,18 @@ QPointF QScrollerPrivate::realDpi(int screen) return QPointF(260, 260); # elif defined(Q_WS_X11) && !defined(QT_NO_XRANDR) - if (X11->use_xrandr && X11->ptrXRRSizes) { + if (X11 && X11->use_xrandr && X11->ptrXRRSizes && X11->ptrXRRRootToScreen) { int nsizes = 0; - XRRScreenSize *sizes = X11->ptrXRRSizes(X11->display, screen == -1 ? X11->defaultScreen : screen, &nsizes); + // QDesktopWidget is based on Xinerama screens, which do not always + // correspond to RandR screens: NVidia's TwinView e.g. will show up + // as 2 screens in QDesktopWidget, but libXRandR will only see 1 screen. + // (although with the combined size of the Xinerama screens). + // Additionally, libXrandr will simply crash when calling XRRSizes + // for (the non-existent) screen 1 in this scenario. + Window root = RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen); + int randrscreen = (root != XNone) ? X11->ptrXRRRootToScreen(X11->display, root) : -1; + + XRRScreenSize *sizes = X11->ptrXRRSizes(X11->display, randrscreen == -1 ? 0 : randrscreen, &nsizes); if (nsizes > 0 && sizes && sizes->width && sizes->height && sizes->mwidth && sizes->mheight) { qScrollerDebug() << "XRandR DPI:" << QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth), qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight)); @@ -1073,8 +1082,23 @@ void QScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 delta deltaPixel = deltaPixelRaw * qreal(2.5) * ppm / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength(); QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / ppm; - if (releaseVelocity != QPointF(0, 0)) - newv = newv * sp->dragVelocitySmoothingFactor + releaseVelocity * (qreal(1) - sp->dragVelocitySmoothingFactor); + // around 95% of all updates are in the [1..50] ms range, so make sure + // to scale the smoothing factor over that range: this way a 50ms update + // will have full impact, while 5ms update will only have a 10% impact. + qreal smoothing = sp->dragVelocitySmoothingFactor * qMin(qreal(deltaTime), qreal(50)) / qreal(50); + + // only smooth if we already have a release velocity and only if the + // user hasn't stopped to move his finger for more than 100ms + if ((releaseVelocity != QPointF(0, 0)) && (deltaTime < 100)) { + qScrollerDebug() << "SMOOTHED from " << newv << " to " << newv * smoothing + releaseVelocity * (qreal(1) - smoothing); + // smooth x or y only if the new velocity is either 0 or at least in + // the same direction of the release velocity + if (!newv.x() || (qSign(releaseVelocity.x()) == qSign(newv.x()))) + newv.setX(newv.x() * smoothing + releaseVelocity.x() * (qreal(1) - smoothing)); + if (!newv.y() || (qSign(releaseVelocity.y()) == qSign(newv.y()))) + newv.setY(newv.y() * smoothing + releaseVelocity.y() * (qreal(1) - smoothing)); + } else + qScrollerDebug() << "NO SMOOTHING to " << newv; releaseVelocity.setX(qBound(-sp->maximumVelocity, newv.x(), sp->maximumVelocity)); releaseVelocity.setY(qBound(-sp->maximumVelocity, newv.y(), sp->maximumVelocity)); @@ -1082,23 +1106,24 @@ void QScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 delta qScrollerDebug() << " --> new velocity:" << releaseVelocity; } -void QScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal startPos, qreal endPos, QEasingCurve::Type curve, Qt::Orientation orientation, qreal maxProgress) +void QScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation) { - if (startPos == endPos) + if (startPos == stopPos || deltaPos == 0) return; ScrollSegment s; if (orientation == Qt::Horizontal && !xSegments.isEmpty()) - s.startTime = xSegments.last().startTime + xSegments.last().deltaTime; + s.startTime = xSegments.last().startTime + xSegments.last().deltaTime * xSegments.last().stopProgress; else if (orientation == Qt::Vertical && !ySegments.isEmpty()) - s.startTime = ySegments.last().startTime + ySegments.last().deltaTime; + s.startTime = ySegments.last().startTime + ySegments.last().deltaTime * ySegments.last().stopProgress; else s.startTime = monotonicTimer.elapsed(); s.startPos = startPos; - s.deltaPos = endPos - startPos; + s.deltaPos = deltaPos; + s.stopPos = stopPos; s.deltaTime = deltaTime * 1000; - s.maxProgress = maxProgress; + s.stopProgress = stopProgress; s.curve.setType(curve); s.type = type; @@ -1133,23 +1158,17 @@ void QScrollerPrivate::recalcScrollingSegments(bool forceRecalc) */ qreal QScrollerPrivate::scrollingSegmentsEndPos(Qt::Orientation orientation) const { - const QQueue<ScrollSegment> *segments; - qreal endPos; - if (orientation == Qt::Horizontal) { - segments = &xSegments; - endPos = contentPosition.x() + overshootPosition.x(); + if (xSegments.isEmpty()) + return contentPosition.x() + overshootPosition.x(); + else + return xSegments.last().stopPos; } else { - segments = &ySegments; - endPos = contentPosition.y() + overshootPosition.y(); - } - - if (!segments->isEmpty()) { - const ScrollSegment &last = segments->last(); - endPos = last.startPos + last.deltaPos; + if (ySegments.isEmpty()) + return contentPosition.y() + overshootPosition.y(); + else + return ySegments.last().stopPos; } - - return endPos; } /*! \internal @@ -1175,23 +1194,23 @@ bool QScrollerPrivate::scrollingSegmentsValid(Qt::Orientation orientation) return true; const ScrollSegment &last = segments->last(); - qreal endPos = last.startPos + last.deltaPos; + qreal stopPos = last.stopPos; if (last.type == ScrollTypeScrollTo) return true; // scrollTo is always valid if (last.type == ScrollTypeOvershoot && - endPos != minPos && endPos != maxPos) + (stopPos != minPos && stopPos != maxPos)) return false; - if (endPos < minPos || endPos > maxPos) + if (stopPos < minPos || stopPos > maxPos) return false; - if (endPos == minPos || endPos == maxPos) // the begin and the end of the list are always ok + if (stopPos == minPos || stopPos == maxPos) // the begin and the end of the list are always ok return true; - qreal nextSnap = nextSnapPos(endPos, 0, orientation); - if (!qIsNaN(nextSnap) && endPos != nextSnap) + qreal nextSnap = nextSnapPos(stopPos, 0, orientation); + if (!qIsNaN(nextSnap) && stopPos != nextSnap) return false; return true; @@ -1204,11 +1223,10 @@ void QScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal en { Q_UNUSED(v); - if (orientation == Qt::Horizontal) { + if (orientation == Qt::Horizontal) xSegments.clear(); - } else { + else ySegments.clear(); - } qScrollerDebug() << "+++ createScrollToSegments: t:" << deltaTime << "ep:" << endPos << "o:" << int(orientation); @@ -1216,10 +1234,10 @@ void QScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal en qreal startPos = (orientation == Qt::Horizontal) ? contentPosition.x() + overshootPosition.x() : contentPosition.y() + overshootPosition.y(); - qreal deltaPos = endPos - startPos; + qreal deltaPos = (endPos - startPos) / 2; - pushSegment(type, deltaTime * 0.3, startPos, startPos + deltaPos * 0.5, QEasingCurve::InQuad, orientation); - pushSegment(type, deltaTime * 0.7, startPos + deltaPos * 0.5, endPos, sp->scrollingCurve.type(), orientation); + pushSegment(type, deltaTime * qreal(0.3), qreal(1.0), startPos, deltaPos, startPos + deltaPos, QEasingCurve::InQuad, orientation); + pushSegment(type, deltaTime * qreal(0.7), qreal(1.0), startPos + deltaPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation); } /*! \internal @@ -1253,17 +1271,6 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp qScrollerDebug() << "+++ createScrollingSegments: s:" << startPos << "maxPos:" << maxPos << "o:" << int(orientation); - // -- check if we are in overshoot - if (startPos < minPos) { - createScrollToSegments(v, sp->overshootScrollTime * 0.5, minPos, orientation, ScrollTypeOvershoot); - return; - } - - if (startPos > maxPos) { - createScrollToSegments(v, sp->overshootScrollTime * 0.5, maxPos, orientation, ScrollTypeOvershoot); - return; - } - qScrollerDebug() << "v = " << v << ", decelerationFactor = " << sp->decelerationFactor << ", curveType = " << sp->scrollingCurve.type(); // This is only correct for QEasingCurve::OutQuad (linear velocity, @@ -1299,6 +1306,16 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp if (nextSnap < lowerSnapPos || qIsNaN(lowerSnapPos)) lowerSnapPos = nextSnap; + // -- check if are in overshoot and end in overshoot + if ((startPos < minPos && endPos < minPos) || + (startPos > maxPos && endPos > maxPos)) { + qreal stopPos = endPos < minPos ? minPos : maxPos; + qreal oDeltaTime = sp->overshootScrollTime; + + pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), startPos, stopPos - startPos, stopPos, sp->scrollingCurve.type(), orientation); + return; + } + if (qAbs(v) < sp->minimumVelocity) { qScrollerDebug() << "### below minimum Vel" << orientation; @@ -1324,8 +1341,9 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp endPos = higherSnapPos; deltaPos = endPos - startPos; - pushSegment(ScrollTypeFlick, sp->snapTime * 0.3, startPos, startPos + deltaPos * 0.3, QEasingCurve::InQuad, orientation); - pushSegment(ScrollTypeFlick, sp->snapTime * 0.7, startPos + deltaPos * 0.3, endPos, sp->scrollingCurve.type(), orientation); + qreal midPos = startPos + deltaPos * qreal(0.3); + pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.3), qreal(1.0), startPos, midPos - startPos, midPos, QEasingCurve::InQuad, orientation); + pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.7), qreal(1.0), midPos, endPos - midPos, endPos, sp->scrollingCurve.type(), orientation); return; } @@ -1352,32 +1370,38 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp qScrollerDebug() << "Overshoot: delta:" << (stopPos - startPos); - qreal maxProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos)); - qScrollerDebug() << "Overshoot maxp:" << maxProgress; - - pushSegment(ScrollTypeFlick, deltaTime * maxProgress, startPos, stopPos, sp->scrollingCurve.type(), orientation, maxProgress); + qreal stopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos)); - if (canOvershoot) { - qreal endV = qSign(v) * deltaTime * sp->decelerationFactor * qreal(0.5) * differentialForProgress(sp->scrollingCurve, maxProgress); - qScrollerDebug() << "Overshoot: velocity" << endV; - qScrollerDebug() << "Overshoot: maxVelocity" << sp->maximumVelocity; - qScrollerDebug() << "Overshoot: viewsize" << viewSize; - qScrollerDebug() << "Overshoot: factor" << sp->overshootScrollDistanceFactor; + if (!canOvershoot) { + qScrollerDebug() << "Overshoot stopp:" << stopProgress; - qreal oDistance = viewSize * sp->overshootScrollDistanceFactor * endV / sp->maximumVelocity; + pushSegment(ScrollTypeFlick, deltaTime, stopProgress, startPos, endPos, stopPos, sp->scrollingCurve.type(), orientation); + } else { qreal oDeltaTime = sp->overshootScrollTime; + qreal oStopProgress = qMin(stopProgress + oDeltaTime * qreal(0.3) / deltaTime, qreal(1)); + qreal oDistance = startPos + deltaPos * sp->scrollingCurve.valueForProgress(oStopProgress) - stopPos; + qreal oMaxDistance = qSign(oDistance) * (viewSize * sp->overshootScrollDistanceFactor); + + qScrollerDebug() << "1 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress; - pushSegment(ScrollTypeOvershoot, oDeltaTime * 0.3, stopPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation); - pushSegment(ScrollTypeOvershoot, oDeltaTime * 0.7, stopPos + oDistance, stopPos, sp->scrollingCurve.type(), orientation); + if (qAbs(oDistance) > qAbs(oMaxDistance)) { + oStopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos + oMaxDistance - startPos) / deltaPos)); + oDistance = oMaxDistance; + qScrollerDebug() << "2 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress; + } + + pushSegment(ScrollTypeFlick, deltaTime, oStopProgress, startPos, deltaPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation); + pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), stopPos + oDistance, -oDistance, stopPos, sp->scrollingCurve.type(), orientation); } return; } - pushSegment(ScrollTypeFlick, deltaTime, startPos, endPos, sp->scrollingCurve.type(), orientation); + pushSegment(ScrollTypeFlick, deltaTime, qreal(1.0), startPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation); } -/*! Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget. +/*! \internal + Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget. Returns true if the scrolling was accepted and a target was returned. */ bool QScrollerPrivate::prepareScrolling(const QPointF &position) @@ -1796,13 +1820,18 @@ qreal QScrollerPrivate::nextSegmentPosition(QQueue<ScrollSegment> &segments, qin while (!segments.isEmpty()) { const ScrollSegment s = segments.head(); - if ((s.startTime + s.deltaTime) <= now) { + if ((s.startTime + s.deltaTime * s.stopProgress) <= now) { segments.dequeue(); - pos = s.startPos + s.deltaPos; + pos = s.stopPos; } else if (s.startTime <= now) { - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress) / s.curve.valueForProgress(s.maxProgress); - break; + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress); + if (s.deltaPos > 0 ? pos > s.stopPos : pos < s.stopPos) { + segments.dequeue(); + pos = s.stopPos; + } else { + break; + } } else { break; } @@ -1835,12 +1864,12 @@ void QScrollerPrivate::setContentPositionHelperScrolling() } /*! \internal - * Returns the next snap point in direction. - * If \a direction >0 it will return the next snap point that is larger than the current position. - * If \a direction <0 it will return the next snap point that is smaller than the current position. - * If \a direction ==0 it will return the nearest snap point (or the current position if we are already - * on a snap point. - * Returns the nearest snap position or NaN if no such point could be found. + Returns the next snap point in direction. + If \a direction >0 it will return the next snap point that is larger than the current position. + If \a direction <0 it will return the next snap point that is smaller than the current position. + If \a direction ==0 it will return the nearest snap point (or the current position if we are already + on a snap point. + Returns the nearest snap position or NaN if no such point could be found. */ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientation) { @@ -1962,7 +1991,7 @@ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientatio This enum contains the different QScroller states. \value Inactive The scroller is not scrolling and nothing is pressed. - \value Pressed A touch event was received or the mouse button pressed but the scroll area is currently not dragged. + \value Pressed A touch event was received or the mouse button was pressed but the scroll area is currently not dragged. \value Dragging The scroll area is currently following the touch point or mouse. \value Scrolling The scroll area is moving on it's own. */ @@ -1973,8 +2002,8 @@ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientatio This enum contains the different gesture types that are supported by the QScroller gesture recognizer. \value TouchGesture The gesture recognizer will only trigger on touch - events. Specifically it will react on single touch points when using a - touch screen and dual touch points when using a touchpad. + events. Specifically it will react on single touch points when using a + touch screen and dual touch points when using a touchpad. \value LeftMouseButtonGesture The gesture recognizer will only trigger on left mouse button events. \value MiddleMouseButtonGesture The gesture recognizer will only trigger on middle mouse button events. \value RightMouseButtonGesture The gesture recognizer will only trigger on right mouse button events. diff --git a/src/gui/util/qscroller_p.h b/src/gui/util/qscroller_p.h index 98f34f7619..d16eef9249 100644 --- a/src/gui/util/qscroller_p.h +++ b/src/gui/util/qscroller_p.h @@ -98,7 +98,8 @@ public: qreal startPos; qreal deltaPos; QEasingCurve curve; - qreal maxProgress; + qreal stopProgress; // whatever is.. + qreal stopPos; // ..reached first ScrollType type; }; @@ -122,7 +123,7 @@ public: void setDpiFromWidget(QWidget *widget); void updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime); - void pushSegment(ScrollType type, qreal deltaTime, qreal startPos, qreal endPos, QEasingCurve::Type curve, Qt::Orientation orientation, qreal maxProgress = 1.0); + void pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation); void recalcScrollingSegments(bool forceRecalc = false); qreal scrollingSegmentsEndPos(Qt::Orientation orientation) const; bool scrollingSegmentsValid(Qt::Orientation orientation); diff --git a/src/gui/util/qscrollerproperties.cpp b/src/gui/util/qscrollerproperties.cpp index 2e52959e6f..b159e05ac0 100644 --- a/src/gui/util/qscrollerproperties.cpp +++ b/src/gui/util/qscrollerproperties.cpp @@ -95,11 +95,11 @@ QScrollerPropertiesPrivate *QScrollerPropertiesPrivate::defaults() The QScrollerProperties class stores the parameters used by QScroller. - The default settings are platform dependant and Qt will emulate the + The default settings are platform dependent so that Qt emulates the platform behaviour for kinetic scrolling. As a convention the QScrollerProperties are in physical units (meter, - seconds) and will be converted by QScroller using the current DPI. + seconds) and are converted by QScroller using the current DPI. \sa QScroller */ @@ -179,10 +179,10 @@ bool QScrollerPropertiesPrivate::operator==(const QScrollerPropertiesPrivate &p) } /*! - Sets the scroller properties returned by the default constructor to \a sp. + Sets the scroller properties for all new QScrollerProperties objects to \a sp. Use this function to override the platform default properties returned by the default - constructor. If you only want to change the scroller properties of a single scroller, then use + constructor. If you only want to change the scroller properties of a single scroller, use QScroller::setScrollerProperties() \note Calling this function will not change the content of already existing @@ -291,50 +291,48 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v This enum describes the various modes of overshooting. - \value OvershootWhenScrollable Overshooting is when the content is scrollable. This is the + \value OvershootWhenScrollable Overshooting is possible when the content is scrollable. This is the default. - \value OvershootAlwaysOff Overshooting is never enabled (even when the content is scrollable). + \value OvershootAlwaysOff Overshooting is never enabled, even when the content is scrollable. - \value OvershootAlwaysOn Overshooting is always enabled (even when the content is not - scrollable). + \value OvershootAlwaysOn Overshooting is always enabled, even when the content is not + scrollable. */ /*! \enum QScrollerProperties::ScrollMetric This enum contains the different scroll metric types. When not indicated otherwise the - setScrollMetric function expects a QVariant of a real value. + setScrollMetric function expects a QVariant of type qreal. - See the QScroller documentation for a further explanation of the concepts behind the different + See the QScroller documentation for further details of the concepts behind the different values. - \value MousePressEventDelay This is the time a mouse press event will be delayed when starting + \value MousePressEventDelay This is the time a mouse press event is delayed when starting a flick gesture in \c{[s]}. If the gesture is triggered within that time, no mouse press or - release will be sent to the scrolled object. If it triggers after that delay the (delayed) - mouse press plus a faked release event (at global postion \c{QPoint(-QWIDGETSIZE_MAX, - -QWIDGETSIZE_MAX)} will be sent. If the gesture is canceled, then both the (delayed) mouse - press plus the real release event will be delivered. + release is sent to the scrolled object. If it triggers after that delay the delayed + mouse press plus a faked release event at global postion \c{QPoint(-QWIDGETSIZE_MAX, + -QWIDGETSIZE_MAX)} is sent. If the gesture is canceled, then both the delayed mouse + press plus the real release event are delivered. \value DragStartDistance This is the minimum distance the touch or mouse point needs to be moved before the flick gesture is triggered in \c m. - \value DragVelocitySmoothingFactor A value that describes how much new drag velocities are + \value DragVelocitySmoothingFactor A value that describes to which extent new drag velocities are included in the final scrolling velocity. This value should be in the range between \c 0 and - \c 1. The lower the value, the more smoothing will be applied to the dragging velocity. The - default value is \c 0.8. + \c 1. The lower the value, the more smoothing is applied to the dragging velocity. - \value AxisLockThreshold If greater than zero a scroll movement will be restricted to one axis - only if the movement is inside an angle about the axis. The threshold must be in the range \c 0 - to \c 1. + \value AxisLockThreshold Restricts the movement to one axis if the movement is inside an angle + around the axis. The threshold must be in the range \c 0 to \c 1. \value ScrollingCurve The QEasingCurve used when decelerating the scrolling velocity after an user initiated flick. Please note that this is the easing curve for the positions, \bold{not} - the velocity: the default is QEasingCurve::OutQuad, which results is a linear decrease in + the velocity: the default is QEasingCurve::OutQuad, which results in a linear decrease in velocity (1st derivative) and a constant deceleration (2nd derivative). \value DecelerationFactor This factor influences how long it takes the scroller to decelerate - to 0 velocity. The actual value heavily depends on the chosen ScrollingCurve, but for most + to 0 velocity. The actual value depends on the chosen ScrollingCurve. For most types the value should be in the range from \c 0.1 to \c 2.0 \value MinimumVelocity The minimum velocity that is needed after ending the touch or releasing @@ -344,39 +342,38 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v \value MaximumClickThroughVelocity This is the maximum allowed scroll speed for a click-through in \c{m/s}. This means that a click on a currently (slowly) scrolling object will not only stop - the scrolling but the click event will also be delivered to the UI control - this is very + the scrolling but the click event will also be delivered to the UI control. This is useful when using exponential-type scrolling curves. \value AcceleratingFlickMaximumTime This is the maximum time in \c seconds that a flick gesture - can take to be recognized as an accelerating flick. If set to zero no such gesture will be + can take to be recognized as an accelerating flick. If set to zero no such gesture is detected. An "accelerating flick" is a flick gesture executed on an already scrolling object. In such cases the scrolling speed is multiplied by AcceleratingFlickSpeedupFactor in order to accelerate it. - \value AcceleratingFlickSpeedupFactor The current speed will be multiplied by this number if an - accelerating flick is detected. Should be \c{> 1}. + \value AcceleratingFlickSpeedupFactor The current speed is multiplied by this number if an + accelerating flick is detected. Should be \c{>= 1}. \value SnapPositionRatio This is the distance that the user must drag the area beween two snap - points in order to snap it to the next position. e.g. \c{0.33} means that the scroll must only + points in order to snap it to the next position. \c{0.33} means that the scroll must only reach one third of the distance between two snap points to snap to the next one. The ratio must - be in the range \c 0 to \c 1. + be between \c 0 and \c 1. \value SnapTime This is the time factor for the scrolling curve. A lower value means that the scrolling will take longer. The scrolling distance is independet of this value. \value OvershootDragResistanceFactor This value is the factor between the mouse dragging and - the actual scroll area movement (during overshoot). The factor must be in the range \c 0 to \c - 1. + the actual scroll area movement (during overshoot). The factor must be between \c 0 and \c 1. \value OvershootDragDistanceFactor This is the maximum distance for overshoot movements while - dragging. The actual overshoot distance will be calculated by multiplying this value with the - viewport size of the scrolled object. The factor must be in the range \c 0 to \c 1. + dragging. The actual overshoot distance is calculated by multiplying this value with the + viewport size of the scrolled object. The factor must be between \c 0 and \c 1. \value OvershootScrollDistanceFactor This is the maximum distance for overshoot movements while - scrolling. The actual overshoot distance will be calculated by multiplying this value with the - viewport size of the scrolled object. The factor must be in the range \c 0 to \c 1. + scrolling. The actual overshoot distance is calculated by multiplying this value with the + viewport size of the scrolled object. The factor must be between \c 0 and \c 1. - \value OvershootScrollTime This is the time in \c seconds that will be used to play the + \value OvershootScrollTime This is the time in \c seconds that is used to play the complete overshoot animation. \value HorizontalOvershootPolicy This is the horizontal overshooting policy (see OvershootPolicy). @@ -385,13 +382,12 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v \value FrameRate This is the frame rate which should be used while dragging or scrolling. QScroller uses a QAbstractAnimation timer internally to sync all scrolling operations to other - animations that might be active at the same time. If the Standard value of 60 frames per - second is too fast for your use case, you can lower the frames per second with this setting - (while still being in-sync with QAbstractAnimation). Please note that only the values of the + animations that might be active at the same time. If the standard value of 60 frames per + second is too fast, it can be lowered with this setting, + while still being in-sync with QAbstractAnimation. Please note that only the values of the FrameRates enum are allowed here. - \value ScrollMetricCount This is just used when enumerating the metrics. It is always the last - entry. + \value ScrollMetricCount This is always the last entry. */ QT_END_NAMESPACE diff --git a/src/gui/widgets/qcocoamenu_mac.mm b/src/gui/widgets/qcocoamenu_mac.mm index d5c8eb89d2..dff37b49ad 100644 --- a/src/gui/widgets/qcocoamenu_mac.mm +++ b/src/gui/widgets/qcocoamenu_mac.mm @@ -48,6 +48,7 @@ #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qapplication_p.h> #include <private/qaction_p.h> +#include <private/qcocoaapplication_mac_p.h> #include <QtGui/QMenu> diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 79461377f4..b5dda5a754 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -2486,10 +2486,18 @@ void QComboBox::showPopup() listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { - // landscape without stacon, menu should be at the right - (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : - listRect.setLeft(screen.left()); + if (staConTopRect.IsEmpty()) { + TRect cbaRect = TRect(); + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EControlPane, cbaRect); + AknLayoutUtils::TAknCbaLocation cbaLocation = AknLayoutUtils::CbaLocation(); + switch (cbaLocation) { + case AknLayoutUtils::EAknCbaLocationRight: + listRect.setRight(screen.right()); + break; + case AknLayoutUtils::EAknCbaLocationLeft: + listRect.setLeft(screen.left()); + break; + } } } #endif diff --git a/src/gui/widgets/qfocusframe.cpp b/src/gui/widgets/qfocusframe.cpp index c159a1e0c8..6508c31b50 100644 --- a/src/gui/widgets/qfocusframe.cpp +++ b/src/gui/widgets/qfocusframe.cpp @@ -85,6 +85,9 @@ void QFocusFramePrivate::update() void QFocusFramePrivate::updateSize() { Q_Q(QFocusFrame); + if (!widget) + return; + int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin), hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); QPoint pos(widget->x(), widget->y()); diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp index a786e2f309..fd6178386f 100644 --- a/src/gui/widgets/qmainwindow.cpp +++ b/src/gui/widgets/qmainwindow.cpp @@ -1516,11 +1516,6 @@ void QMainWindow::setUnifiedTitleAndToolBarOnMac(bool set) if (!isWindow() || d->useHIToolBar == set || QSysInfo::MacintoshVersion < QSysInfo::MV_10_3) return; - // ### Disable when using alien widgets - if (testAttribute(Qt::WA_NativeWindow) == false) { - return; - } - d->useHIToolBar = set; createWinId(); // We need the hiview for down below. diff --git a/src/gui/widgets/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm index 9a23ca76c2..e428ffcde0 100644 --- a/src/gui/widgets/qmainwindowlayout_mac.mm +++ b/src/gui/widgets/qmainwindowlayout_mac.mm @@ -355,7 +355,11 @@ void QMainWindowLayout::updateHIToolBarStatus() // Move everything out of the HIToolbar into the main toolbar. while (!qtoolbarsInUnifiedToolbarList.isEmpty()) { // Should shrink the list by one every time. - layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first()); + QToolBar *toolbar = qtoolbarsInUnifiedToolbarList.first(); + layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); +#if defined(QT_MAC_USE_COCOA) + toolbar->d_func()->isInUnifiedToolbar = false; +#endif } macWindowToolbarSet(qt_mac_window_for(layoutState.mainWindow), 0); } else { @@ -363,7 +367,8 @@ void QMainWindowLayout::updateHIToolBarStatus() for (int i = 0; i < toolbars.size(); ++i) { QToolBar *toolbar = toolbars.at(i); if (toolBarArea(toolbar) == Qt::TopToolBarArea) { - removeWidget(toolbar); // Do this here, because we are in an in-between state. + // Do this here, because we are in an in-between state. + removeWidget(toolbar); layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); } } @@ -387,10 +392,20 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar if (toolbar == 0) return; +#if defined(QT_MAC_USE_COCOA) + // toolbar will now become native (if not allready) since we need + // an nsview for it inside the corresponding NSToolbarItem. + // Setting isInUnifiedToolbar will (among other things) stop alien + // siblings from becoming native when this happends since the toolbar + // will not overlap with other children of the QMainWindow. NB: Switching + // unified toolbar off after this stage is not supported, as this means + // that either the menubar must be alien again, or the sibling must + // be backed by an nsview to protect from overlapping issues: + toolbar->d_func()->isInUnifiedToolbar = true; +#endif QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout()); - toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), - toolbar->maximumSize())); + toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), toolbar->maximumSize())); if (toolbarLayout->hasExpandFlag() == false) toolbar->setMaximumSize(toolbar->sizeHint()); @@ -399,8 +414,8 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar toolbarLayout->setUsePopupMenu(true); // Make the toolbar a child of the mainwindow to avoid creating a window. toolbar->setParent(layoutState.mainWindow); - toolbar->createWinId(); // Now create the OSViewRef. + toolbar->winId(); // Now create the OSViewRef. layoutState.mainWindow->createWinId(); OSWindowRef window = qt_mac_window_for(layoutState.mainWindow); diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index dda5abacc7..5bfac9a646 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -1073,8 +1073,11 @@ void QMenuBar::paintEvent(QPaintEvent *e) void QMenuBar::setVisible(bool visible) { #if defined(Q_WS_MAC) || defined(Q_OS_WINCE) || defined(Q_WS_S60) - if (isNativeMenuBar()) + if (isNativeMenuBar()) { + if (!visible) + QWidget::setVisible(false); return; + } #endif QWidget::setVisible(visible); } |