diff options
author | Michael Brasser <michael.brasser@nokia.com> | 2009-10-25 23:26:53 (GMT) |
---|---|---|
committer | Michael Brasser <michael.brasser@nokia.com> | 2009-10-25 23:26:53 (GMT) |
commit | cbb1b29d397c063fc3bcae04c5b062f20cbd12c5 (patch) | |
tree | d89cebe45c9f6dfdcab34a95da19ee0d670e30f5 /src/gui | |
parent | ed7d58f6668be4ffb5f915722dee81c864f74844 (diff) | |
parent | 9aa00de1d277763c0f7a380fde27face60e5f686 (diff) | |
download | Qt-cbb1b29d397c063fc3bcae04c5b062f20cbd12c5.zip Qt-cbb1b29d397c063fc3bcae04c5b062f20cbd12c5.tar.gz Qt-cbb1b29d397c063fc3bcae04c5b062f20cbd12c5.tar.bz2 |
Merge branch '4.6' of git@scm.dev.nokia.troll.no:qt/qt into kinetic-declarativeui
Conflicts:
tools/qdoc3/cppcodemarker.cpp
Diffstat (limited to 'src/gui')
125 files changed, 2690 insertions, 1246 deletions
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h index 96fe91c..8f6d9d9 100644 --- a/src/gui/accessible/qaccessible.h +++ b/src/gui/accessible/qaccessible.h @@ -310,7 +310,8 @@ namespace QAccessible2 TextInterface, EditableTextInterface, ValueInterface, - TableInterface + TableInterface, + ActionInterface }; } @@ -319,6 +320,7 @@ class QAccessibleTextInterface; class QAccessibleEditableTextInterface; class QAccessibleValueInterface; class QAccessibleTableInterface; +class QAccessibleActionInterface; class Q_GUI_EXPORT QAccessibleInterface : public QAccessible { @@ -376,6 +378,9 @@ public: inline QAccessibleTableInterface *tableInterface() { return reinterpret_cast<QAccessibleTableInterface *>(cast_helper(QAccessible2::TableInterface)); } + inline QAccessibleActionInterface *actionInterface() + { return reinterpret_cast<QAccessibleActionInterface *>(cast_helper(QAccessible2::ActionInterface)); } + private: QAccessible2Interface *cast_helper(QAccessible2::InterfaceType); }; diff --git a/src/gui/accessible/qaccessible2.cpp b/src/gui/accessible/qaccessible2.cpp index f731962..0867368 100644 --- a/src/gui/accessible/qaccessible2.cpp +++ b/src/gui/accessible/qaccessible2.cpp @@ -108,6 +108,18 @@ QT_BEGIN_NAMESPACE \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink */ +/*! + \class QAccessibleActionInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleActionInterface class implements support for + the IAccessibleAction interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + QAccessibleSimpleEditableTextInterface::QAccessibleSimpleEditableTextInterface( QAccessibleInterface *accessibleInterface) : iface(accessibleInterface) diff --git a/src/gui/accessible/qaccessible2.h b/src/gui/accessible/qaccessible2.h index 3281509..435c640 100644 --- a/src/gui/accessible/qaccessible2.h +++ b/src/gui/accessible/qaccessible2.h @@ -81,6 +81,7 @@ inline QAccessible2Interface *qAccessibleValueCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleTextCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleActionCastHelper() { return 0; } #define Q_ACCESSIBLE_OBJECT \ public: \ @@ -95,6 +96,8 @@ inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; } return qAccessibleValueCastHelper(); \ case QAccessible2::TableInterface: \ return qAccessibleTableCastHelper(); \ + case QAccessible2::ActionInterface: \ + return qAccessibleActionCastHelper(); \ } \ return 0; \ } \ @@ -208,6 +211,19 @@ public: int *columnSpan, bool *isSelected) = 0; }; +class Q_GUI_EXPORT QAccessibleActionInterface : public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleActionCastHelper() { return this; } + + virtual int actionCount() = 0; + virtual void doAction(int actionIndex) = 0; + virtual QString description(int actionIndex) = 0; + virtual QString name(int actionIndex) = 0; + virtual QString localizedName(int actionIndex) = 0; + virtual QStringList keyBindings(int actionIndex) = 0; +}; + #endif // QT_NO_ACCESSIBILITY QT_END_NAMESPACE diff --git a/src/gui/animation/qguivariantanimation.cpp b/src/gui/animation/qguivariantanimation.cpp index 0ae79b6..1e9c166 100644 --- a/src/gui/animation/qguivariantanimation.cpp +++ b/src/gui/animation/qguivariantanimation.cpp @@ -54,10 +54,10 @@ QT_BEGIN_NAMESPACE template<> Q_INLINE_TEMPLATE QColor _q_interpolate(const QColor &f,const QColor &t, qreal progress) { - return QColor(_q_interpolate(f.red(), t.red(), progress), - _q_interpolate(f.green(), t.green(), progress), - _q_interpolate(f.blue(), t.blue(), progress), - _q_interpolate(f.alpha(), t.alpha(), progress)); + return QColor(qBound(0,_q_interpolate(f.red(), t.red(), progress),255), + qBound(0,_q_interpolate(f.green(), t.green(), progress),255), + qBound(0,_q_interpolate(f.blue(), t.blue(), progress),255), + qBound(0,_q_interpolate(f.alpha(), t.alpha(), progress),255)); } template<> Q_INLINE_TEMPLATE QQuaternion _q_interpolate(const QQuaternion &f,const QQuaternion &t, qreal progress) diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm index 8e4c461..d9bec27 100644 --- a/src/gui/dialogs/qfiledialog_mac.mm +++ b/src/gui/dialogs/qfiledialog_mac.mm @@ -280,6 +280,10 @@ QT_USE_NAMESPACE - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename { Q_UNUSED(sender); + + if ([filename length] == 0) + return NO; + QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename); QFileInfo info(qtFileName.normalized(QT_PREPEND_NAMESPACE(QString::NormalizationForm_C))); QString path = info.absolutePath(); diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp index b59c28b..8089b43 100644 --- a/src/gui/dialogs/qfiledialog_win.cpp +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -251,10 +251,10 @@ static OPENFILENAME* qt_win_make_OFN(QWidget *parent, ofn->nMaxFile = maxLen; ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16(); ofn->lpstrTitle = (wchar_t*)tTitle.utf16(); - ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER); + ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST); if (mode == QFileDialog::ExistingFile || mode == QFileDialog::ExistingFiles) - ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); + ofn->Flags |= (OFN_FILEMUSTEXIST); if (mode == QFileDialog::ExistingFiles) ofn->Flags |= (OFN_ALLOWMULTISELECT); if (!(options & QFileDialog::DontConfirmOverwrite)) diff --git a/src/gui/dialogs/qwizard.cpp b/src/gui/dialogs/qwizard.cpp index 0102e25..db1c9e9 100644 --- a/src/gui/dialogs/qwizard.cpp +++ b/src/gui/dialogs/qwizard.cpp @@ -1537,7 +1537,9 @@ void QWizardPrivate::handleAeroStyleChange() vistaHelper->backButton()->show(); } else { q->setMouseTracking(true); // ### original value possibly different +#ifndef QT_NO_CURSOR q->unsetCursor(); // ### ditto +#endif antiFlickerWidget->move(0, 0); vistaHelper->hideBackButton(); vistaHelper->setTitleBarIconAndCaptionVisible(true); diff --git a/src/gui/dialogs/qwizard_win.cpp b/src/gui/dialogs/qwizard_win.cpp index aa38ddc..f3f3a4e 100644 --- a/src/gui/dialogs/qwizard_win.cpp +++ b/src/gui/dialogs/qwizard_win.cpp @@ -387,10 +387,12 @@ bool QVistaHelper::winEvent(MSG* msg, long* result) void QVistaHelper::setMouseCursor(QPoint pos) { +#ifndef QT_NO_CURSOR if (rtTop.contains(pos)) wizard->setCursor(Qt::SizeVerCursor); else wizard->setCursor(Qt::ArrowCursor); +#endif } void QVistaHelper::mouseEvent(QEvent *event) diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp index 91641b0..96d35b0 100644 --- a/src/gui/effects/qgraphicseffect.cpp +++ b/src/gui/effects/qgraphicseffect.cpp @@ -253,7 +253,24 @@ bool QGraphicsEffectSource::isPixmap() const */ QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset) const { - return d_func()->pixmap(system, offset); + Q_D(const QGraphicsEffectSource); + + QPixmap pm; + if (d->m_cachedSystem == system) + QPixmapCache::find(d->m_cacheKey, &pm); + + if (pm.isNull()) { + pm = d->pixmap(system, &d->m_cachedOffset); + d->m_cachedSystem = system; + + d->invalidateCache(); + d->m_cacheKey = QPixmapCache::insert(pm); + } + + if (offset) + *offset = d->m_cachedOffset; + + return pm; } /*! diff --git a/src/gui/effects/qgraphicseffect.h b/src/gui/effects/qgraphicseffect.h index c5d3ede..c89851e 100644 --- a/src/gui/effects/qgraphicseffect.h +++ b/src/gui/effects/qgraphicseffect.h @@ -87,6 +87,7 @@ private: friend class QGraphicsEffectPrivate; friend class QGraphicsScenePrivate; friend class QGraphicsItem; + friend class QGraphicsItemPrivate; friend class QWidget; friend class QWidgetPrivate; }; diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h index ff2fb85..fc925f2 100644 --- a/src/gui/effects/qgraphicseffect_p.h +++ b/src/gui/effects/qgraphicseffect_p.h @@ -55,6 +55,8 @@ #include "qgraphicseffect.h" +#include <QPixmapCache> + #include <private/qobject_p.h> #include <private/qpixmapfilter_p.h> @@ -65,7 +67,7 @@ class QGraphicsEffectSourcePrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QGraphicsEffectSource) public: QGraphicsEffectSourcePrivate() : QObjectPrivate() {} - virtual ~QGraphicsEffectSourcePrivate() {} + virtual ~QGraphicsEffectSourcePrivate() { invalidateCache(); } virtual void detach() = 0; virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0; virtual QRect deviceRect() const = 0; @@ -77,9 +79,16 @@ public: virtual bool isPixmap() const = 0; virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0) const = 0; virtual void effectBoundingRectChanged() = 0; + void invalidateCache() const { QPixmapCache::remove(m_cacheKey); } + friend class QGraphicsScenePrivate; friend class QGraphicsItem; friend class QGraphicsItemPrivate; + +private: + mutable Qt::CoordinateSystem m_cachedSystem; + mutable QPoint m_cachedOffset; + mutable QPixmapCache::Key m_cacheKey; }; class Q_GUI_EXPORT QGraphicsEffectPrivate : public QObjectPrivate @@ -93,6 +102,7 @@ public: QGraphicsEffect::ChangeFlags flags; if (source) { flags |= QGraphicsEffect::SourceDetached; + source->d_func()->invalidateCache(); source->d_func()->detach(); delete source; } diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 840b9d6..39291d3 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -61,6 +61,7 @@ QEglContext::QEglContext() , cfg(0) , currentSurface(EGL_NO_SURFACE) , current(false) + , ownsContext(true) { } @@ -206,7 +207,7 @@ void QEglContext::destroySurface(EGLSurface surface) // Destroy the context. Note: this does not destroy the surface. void QEglContext::destroy() { - if (ctx != EGL_NO_CONTEXT) + if (ctx != EGL_NO_CONTEXT && ownsContext) eglDestroyContext(dpy, ctx); dpy = EGL_NO_DISPLAY; ctx = EGL_NO_CONTEXT; diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index dc399da..16b5b16 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -110,7 +110,7 @@ public: EGLDisplay display() const { return dpy; } EGLContext context() const { return ctx; } - void setContext(EGLContext context) { ctx = context; } + void setContext(EGLContext context) { ctx = context; ownsContext = false;} EGLConfig config() const { return cfg; } void setConfig(EGLConfig config) { cfg = config; } @@ -131,6 +131,7 @@ private: EGLConfig cfg; EGLSurface currentSurface; bool current; + bool ownsContext; static EGLDisplay getDisplay(QPaintDevice *device); diff --git a/src/gui/embedded/qdecorationdefault_qws.cpp b/src/gui/embedded/qdecorationdefault_qws.cpp index 5bc8826..70389d3 100644 --- a/src/gui/embedded/qdecorationdefault_qws.cpp +++ b/src/gui/embedded/qdecorationdefault_qws.cpp @@ -394,7 +394,7 @@ QPixmap QDecorationDefault::pixmapFor(const QWidget *widget, */ int QDecorationDefault::titleBarHeight(const QWidget *) { - return qMax(20, QApplication::fontMetrics().lineSpacing() + BORDER_WIDTH); + return qMax(20, QApplication::fontMetrics().height() + BORDER_WIDTH); } /*! diff --git a/src/gui/embedded/qdecorationwindows_qws.cpp b/src/gui/embedded/qdecorationwindows_qws.cpp index 77393cf..7764ca5 100644 --- a/src/gui/embedded/qdecorationwindows_qws.cpp +++ b/src/gui/embedded/qdecorationwindows_qws.cpp @@ -216,7 +216,7 @@ QRegion QDecorationWindows::region(const QWidget *widget, const QRect &rect, int bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; const QFontMetrics fontMetrics = QApplication::fontMetrics(); - int titleHeight = hasTitle ? qMax(20, fontMetrics.lineSpacing()) : 0; + int titleHeight = hasTitle ? qMax(20, fontMetrics.height()) : 0; int state = widget->windowState(); bool isMinimized = state & Qt::WindowMinimized; bool isMaximized = state & Qt::WindowMaximized; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ffbb67c..e21cd99 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -48,36 +48,60 @@ \ingroup geomanagement \ingroup graphicsview-api - The anchor layout is a layout where one can specify how widgets should be placed relative to - each other. The specification is called an anchor, and it is set up by calling anchor(). + The anchor layout allows developers to specify how widgets should be placed relative to + each other, and to the layout itself. The specification is made by adding anchors to the + layout by calling addAnchor(), addAnchors() or addCornerAnchors(). + + Existing anchors in the layout can be accessed with the anchor() function. + Items that are anchored are automatically added to the layout, and if items + are removed, all their anchors will be automatically removed. + + \beginfloatleft + \inlineimage simpleanchorlayout-example.png Using an anchor layout to align simple colored widgets. + \endfloat + Anchors are always set up between edges of an item, where the "center" is also considered to - be an edge. Considering this example: - \code - QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; - QGraphicsWidget *a = new QGraphicsWidget; - QGraphicsWidget *b = new QGraphicsWidget; - l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); - \endcode - - Here is the right edge of item A anchored to the left edge of item B, with the result that - item B will be placed to the right of item A, with a spacing between A and B. If the - spacing is negative, the items will overlap to some extent. Items that are anchored are - automatically added to the layout, and if items are removed, all their anchors will be - automatically removed - - \section1 Size Hints and Size Policies in QGraphicsAnchorLayout + be an edge. Consider the following example: + + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors + + Here, the right edge of item \c a is anchored to the left edge of item \c b and the bottom + edge of item \c a is anchored to the top edge of item \c b, with the result that + item \c b will be placed diagonally to the right and below item \c b. + + The addCornerAnchors() function provides a simpler way of anchoring the corners + of two widgets than the two individual calls to addAnchor() shown in the code + above. Here, we see how a widget can be anchored to the top-left corner of the enclosing + layout: + + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor + + In cases where anchors are used to match the widths or heights of widgets, it is + convenient to use the addAnchors() function. As with the other functions for specifying + anchors, it can also be used to anchor a widget to a layout. + + \clearfloat + \section1 Size Hints and Size Policies in an Anchor Layout QGraphicsAnchorLayout respects each item's size hints and size policies. However it does - not respect stretch factors currently. This might change in the future, so please refrain - from using stretch factors in anchor layout to avoid any future regressions. + not currently respect their stretch factors. This might change in the future, so avoid + using stretch factors in anchor layouts if you want to avoid any future regressions in + behavior. + + \section1 Spacing within an Anchor Layout - \section1 Spacing within QGraphicsAnchorLayout + The layout may distribute some space between the items. If the spacing has not been + explicitly specified, the actual amount of space will usually be 0. - Between the items, the layout can distribute some space. If the spacing has not been - explicitly specified, the actual amount of space will usually be 0, but if the first edge - is the "opposite" of the second edge (i.e. Right is anchored to Left or vice-versa), the - size of the anchor will be queried from the style through the pixelMetric - PM_LayoutHorizontalSpacing (or PM_LayoutVerticalSpacing for vertical anchors). + However, if the first edge is the \e opposite of the second edge (e.g., the right edge + of the first widget is anchored to the left edge of the second widget), the size of the + anchor will be queried from the style through a pixel metric: + \l{QStyle::}{PM_LayoutHorizontalSpacing} for horizontal anchors and + \l{QStyle::}{PM_LayoutVerticalSpacing} for vertical anchors. + + If the spacing is negative, the items will overlap to some extent. + + \sa QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayout */ /*! @@ -118,13 +142,40 @@ QGraphicsAnchor::~QGraphicsAnchor() } /*! + \property QGraphicsAnchor::sizePolicy + \brief the size policy for the QGraphicsAnchor. + + By setting the size policy on an anchor you can configure how the item can resize itself + from its preferred spacing. For instance, if the anchor has the size policy + QSizePolicy::Minimum, the spacing is the minimum size of the anchor. However, its size + can grow up to the anchors maximum size. If the default size policy is QSizePolicy::Fixed, + the anchor can neither grow or shrink, which means that the only size the anchor can have + is the spacing. QSizePolicy::Fixed is the default size policy. + QGraphicsAnchor always has a minimum spacing of 0 and a very large maximum spacing. + + \sa QGraphicsAnchor::spacing +*/ + +void QGraphicsAnchor::setSizePolicy(QSizePolicy::Policy policy) +{ + Q_D(QGraphicsAnchor); + d->setSizePolicy(policy); +} + +QSizePolicy::Policy QGraphicsAnchor::sizePolicy() const +{ + Q_D(const QGraphicsAnchor); + return d->sizePolicy; +} + +/*! \property QGraphicsAnchor::spacing - \brief the space between items in the QGraphicsAnchorLayout. + \brief the preferred space between items in the QGraphicsAnchorLayout. Depending on the anchor type, the default spacing is either 0 or a value returned from the style. - \sa QGraphicsAnchorLayout::anchor() + \sa QGraphicsAnchorLayout::addAnchor() */ void QGraphicsAnchor::setSpacing(qreal spacing) { @@ -188,7 +239,7 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() If there is already an anchor between the edges, the the new anchor will replace the old one. \a firstItem and \a secondItem are automatically added to the layout if they are not part - of the layout. This means that count() can increase with up to 2. + of the layout. This means that count() can increase by up to 2. The spacing an anchor will get depends on the type of anchor. For instance, anchors from the Right edge of one item to the Left edge of another (or vice versa) will use the default @@ -198,7 +249,10 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() The spacing can also be set manually by using QGraphicsAnchor::setSpacing() method. - \sa addCornerAnchors(), addAnchors() + Calling this function where \a firstItem or \a secondItem are ancestors of the layout have + undefined behaviour. + + \sa addAnchors(), addCornerAnchors() */ QGraphicsAnchor * QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, @@ -223,29 +277,26 @@ QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint fi } /*! - Creates two anchors between \a firstItem and \a secondItem, where one is for the horizontal - edge and another one for the vertical edge that the corners \a firstCorner and \a - secondCorner specifies. - The magnitude of the anchors is picked up from the style. + Creates two anchors between \a firstItem and \a secondItem specified by the corners, + \a firstCorner and \a secondCorner, where one is for the horizontal edge and another + one for the vertical edge. - This is a convenience function, since anchoring corners can be expressed as anchoring two edges. - For instance, - \code - layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop); - layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft); - \endcode + This is a convenience function, since anchoring corners can be expressed as anchoring + two edges. For instance: - has the same effect as + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor in two steps - \code - layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft); - \endcode + This can also be achieved with the following line of code: + + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor If there is already an anchor between the edge pairs, it will be replaced by the anchors that this function specifies. \a firstItem and \a secondItem are automatically added to the layout if they are not part of the - layout. This means that count() can increase with up to 2. + layout. This means that count() can increase by up to 2. + + \sa addAnchor(), addAnchors() */ void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, @@ -272,17 +323,16 @@ void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, edges of \a secondItem, so that \a firstItem has the same size as \a secondItem in the dimensions specified by \a orientations. - Calling this convenience function with the following arguments - \code - l->addAnchors(firstItem, secondItem, Qt::Horizontal) - \endcode + For example, the following example anchors the left and right edges of two items + to match their widths: + + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes in two steps - is the same as + This can also be achieved using the following line of code: - \code - l->addAnchor(firstItem, Qt::AnchorLeft, secondItem, Qt::AnchorLeft); - l->addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight); - \endcode + \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes + + \sa addAnchor(), addCornerAnchors() */ void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem, QGraphicsLayoutItem *secondItem, @@ -306,6 +356,13 @@ void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem, void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = spacing; invalidate(); } @@ -318,6 +375,13 @@ void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[1] = spacing; invalidate(); } @@ -327,11 +391,23 @@ void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) If an item is anchored with no spacing associated with the anchor, it will use the default spacing. + + Currently QGraphicsAnchorLayout does not support negative default spacings. + \sa setHorizontalSpacing(), setVerticalSpacing() */ void QGraphicsAnchorLayout::setSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative spacing, we must + // make this test. + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = d->spacings[1] = spacing; invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 99dbf92..01c3a86 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -62,10 +62,13 @@ class Q_GUI_EXPORT QGraphicsAnchor : public QObject { Q_OBJECT Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET unsetSpacing) + Q_PROPERTY(QSizePolicy::Policy sizePolicy READ sizePolicy WRITE setSizePolicy) public: void setSpacing(qreal spacing); void unsetSpacing(); qreal spacing() const; + void setSizePolicy(QSizePolicy::Policy policy); + QSizePolicy::Policy sizePolicy() const; ~QGraphicsAnchor(); private: QGraphicsAnchor(QGraphicsAnchorLayout *parent); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f9b5c8c..8c8c303 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -53,7 +53,8 @@ QT_BEGIN_NAMESPACE QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version) - : QObjectPrivate(version), layoutPrivate(0), data(0) + : QObjectPrivate(version), layoutPrivate(0), data(0), + sizePolicy(QSizePolicy::Fixed) { } @@ -62,6 +63,14 @@ QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate() layoutPrivate->removeAnchor(data->from, data->to); } +void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy) +{ + if (sizePolicy != policy) { + sizePolicy = policy; + layoutPrivate->q_func()->invalidate(); + } +} + void QGraphicsAnchorPrivate::setSpacing(qreal value) { if (data) { @@ -92,28 +101,11 @@ qreal QGraphicsAnchorPrivate::spacing() const } -static void sizeHintsFromItem(QGraphicsLayoutItem *item, - const QGraphicsAnchorLayoutPrivate::Orientation orient, - qreal *minSize, qreal *prefSize, - qreal *expSize, qreal *maxSize) +static void internalSizeHints(QSizePolicy::Policy policy, + qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, + qreal *minSize, qreal *prefSize, + qreal *expSize, qreal *maxSize) { - QSizePolicy::Policy policy; - qreal minSizeHint; - qreal prefSizeHint; - qreal maxSizeHint; - - if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) { - policy = item->sizePolicy().horizontalPolicy(); - minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width(); - prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width(); - maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width(); - } else { - policy = item->sizePolicy().verticalPolicy(); - minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); - prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); - maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); - } - // minSize, prefSize and maxSize are initialized // with item's preferred Size: this is QSizePolicy::Fixed. // @@ -139,7 +131,7 @@ static void sizeHintsFromItem(QGraphicsLayoutItem *item, // Note that these two initializations are affected by the previous flags if (policy & QSizePolicy::IgnoreFlag) - *prefSize = *maxSize; + *prefSize = *minSize; else *prefSize = prefSizeHint; @@ -153,38 +145,63 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) { const bool isInternalAnchor = from->m_item == to->m_item; + QSizePolicy::Policy policy; + qreal minSizeHint; + qreal prefSizeHint; + qreal maxSizeHint; + if (isInternalAnchor) { const QGraphicsAnchorLayoutPrivate::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); + const Qt::AnchorPoint centerEdge = + QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); + bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); if (isLayoutAnchor) { minSize = 0; prefSize = 0; expSize = 0; maxSize = QWIDGETSIZE_MAX; + if (hasCenter) + maxSize /= 2; + return; } else { - QGraphicsLayoutItem *item = from->m_item; - sizeHintsFromItem(item, orient, &minSize, &prefSize, &expSize, &maxSize); - } - const Qt::AnchorPoint centerEdge = - QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); - bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); + QGraphicsLayoutItem *item = from->m_item; + if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) { + policy = item->sizePolicy().horizontalPolicy(); + minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width(); + prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width(); + maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width(); + } else { + policy = item->sizePolicy().verticalPolicy(); + minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); + prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); + maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); + } - if (hasCenter) { - minSize /= 2; - prefSize /= 2; - expSize /= 2; - maxSize /= 2; + if (hasCenter) { + minSizeHint /= 2; + prefSizeHint /= 2; + maxSizeHint /= 2; + } } - - } else if (!hasSize) { - // Anchor has no size defined, use given default information - minSize = effectiveSpacing; - prefSize = effectiveSpacing; - expSize = effectiveSpacing; - maxSize = effectiveSpacing; + } else { + Q_ASSERT(graphicsAnchor); + policy = graphicsAnchor->sizePolicy(); + minSizeHint = 0; + if (hasSize) { + // One can only configure the preferred size of a normal anchor. Their minimum and + // maximum "size hints" are always 0 and QWIDGETSIZE_MAX, correspondingly. However, + // their effective size hints might be narrowed down due to their size policies. + prefSizeHint = prefSize; + } else { + prefSizeHint = effectiveSpacing; + } + maxSizeHint = QWIDGETSIZE_MAX; } + internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, + &minSize, &prefSize, &expSize, &maxSize); // Set the anchor effective sizes to preferred. // @@ -250,44 +267,61 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, 0 is at Preferred 1 is at Maximum */ -static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) -{ - // ### Maybe remove some of the assertions? (since outside is asserting us) - Q_ASSERT(value > min || qFuzzyCompare(value, min)); - Q_ASSERT(value < max || qFuzzyCompare(value, max)); - - if (qFuzzyCompare(value, min)) { - return -1.0; - } else if (qFuzzyCompare(value, pref)) { - return 0.0; - } else if (qFuzzyCompare(value, max)) { - return 1.0; - } else if (value < pref) { - // Since value < pref and value != pref and min <= value, - // we can assert that min < pref. - Q_ASSERT(min < pref); - return (value - min) / (pref - min) - 1; +static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, + qreal pref, qreal exp, + qreal max) +{ + QGraphicsAnchorLayoutPrivate::Interval interval; + qreal lower; + qreal upper; + + if (value < pref) { + interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; + lower = min; + upper = pref; + } else if (value < exp) { + interval = QGraphicsAnchorLayoutPrivate::PreferredToExpanding; + lower = pref; + upper = exp; } else { - // Since value > pref and value != pref and max >= value, - // we can assert that max > pref. - Q_ASSERT(max > pref); - return (value - pref) / (max - pref); + interval = QGraphicsAnchorLayoutPrivate::ExpandingToMax; + lower = exp; + upper = max; } + + qreal progress; + if (upper == lower) { + progress = 0; + } else { + progress = (value - lower) / (upper - lower); + } + + return qMakePair(interval, progress); } -static qreal getExpandingFactor(const qreal &expSize, const qreal &sizeAtPreferred, - const qreal &sizeAtExpanding, const qreal &sizeAtMaximum) +static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, + qreal min, qreal pref, + qreal exp, qreal max) { - const qreal lower = qMin(sizeAtPreferred, sizeAtMaximum); - const qreal upper = qMax(sizeAtPreferred, sizeAtMaximum); - const qreal boundedExpSize = qBound(lower, expSize, upper); + qreal lower; + qreal upper; - const qreal bandSize = sizeAtMaximum - boundedExpSize; - if (bandSize == 0) { - return 0; - } else { - return (sizeAtExpanding - boundedExpSize) / bandSize; + switch (factor.first) { + case QGraphicsAnchorLayoutPrivate::MinToPreferred: + lower = min; + upper = pref; + break; + case QGraphicsAnchorLayoutPrivate::PreferredToExpanding: + lower = pref; + upper = exp; + break; + case QGraphicsAnchorLayoutPrivate::ExpandingToMax: + lower = exp; + upper = max; + break; } + + return lower + factor.second * (upper - lower); } void SequentialAnchorData::updateChildrenSizes() @@ -307,27 +341,22 @@ void SequentialAnchorData::updateChildrenSizes() // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). - const qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); - const qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); - const qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); - const qreal expFactor = getExpandingFactor(expSize, sizeAtPreferred, sizeAtExpanding, sizeAtMaximum); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = + getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = + getFactor(sizeAtPreferred, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> expFactor = + getFactor(sizeAtExpanding, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = + getFactor(sizeAtMaximum, minSize, prefSize, expSize, maxSize); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMinimum = e->prefSize + bandSize * minFactor; - - bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; - - bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; - - const qreal lower = qMin(e->sizeAtPreferred, e->sizeAtMaximum); - const qreal upper = qMax(e->sizeAtPreferred, e->sizeAtMaximum); - const qreal edgeBoundedExpSize = qBound(lower, e->expSize, upper); - e->sizeAtExpanding = edgeBoundedExpSize + expFactor * (e->sizeAtMaximum - edgeBoundedExpSize); + e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtExpanding = interpolate(expFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); e->updateChildrenSizes(); } @@ -494,36 +523,46 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, const QVector<AnchorVertex*> &vertices, AnchorVertex *after) { - int i; + AnchorData *data = graph->edgeData(before, vertices.first()); + Q_ASSERT(data); + + const bool forward = (before == data->from); + QVector<AnchorVertex *> orderedVertices; + + if (forward) { + orderedVertices = vertices; + } else { + qSwap(before, after); + for (int i = vertices.count() - 1; i >= 0; --i) + orderedVertices.append(vertices.at(i)); + } + #if defined(QT_DEBUG) && 0 QString strVertices; - for (i = 0; i < vertices.count(); ++i) - strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); + for (int i = 0; i < orderedVertices.count(); ++i) { + strVertices += QString::fromAscii("%1 - ").arg(orderedVertices.at(i)->toString()); + } QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); #endif SequentialAnchorData *sequence = new SequentialAnchorData; AnchorVertex *prev = before; - AnchorData *data; - for (i = 0; i <= vertices.count(); ++i) { - AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after; - data = graph->takeEdge(prev, next); - sequence->m_edges.append(data); + + for (int i = 0; i <= orderedVertices.count(); ++i) { + AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; + AnchorData *ad = graph->takeEdge(prev, next); + Q_ASSERT(ad); + sequence->m_edges.append(ad); prev = next; } - sequence->setVertices(vertices); + + sequence->setVertices(orderedVertices); sequence->from = before; sequence->to = after; sequence->refreshSizeHints_helper(0, false); - // data here is the last edge in the sequence - // ### this seems to be here for supporting reverse order sequences, - // but doesnt seem to be used right now - if (data->from != vertices.last()) - qSwap(sequence->from, sequence->to); - // Note that since layout 'edges' can't be simplified away from // the graph, it's safe to assume that if there's a layout // 'edge', it'll be in the boundaries of the sequence. @@ -578,15 +617,6 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, 2. Go to (1) 3. Done - - * Gathering sequential anchors * - The algorithm walks the graph in depth-first order, and only collects vertices that has two - edges connected to it. If the vertex does not have two edges or if it is a layout edge, - it will take all the previously collected vertices and try to create a simplified sequential - anchor representing all the previously collected vertices. - Once the simplified anchor is inserted, the collected list is cleared in order to find the next - sequence to simplify. - Note that there are some catches to this that are not covered by the above explanation. */ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { @@ -603,9 +633,7 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - AnchorVertex *rootVertex = graph[orientation].rootVertex(); - - if (!rootVertex) + if (!graph[orientation].rootVertex()) return; bool dirty; @@ -614,164 +642,171 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) } while (dirty); } +/*! + \internal + + One iteration of the simplification algorithm. Returns true if another iteration is needed. + + The algorithm walks the graph in depth-first order, and only collects vertices that has two + edges connected to it. If the vertex does not have two edges or if it is a layout edge, it + will take all the previously collected vertices and try to create a simplified sequential + anchor representing all the previously collected vertices. Once the simplified anchor is + inserted, the collected list is cleared in order to find the next sequence to simplify. + + Note that there are some catches to this that are not covered by the above explanation, see + the function comments for more details. +*/ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) { Q_Q(QGraphicsAnchorLayout); Graph<AnchorVertex, AnchorData> &g = graph[orientation]; - AnchorVertex *v = g.rootVertex(); QSet<AnchorVertex *> visited; - QStack<AnchorVertex *> stack; - stack.push(v); + QStack<QPair<AnchorVertex *, AnchorVertex *> > stack; + stack.push(qMakePair(static_cast<AnchorVertex *>(0), g.rootVertex())); QVector<AnchorVertex*> candidates; + bool candidatesForward; const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); - const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge); - bool dirty = false; - - // walk depth-first. + // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence) + // and the vertex to be visited. while (!stack.isEmpty()) { - v = stack.pop(); - QList<AnchorVertex *> vertices = g.adjacentVertices(v); - const int count = vertices.count(); - bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2; - if (count == 2 && v->m_item != q) { - candidates.append(v); - if (visited.contains(vertices.first()) && visited.contains(vertices.last())) { - // in case of a cycle - endOfSequence = true; + QPair<AnchorVertex *, AnchorVertex *> pair = stack.pop(); + AnchorVertex *beforeSequence = pair.first; + AnchorVertex *v = pair.second; + + // The basic idea is to determine whether we found an end of sequence, + // if that's the case, we stop adding vertices to the candidate list + // and do a simplification step. + // + // A vertex can trigger an end of sequence if + // (a) it is a layout vertex, we don't simplify away the layout vertices; + // (b) it does not have exactly 2 adjacents; + // (c) it will change the direction of the sequence; + // (d) its next adjacent is already visited (a cycle in the graph). + + const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v); + const bool isLayoutVertex = v->m_item == q; + AnchorVertex *afterSequence = v; + bool endOfSequence = false; + + // + // Identify the end cases. + // + + // Identifies cases (a) and (b) + endOfSequence = isLayoutVertex || adjacents.count() != 2; + + if (!endOfSequence) { + // If this is the first vertice, determine what is the direction to use for this + // sequence. + if (candidates.isEmpty()) { + const AnchorData *data = g.edgeData(beforeSequence, v); + Q_ASSERT(data); + candidatesForward = (beforeSequence == data->from); } - } - if (endOfSequence && candidates.count() >= 1) { - int i; - AnchorVertex *afterSequence= 0; - AnchorVertex *beforeSequence = 0; - // find the items before and after the valid sequence - if (candidates.count() == 1) { - QList<AnchorVertex *> beforeAndAfterVertices = g.adjacentVertices(candidates.at(0)); - Q_ASSERT(beforeAndAfterVertices.count() == 2); - // Since we only have one vertex, we can pick - // any of the two vertices to become before/after. - afterSequence = beforeAndAfterVertices.last(); - beforeSequence = beforeAndAfterVertices.first(); - } else { - QList<AnchorVertex *> adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); - Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); - if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) - afterSequence = adjacentOfSecondLastVertex.last(); - else - afterSequence = adjacentOfSecondLastVertex.first(); - QList<AnchorVertex *> adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); - Q_ASSERT(adjacentOfSecondVertex.count() == 2); - if (adjacentOfSecondVertex.first() == candidates.at(1)) - beforeSequence = adjacentOfSecondVertex.last(); - else - beforeSequence = adjacentOfSecondVertex.first(); + // This is a tricky part. We peek at the next vertex to find out + // + // - whether the edge from this vertex to the next vertex has the same direction; + // - whether we already visited the next vertex. + // + // Those are needed to identify (c) and (d). Note that unlike (a) and (b), we preempt + // the end of sequence by looking into the next vertex. + + // Peek at the next vertex + AnchorVertex *after; + if (candidates.isEmpty()) + after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last()); + else + after = (candidates.last() == adjacents.last() ? adjacents.first() : adjacents.last()); + + // ### At this point we assumed that candidates will not contain 'after', this may not hold + // when simplifying FLOATing anchors. + Q_ASSERT(!candidates.contains(after)); + + const AnchorData *data = g.edgeData(v, after); + Q_ASSERT(data); + const bool willChangeDirection = (candidatesForward != (v == data->from)); + const bool cycleFound = visited.contains(after); + + // Now cases (c) and (d)... + endOfSequence = willChangeDirection || cycleFound; + + if (endOfSequence) { + if (!willChangeDirection) { + // If the direction will not change, we can add the current vertex to the + // candidates list and we know that 'after' can be used as afterSequence. + candidates.append(v); + afterSequence = after; + } + } else { + // If it's not an end of sequence, then the vertex didn't trigger neither of the + // previously four cases, so it can be added to the candidates list. + candidates.append(v); } - // The complete path of the sequence to simplify is: beforeSequence, <candidates>, afterSequence - // where beforeSequence and afterSequence are the endpoints where the anchor is inserted - // between. -#if defined(QT_DEBUG) && 0 - // ### DEBUG - QString strCandidates; - for (i = 0; i < candidates.count(); ++i) - strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); - QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); - qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath)); -#endif + } - bool forward = true; - AnchorVertex *prev = beforeSequence; - int intervalFrom = 0; + // + // Add next non-visited vertices to the stack. + // + for (int i = 0; i < adjacents.count(); ++i) { + AnchorVertex *next = adjacents.at(i); + if (visited.contains(next)) + continue; - // Check for directionality (from). We don't want to destroy that information, - // thus we only combine anchors with the same direction. + // If current vertex is an end of sequence, and it'll reset the candidates list. So + // the next vertices will build candidates lists with the current vertex as 'before' + // vertex. If it's not an end of sequence, we keep the original 'before' vertex, + // since we are keeping the candidates list. + if (endOfSequence) + stack.push(qMakePair(v, next)); + else + stack.push(qMakePair(beforeSequence, next)); + } - // "i" is the index *including* the beforeSequence and afterSequence vertices. - for (i = 1; i <= candidates.count() + 1; ++i) { - bool atVertexAfter = i > candidates.count(); - AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1); - AnchorData *data = g.edgeData(prev, v1); - Q_ASSERT(data); - if (i == 1) { - forward = (prev == data->from ? true : false); - } else if (forward != (prev == data->from) || atVertexAfter) { - int intervalTo = i; - if (forward != (prev == data->from)) - --intervalTo; - - // intervalFrom and intervalTo should now be indices to the vertex before and - // after the sequential anchor. - if (intervalTo - intervalFrom >= 2) { - // simplify in the range [intervalFrom, intervalTo] - - // Trim off internal center anchors (Left-Center/Center-Right) from the - // start and the end of the sequence. We never want to simplify internal - // center anchors where there is an external anchor connected to the center. - AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); - int effectiveIntervalFrom = intervalFrom; - if (intervalVertexFrom->m_edge == centerEdge - && intervalVertexFrom->m_item == candidates.at(effectiveIntervalFrom)->m_item) { - ++effectiveIntervalFrom; - intervalVertexFrom = candidates.at(effectiveIntervalFrom - 1); - } - AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; - int effectiveIntervalTo = intervalTo; - if (intervalVertexTo->m_edge == centerEdge - && intervalVertexTo->m_item == candidates.at(effectiveIntervalTo - 2)->m_item) { - --effectiveIntervalTo; - intervalVertexTo = candidates.at(effectiveIntervalTo - 1); - } - if (effectiveIntervalTo - effectiveIntervalFrom >= 2) { - QVector<AnchorVertex*> subCandidates; - if (forward) { - subCandidates = candidates.mid(effectiveIntervalFrom, effectiveIntervalTo - effectiveIntervalFrom - 1); - } else { - // reverse the order of the candidates. - qSwap(intervalVertexFrom, intervalVertexTo); - do { - ++effectiveIntervalFrom; - subCandidates.prepend(candidates.at(effectiveIntervalFrom - 1)); - } while (effectiveIntervalFrom < effectiveIntervalTo - 1); - } - if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) { - dirty = true; - break; - } - // finished simplification of chunk with same direction - } - } - if (forward == (prev == data->from)) - --intervalTo; - intervalFrom = intervalTo; - - forward = !forward; - } - prev = v1; - } + visited.insert(v); - if (dirty) - break; - } + if (!endOfSequence || candidates.isEmpty()) + continue; - if (endOfSequence) - candidates.clear(); + // + // Create a sequence for (beforeSequence, candidates, afterSequence). + // - for (int i = 0; i < count; ++i) { - AnchorVertex *next = vertices.at(i); - if (next->m_item == q && next->m_edge == centerEdge) + // One restriction we have is to not simplify half of an anchor and let the other half + // unsimplified. So we remove center edges before and after the sequence. + if (beforeSequence->m_edge == centerEdge && beforeSequence->m_item == candidates.first()->m_item) { + beforeSequence = candidates.first(); + candidates.remove(0); + + // If there's not candidates to be simplified, leave. + if (candidates.isEmpty()) continue; - if (visited.contains(next)) + } + + if (afterSequence->m_edge == centerEdge && afterSequence->m_item == candidates.last()->m_item) { + afterSequence = candidates.last(); + candidates.remove(candidates.count() - 1); + + if (candidates.isEmpty()) continue; - stack.push(next); } - visited.insert(v); + // This function will remove the candidates from the graph and create one edge between + // beforeSequence and afterSequence. This function returns true if the sequential + // simplification also caused a parallel simplification to be created. In this case we end + // the iteration and start again (since all the visited state we have may be outdated). + if (simplifySequentialChunk(&g, beforeSequence, candidates, afterSequence)) + return true; + + // If there was no parallel simplification, we'll keep walking the graph. So we clear the + // candidates list to start again. + candidates.clear(); } - return dirty; + return false; } static void restoreSimplifiedAnchor(Graph<AnchorVertex, AnchorData> &g, @@ -1173,18 +1208,18 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *fi || secondItem == q || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter || oppositeEdge(firstEdge) != secondEdge) { - data->setFixedSize(0); + data->setPreferredSize(0); } else { data->unsetSize(); } addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); } else if (*spacing >= 0) { - data->setFixedSize(*spacing); + data->setPreferredSize(*spacing); addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); } else { - data->setFixedSize(-*spacing); + data->setPreferredSize(-*spacing); addAnchor_helper(secondItem, secondEdge, firstItem, firstEdge, data); } @@ -1371,9 +1406,9 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSize(AnchorData *data, const qreal * // positive by definition. // "negative spacing" is handled by inverting the standard item order. if (*anchorSize >= 0) { - data->setFixedSize(*anchorSize); + data->setPreferredSize(*anchorSize); } else { - data->setFixedSize(-*anchorSize); + data->setPreferredSize(-*anchorSize); qSwap(data->from, data->to); } } else { @@ -1550,6 +1585,13 @@ qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) co } } } + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative style spacing, we must + // make this test. + if (s < 0) + s = 0; + return s; } @@ -1565,13 +1607,24 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; +#if defined(QT_DEBUG) && 0 + static int count = 0; + count++; + dumpGraph(QString::fromAscii("%1-before").arg(count)); +#endif + calculateGraphs(Horizontal); calculateGraphs(Vertical); +#if defined(QT_DEBUG) && 0 + dumpGraph(QString::fromAscii("%1-after").arg(count)); +#endif + calculateGraphCacheDirty = 0; } -// ### remove me: +// ### Maybe getGraphParts could return the variables when traversing, at least +// for trunk... QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) { QSet<AnchorData *> variableSet; @@ -1635,65 +1688,92 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // 2) The floating or semi-floating anchors (items) that are those which // are connected to only one (or none) of the layout sides, thus are not // influenced by the layout size. - QList<QList<QSimplexConstraint *> > parts; - parts = getGraphParts(orientation); + QList<QList<QSimplexConstraint *> > parts = getGraphParts(orientation); // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes // of the "trunk" set of constraints and variables. // ### does trunk always exist? empty = trunk is the layout left->center->right QList<QSimplexConstraint *> trunkConstraints = parts[0]; - QList<QSimplexConstraint *> sizeHintConstraints; - sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); - trunkConstraints += sizeHintConstraints; + QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); // For minimum and maximum, use the path between the two layout sides as the // objective function. - - // Retrieve that path AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); GraphPath trunkPath = graphPaths[orientation].value(v); + bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables); + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Skipping the first (trunk) + for (int i = 1; i < parts.count(); ++i) { + if (!feasible) + break; + + QList<QSimplexConstraint *> partConstraints = parts[i]; + QList<AnchorData *> partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); + feasible &= calculateNonTrunk(partConstraints, partVariables); + } + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + updateAnchorSizes(orientation); + + graphHasConflicts[orientation] = !feasible; + + // Clean up our data structures. They are not needed anymore since + // distribution uses just interpolation. + qDeleteAll(constraints[orientation]); + constraints[orientation].clear(); + graphPaths[orientation].clear(); // ### +} + +/*! + \internal + + Calculate the sizes for all anchors which are part of the trunk. This works + on top of a (possibly) simplified graph. +*/ +bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const GraphPath &path, + const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) +{ bool feasible = true; - if (!trunkConstraints.isEmpty()) { + bool needsSimplex = !constraints.isEmpty(); + #if 0 - qDebug("Simplex used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); + qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used", + orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - // Solve min and max size hints for trunk - qreal min, max; - feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); + if (needsSimplex) { - if (feasible) { - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - solvePreferred(trunkConstraints); + QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); + QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; - // remove sizeHintConstraints from trunkConstraints - trunkConstraints = parts[0]; - - // Solve for expanding. The objective function and the constraints from items - // are calculated internally. - solveExpanding(trunkConstraints); - - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. + // Solve min and max size hints + qreal min, max; + feasible = solveMinMax(allConstraints, path, &min, &max); - // ### we calculated variables already a few times, can't we reuse that? - QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); + if (feasible) { + solvePreferred(allConstraints, variables); - for (int i = 0; i < trunkVariables.count(); ++i) - trunkVariables.at(i)->updateChildrenSizes(); + // Note that we don't include the sizeHintConstraints, since they + // have a different logic for solveExpanding(). + solveExpanding(constraints, variables); // Calculate and set the preferred and expanding sizes for the layout, // from the edge sizes that were calculated above. qreal pref(0.0); qreal expanding(0.0); - foreach (const AnchorData *ad, trunkPath.positives) { + foreach (const AnchorData *ad, path.positives) { pref += ad->sizeAtPreferred; expanding += ad->sizeAtExpanding; } - foreach (const AnchorData *ad, trunkPath.negatives) { + foreach (const AnchorData *ad, path.negatives) { pref -= ad->sizeAtPreferred; expanding -= ad->sizeAtExpanding; } @@ -1703,76 +1783,57 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MaximumSize] = max; sizeAtExpanding[orientation] = expanding; } - } else { -#if 0 - qDebug("Simplex NOT used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif + qDeleteAll(sizeHintConstraints); + + } else { // No Simplex is necessary because the path was simplified all the way to a single // anchor. - Q_ASSERT(trunkPath.positives.count() == 1); - Q_ASSERT(trunkPath.negatives.count() == 0); + Q_ASSERT(path.positives.count() == 1); + Q_ASSERT(path.negatives.count() == 0); - AnchorData *ad = trunkPath.positives.toList()[0]; + AnchorData *ad = path.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; ad->sizeAtExpanding = ad->expSize; ad->sizeAtMaximum = ad->maxSize; - // Propagate - ad->updateChildrenSizes(); - sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) + lastCalculationUsedSimplex[orientation] = needsSimplex; +#endif - // For the other parts that not the trunk, solve only for the preferred size - // that is the size they will remain at, since they are not stretched by the - // layout. + return feasible; +} - // Solve the other only for preferred, skip trunk - if (feasible) { - for (int i = 1; i < parts.count(); ++i) { - QList<QSimplexConstraint *> partConstraints = parts[i]; - QList<AnchorData *> partVariables = getVariables(partConstraints); - Q_ASSERT(!partVariables.isEmpty()); - - sizeHintConstraints = constraintsFromSizeHints(partVariables); - partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints); - if (!feasible) - break; - - // Propagate size at preferred to other sizes. Semi-floats - // always will be in their sizeAtPreferred. - for (int j = 0; j < partVariables.count(); ++j) { - AnchorData *ad = partVariables[j]; - Q_ASSERT(ad); - ad->sizeAtMinimum = ad->sizeAtPreferred; - ad->sizeAtExpanding = ad->sizeAtPreferred; - ad->sizeAtMaximum = ad->sizeAtPreferred; - ad->updateChildrenSizes(); - } +/*! + \internal +*/ +bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) +{ + QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); + bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); + if (feasible) { + // Propagate size at preferred to other sizes. Semi-floats always will be + // in their sizeAtPreferred. + for (int j = 0; j < variables.count(); ++j) { + AnchorData *ad = variables[j]; + Q_ASSERT(ad); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtExpanding = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; } } - graphHasConflicts[orientation] = !feasible; - // Clean up our data structures. They are not needed anymore since - // distribution uses just interpolation. - qDeleteAll(constraints[orientation]); - constraints[orientation].clear(); - graphPaths[orientation].clear(); // ### + qDeleteAll(sizeHintConstraints); + return feasible; } /*! @@ -1876,6 +1937,20 @@ void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) /*! \internal +*/ +void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation) +{ + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + const QList<QPair<AnchorVertex *, AnchorVertex *> > &vertices = g.connections(); + + for (int i = 0; i < vertices.count(); ++i) { + AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second); + ad->updateChildrenSizes(); + } +} + +/*! + \internal Create LP constraints for each anchor based on its minimum and maximum sizes, as specified in its size hints @@ -2160,39 +2235,26 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( \internal Calculate interpolation parameters based on current Layout Size. - Must once before calling "interpolateEdgeSize()" for each edge. + Must be called once before calling "interpolateEdgeSize()" for + the edges. */ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( Orientation orientation) { Q_Q(QGraphicsAnchorLayout); - qreal lower, upper, current; - if (orientation == Horizontal) { - current = q->contentsRect().width(); - } else { - current = q->contentsRect().height(); - } + qreal current; + current = (orientation == Horizontal) ? q->contentsRect().width() : q->contentsRect().height(); - if (current < sizeHints[orientation][Qt::PreferredSize]) { - interpolationInterval[orientation] = MinToPreferred; - lower = sizeHints[orientation][Qt::MinimumSize]; - upper = sizeHints[orientation][Qt::PreferredSize]; - } else if (current < sizeAtExpanding[orientation]) { - interpolationInterval[orientation] = PreferredToExpanding; - lower = sizeHints[orientation][Qt::PreferredSize]; - upper = sizeAtExpanding[orientation]; - } else { - interpolationInterval[orientation] = ExpandingToMax; - lower = sizeAtExpanding[orientation]; - upper = sizeHints[orientation][Qt::MaximumSize]; - } + QPair<Interval, qreal> result; + result = getFactor(current, + sizeHints[orientation][Qt::MinimumSize], + sizeHints[orientation][Qt::PreferredSize], + sizeAtExpanding[orientation], + sizeHints[orientation][Qt::MaximumSize]); - if (upper == lower) { - interpolationProgress[orientation] = 0; - } else { - interpolationProgress[orientation] = (current - lower) / (upper - lower); - } + interpolationInterval[orientation] = result.first; + interpolationProgress[orientation] = result.second; } /*! @@ -2219,20 +2281,11 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation) { - qreal lower, upper; + const QPair<Interval, qreal> factor(interpolationInterval[orientation], + interpolationProgress[orientation]); - if (interpolationInterval[orientation] == MinToPreferred) { - lower = edge->sizeAtMinimum; - upper = edge->sizeAtPreferred; - } else if (interpolationInterval[orientation] == PreferredToExpanding) { - lower = edge->sizeAtPreferred; - upper = edge->sizeAtExpanding; - } else { - lower = edge->sizeAtExpanding; - upper = edge->sizeAtMaximum; - } - - qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, + edge->sizeAtExpanding, edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); @@ -2303,7 +2356,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( interpolateEdge(prev, data->m_edges.last(), orientation); } -bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> constraints, +bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints, GraphPath path, qreal *min, qreal *max) { QSimplex simplex; @@ -2344,9 +2397,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> const return feasible; } -bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> constraints) +bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) { - QList<AnchorData *> variables = getVariables(constraints); QList<QSimplexConstraint *> preferredConstraints; QList<QSimplexVariable *> preferredVariables; QSimplexConstraint objective; @@ -2369,7 +2422,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> co // A + A_shrinker - A_grower = A_pref // for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast<AnchorData *>(variables[i]); + AnchorData *ad = variables[i]; if (ad->skipInPreferred) continue; @@ -2400,7 +2453,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> co // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast<AnchorData *>(variables[i]); + AnchorData *ad = variables[i]; ad->sizeAtPreferred = ad->result; } @@ -2461,9 +2514,9 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> co expanding ones will shrink. Only after non-expanding anchors have shrinked all the way, the expanding anchors will start to shrink too. */ -void QGraphicsAnchorLayoutPrivate::solveExpanding(QList<QSimplexConstraint *> constraints) +void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) { - QList<AnchorData *> variables = getVariables(constraints); QList<QSimplexConstraint *> itemConstraints; QSimplexConstraint *objective = new QSimplexConstraint; bool hasExpanding = false; @@ -2566,9 +2619,9 @@ bool QGraphicsAnchorLayoutPrivate::hasConflicts() const } #ifdef QT_DEBUG -void QGraphicsAnchorLayoutPrivate::dumpGraph() +void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name) { - QFile file(QString::fromAscii("anchorlayout.dot")); + QFile file(QString::fromAscii("anchorlayout.%1.dot").arg(name)); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 9ac0e19..d45c004 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -169,16 +169,9 @@ struct AnchorData : public QSimplexVariable { QString name; #endif - inline void setFixedSize(qreal size) + inline void setPreferredSize(qreal size) { - minSize = size; prefSize = size; - expSize = size; - maxSize = size; - sizeAtMinimum = size; - sizeAtPreferred = size; - sizeAtExpanding = size; - sizeAtMaximum = size; hasSize = true; } @@ -316,8 +309,11 @@ public: void unsetSpacing(); qreal spacing() const; + void setSizePolicy(QSizePolicy::Policy policy); + QGraphicsAnchorLayoutPrivate *layoutPrivate; AnchorData *data; + QSizePolicy::Policy sizePolicy; }; @@ -438,9 +434,17 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); + + bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath, + const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); + void updateAnchorSizes(Orientation orientation); QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors); QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation); void identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation); @@ -471,14 +475,16 @@ public: Orientation orientation); // Linear Programming solver methods - bool solveMinMax(QList<QSimplexConstraint *> constraints, + bool solveMinMax(const QList<QSimplexConstraint *> &constraints, GraphPath path, qreal *min, qreal *max); - bool solvePreferred(QList<QSimplexConstraint *> constraints); - void solveExpanding(QList<QSimplexConstraint *> constraints); + bool solvePreferred(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + void solveExpanding(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); bool hasConflicts() const; #ifdef QT_DEBUG - void dumpGraph(); + void dumpGraph(const QString &name = QString()); #endif @@ -513,7 +519,13 @@ public: bool graphHasConflicts[2]; QSet<QGraphicsLayoutItem *> m_floatItems[2]; +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) + bool lastCalculationUsedSimplex[2]; +#endif + uint calculateGraphCacheDirty : 1; + + friend class QGraphicsAnchorPrivate; }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index d1d91db..9a8dba0 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -63,7 +63,7 @@ You can access each item in the layout by calling count() and itemAt(). Calling removeAt() will remove an item from the layout, without destroying it. - + \sa QGraphicsLinearLayout, QGraphicsWidget */ @@ -89,7 +89,7 @@ public: QLayoutStyleInfo styleInfo() const; QGridLayoutEngine engine; -#ifdef QT_DEBUG +#ifdef QT_DEBUG void dump(int indent) const; #endif }; @@ -121,7 +121,7 @@ QGraphicsGridLayout::~QGraphicsGridLayout() for (int i = count() - 1; i >= 0; --i) { QGraphicsLayoutItem *item = itemAt(i); // The following lines can be removed, but this removes the item - // from the layout more efficiently than the implementation of + // from the layout more efficiently than the implementation of // ~QGraphicsLayoutItem. removeAt(i); if (item) { @@ -141,18 +141,22 @@ void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column { Q_D(QGraphicsGridLayout); if (row < 0 || column < 0) { - qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d", - row < 0 ? row : column); - return; + qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d", + row < 0 ? row : column); + return; } if (columnSpan < 1 || rowSpan < 1) { - qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d", - rowSpan < 1 ? rowSpan : columnSpan); - return; + qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + return; } if (!item) { - qWarning("QGraphicsGridLayout::addItem: cannot add null item"); - return; + qWarning("QGraphicsGridLayout::addItem: cannot add null item"); + return; + } + if (item == this) { + qWarning("QGraphicsGridLayout::addItem: cannot insert itself"); + return; } d->addChildLayoutItem(item); @@ -647,5 +651,5 @@ QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) con #endif QT_END_NAMESPACE - -#endif //QT_NO_GRAPHICSVIEW + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index c914402..e22486b 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1228,7 +1228,7 @@ void QGraphicsItemCache::purge() } /*! - Constructs a QGraphicsItem with the given \a parent. + Constructs a QGraphicsItem, passing \a item to QGraphicsItem's constructor. It does not modify \fn QObject::parent(). If \a parent is 0, you can add the item to a scene by calling QGraphicsScene::addItem(). The item will then become a top-level item. @@ -1511,6 +1511,8 @@ const QGraphicsObject *QGraphicsItem::toGraphicsObject() const the parent. You should not \l{QGraphicsScene::addItem()}{add} the item to the scene yourself. + Calling this function on an item that is an ancestor of \a parent have undefined behaviour. + \sa parentItem(), childItems() */ void QGraphicsItem::setParentItem(QGraphicsItem *parent) @@ -2482,12 +2484,14 @@ void QGraphicsItem::setOpacity(qreal opacity) itemChange(ItemOpacityHasChanged, newOpacityVariant); // Update. - if (d_ptr->scene) + if (d_ptr->scene) { + d_ptr->invalidateGraphicsEffectsRecursively(); d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true, /*maybeDirtyClipPath=*/false, /*force=*/false, /*ignoreOpacity=*/true); + } if (d_ptr->isObject) emit static_cast<QGraphicsObject *>(this)->opacityChanged(); @@ -4745,7 +4749,7 @@ bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const { if (!item) return false; - return QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(item, this) + return qt_closestItemFirst(item, this) && qt_QGraphicsItem_isObscured(this, item, boundingRect()); } @@ -4959,6 +4963,22 @@ int QGraphicsItemPrivate::depth() const /*! \internal */ +void QGraphicsItemPrivate::invalidateGraphicsEffectsRecursively() +{ + QGraphicsItemPrivate *itemPrivate = this; + do { + if (itemPrivate->graphicsEffect) { + itemPrivate->notifyInvalidated = 1; + + if (!itemPrivate->updateDueToGraphicsEffect) + static_cast<QGraphicsItemEffectSourcePrivate *>(itemPrivate->graphicsEffect->d_func()->source->d_func())->invalidateCache(); + } + } while ((itemPrivate = itemPrivate->parent ? itemPrivate->parent->d_ptr.data() : 0)); +} + +/*! + \internal +*/ void QGraphicsItemPrivate::invalidateDepthRecursively() { if (itemDepth == -1) @@ -5290,11 +5310,7 @@ void QGraphicsItem::update(const QRectF &rect) return; // Make sure we notify effects about invalidated source. - QGraphicsItem *item = this; - do { - if (item->d_ptr->graphicsEffect) - item->d_ptr->notifyInvalidated = 1; - } while ((item = item->d_ptr->parent)); + d_ptr->invalidateGraphicsEffectsRecursively(); if (CacheMode(d_ptr->cacheMode) != NoCache) { // Invalidate cache. @@ -7312,7 +7328,7 @@ void QGraphicsObject::grabGesture(Qt::GestureType gesture, Qt::GestureContext co /*! \property QGraphicsObject::parent - \brief the parent of the item + \brief the parent of the item. It is independent from \fn QObject::parent. \sa QGraphicsItem::setParentItem(), QGraphicsItem::parentObject() */ @@ -10731,6 +10747,7 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP } pixmapPainter.end(); + return pixmap; } @@ -10750,6 +10767,23 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item) return debug; } +QDebug operator<<(QDebug debug, QGraphicsObject *item) +{ + if (!item) { + debug << "QGraphicsObject(0)"; + return debug; + } + + debug.nospace() << item->metaObject()->className() << '(' << (void*)item; + if (!item->objectName().isEmpty()) + debug << ", name = " << item->objectName(); + debug.nospace() << ", parent = " << ((void*)item->parentItem()) + << ", pos = " << item->pos() + << ", z = " << item->zValue() << ", flags = " + << item->flags() << ')'; + return debug.space(); +} + QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change) { const char *str = "UnknownChange"; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 2665235..f3fe99c 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -555,7 +555,7 @@ public: using QObject::children; #endif - void grabGesture(Qt::GestureType type, Qt::GestureContext context = Qt::WidgetWithChildrenGesture); + void grabGesture(Qt::GestureType type, Qt::GestureContext context = Qt::ItemWithChildrenGesture); Q_SIGNALS: void parentChanged(); @@ -1120,6 +1120,7 @@ template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item) #ifndef QT_NO_DEBUG_STREAM Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem *item); +Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsObject *item); Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change); Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag); Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 6550362..7c3c4f0 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -177,6 +177,7 @@ public: wantsActive(0), holesInSiblingIndex(0), sequentialOrdering(1), + updateDueToGraphicsEffect(0), globalStackingOrder(-1), q_ptr(0) { @@ -221,6 +222,7 @@ public: bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; int depth() const; + void invalidateGraphicsEffectsRecursively(); void invalidateDepthRecursively(); void resolveDepth(); void addChild(QGraphicsItem *child); @@ -502,6 +504,7 @@ public: quint32 wantsActive : 1; quint32 holesInSiblingIndex : 1; quint32 sequentialOrdering : 1; + quint32 updateDueToGraphicsEffect : 1; // Optional stacking order int globalStackingOrder; @@ -589,8 +592,11 @@ public: inline const QWidget *widget() const { return 0; } - inline void update() - { item->update(); } + inline void update() { + item->d_ptr->updateDueToGraphicsEffect = true; + item->update(); + item->d_ptr->updateDueToGraphicsEffect = false; + } inline void effectBoundingRectChanged() { item->prepareGeometryChange(); } @@ -619,10 +625,76 @@ public: QGraphicsItem *item; QGraphicsItemPaintInfo *info; + QTransform lastEffectTransform; }; /*! + Returns true if \a item1 is on top of \a item2. + The items dont need to be siblings. + + \internal +*/ +inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Siblings? Just check their z-values. + const QGraphicsItemPrivate *d1 = item1->d_ptr.data(); + const QGraphicsItemPrivate *d2 = item2->d_ptr.data(); + if (d1->parent == d2->parent) + return qt_closestLeaf(item1, item2); + + // Find common ancestor, and each item's ancestor closest to the common + // ancestor. + int item1Depth = d1->depth(); + int item2Depth = d2->depth(); + const QGraphicsItem *p = item1; + const QGraphicsItem *t1 = item1; + while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { + if (p == item2) { + // item2 is one of item1's ancestors; item1 is on top + return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t1 = p; + --item1Depth; + } + p = item2; + const QGraphicsItem *t2 = item2; + while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { + if (p == item1) { + // item1 is one of item2's ancestors; item1 is not on top + return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t2 = p; + --item2Depth; + } + + // item1Ancestor is now at the same level as item2Ancestor, but not the same. + const QGraphicsItem *p1 = t1; + const QGraphicsItem *p2 = t2; + while (t1 && t1 != t2) { + p1 = t1; + p2 = t2; + t1 = t1->d_ptr->parent; + t2 = t2->d_ptr->parent; + } + + // in case we have a common ancestor, we compare the immediate children in the ancestor's path. + // otherwise we compare the respective items' topLevelItems directly. + return qt_closestLeaf(p1, p2); +} + +/*! + Returns true if \a item2 is on top of \a item1. + The items dont need to be siblings. + + \internal +*/ +inline bool qt_closestItemLast(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + return qt_closestItemFirst(item2, item1); +} + +/*! \internal */ inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) @@ -642,7 +714,7 @@ inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item /*! \internal */ -static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) { return qt_closestLeaf(item2, item1); } /* diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp index 0aa68df..7ff7c9b 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.cpp +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -272,6 +272,10 @@ void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item) qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item"); return; } + if (item == this) { + qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself"); + return; + } d->addChildLayoutItem(item); Q_ASSERT(item); diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index b7a3962..64c51ad 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -57,6 +57,9 @@ #include <QtGui/qpainter.h> #include <QtGui/qstyleoption.h> #include <QtGui/qgraphicsview.h> +#include <QtGui/qlistview.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qtextedit.h> QT_BEGIN_NAMESPACE @@ -86,7 +89,9 @@ QT_BEGIN_NAMESPACE of embedded widgets through creating a child proxy for each popup. This means that when an embedded QComboBox shows its popup list, a new QGraphicsProxyWidget is created automatically, embedding the popup, and - positioning it correctly. + positioning it correctly. This only works if the popup is child of the + embedded widget (for example QToolButton::setMenu() requires the QMenu instance + to be child of the QToolButton). \section1 Embedding a Widget with QGraphicsProxyWidget @@ -184,6 +189,7 @@ QT_BEGIN_NAMESPACE */ extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); +extern bool qt_tab_all_widgets; /*! \internal @@ -369,6 +375,7 @@ QVariant QGraphicsProxyWidgetPrivate::inputMethodQueryHelper(Qt::InputMethodQuer /*! \internal + Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper */ QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const { @@ -382,14 +389,16 @@ QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) child = next ? child->d_func()->focus_next : child->d_func()->focus_prev; if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) { return 0; - } + } } QWidget *oldChild = child; + uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus; do { if (child->isEnabled() && child->isVisibleTo(widget) - && (child->focusPolicy() & Qt::TabFocus)) { + && (child->focusPolicy() & focus_flag == focus_flag) + && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) { return child; } child = next ? child->d_func()->focus_next : child->d_func()->focus_prev; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a624b10..c459d21 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -242,7 +242,6 @@ #include <QtGui/qstyleoption.h> #include <QtGui/qtooltip.h> #include <QtGui/qtransform.h> -#include <QtGui/qgesture.h> #include <QtGui/qinputcontext.h> #include <QtGui/qgraphicseffect.h> #include <private/qapplication_p.h> @@ -251,6 +250,14 @@ #include <private/qt_x11_p.h> #endif #include <private/qgraphicseffect_p.h> +#include <private/qgesturemanager_p.h> + +// #define GESTURE_DEBUG +#ifndef GESTURE_DEBUG +# define DEBUG if (0) qDebug +#else +# define DEBUG qDebug +#endif QT_BEGIN_NAMESPACE @@ -1052,6 +1059,14 @@ bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event) */ bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event) { + if (QGraphicsObject *object = item->toGraphicsObject()) { + QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); + if (qAppPriv->gestureManager) { + if (qAppPriv->gestureManager->filterEvent(object, event)) + return true; + } + } + if (filterEvent(item, event)) return false; if (filterDescendantEvent(item, event)) @@ -3365,6 +3380,10 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::TouchEnd: d->touchEventHandler(static_cast<QTouchEvent *>(event)); break; + case QEvent::Gesture: + case QEvent::GestureOverride: + d->gestureEventHandler(static_cast<QGestureEvent *>(event)); + break; default: return QObject::event(event); } @@ -4569,6 +4588,11 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * else painter->setWorldTransform(*transformPtr); painter->setOpacity(opacity); + + if (sourced->lastEffectTransform != painter->worldTransform()) { + sourced->lastEffectTransform = painter->worldTransform(); + sourced->invalidateCache(); + } item->d_ptr->graphicsEffect->draw(painter, source); painter->setWorldTransform(restoreTransform); sourced->info = 0; @@ -5699,6 +5723,238 @@ void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel) dispatchHoverEvent(&hoverEvent); } +void QGraphicsScenePrivate::getGestureTargets(const QSet<QGesture *> &gestures, + QWidget *viewport, + QMap<Qt::GestureType, QGesture *> *conflictedGestures, + QList<QList<QGraphicsObject *> > *conflictedItems, + QHash<QGesture *, QGraphicsObject *> *normalGestures) +{ + foreach (QGesture *gesture, gestures) { + Qt::GestureType gestureType = gesture->gestureType(); + if (gesture->hasHotSpot()) { + QPoint screenPos = gesture->hotSpot().toPoint(); + QList<QGraphicsItem *> items = itemsAtPosition(screenPos, QPointF(), viewport); + QList<QGraphicsObject *> result; + for (int j = 0; j < items.size(); ++j) { + QGraphicsObject *item = items.at(j)->toGraphicsObject(); + if (!item) + continue; + QGraphicsItemPrivate *d = item->QGraphicsItem::d_func(); + if (d->gestureContext.contains(gestureType)) { + result.append(item); + } + } + DEBUG() << "QGraphicsScenePrivate::getGestureTargets:" + << gesture << result; + if (result.size() == 1) { + normalGestures->insert(gesture, result.first()); + } else if (!result.isEmpty()) { + conflictedGestures->insert(gestureType, gesture); + conflictedItems->append(result); + } + } + } +} + +void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) +{ + QWidget *viewport = event->widget(); + if (!viewport) + return; + QList<QGesture *> allGestures = event->allGestures(); + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "Delivering gestures:" << allGestures; + + typedef QHash<QGraphicsObject *, QList<QGesture *> > GesturesPerItem; + GesturesPerItem gesturesPerItem; + + QSet<QGesture *> startedGestures; + foreach (QGesture *gesture, allGestures) { + QGraphicsObject *target = gestureTargets.value(gesture, 0); + if (!target) { + // when we are not in started mode but don't have a target + // then the only one interested in gesture is the view/scene + if (gesture->state() == Qt::GestureStarted) + startedGestures.insert(gesture); + } else { + gesturesPerItem[target].append(gesture); + } + } + + QMap<Qt::GestureType, QGesture *> conflictedGestures; + QList<QList<QGraphicsObject *> > conflictedItems; + QHash<QGesture *, QGraphicsObject *> normalGestures; + getGestureTargets(startedGestures, viewport, &conflictedGestures, &conflictedItems, + &normalGestures); + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "Conflicting gestures:" << conflictedGestures.values() << conflictedItems; + Q_ASSERT((conflictedGestures.isEmpty() && conflictedItems.isEmpty()) || + (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty())); + + // gestures that were sent as override events, but no one accepted them + QHash<QGesture *, QGraphicsObject *> ignoredConflictedGestures; + + // deliver conflicted gestures as override events first + while (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty()) { + // get the topmost item to deliver the override event + Q_ASSERT(!conflictedItems.isEmpty()); + Q_ASSERT(!conflictedItems.first().isEmpty()); + QGraphicsObject *topmost = conflictedItems.first().first(); + for (int i = 1; i < conflictedItems.size(); ++i) { + QGraphicsObject *item = conflictedItems.at(i).first(); + if (qt_closestItemFirst(item, topmost)) { + topmost = item; + } + } + // get a list of gestures to send to the item + QList<Qt::GestureType> grabbedGestures = + topmost->QGraphicsItem::d_func()->gestureContext.keys(); + QList<QGesture *> gestures; + for (int i = 0; i < grabbedGestures.size(); ++i) { + if (QGesture *g = conflictedGestures.value(grabbedGestures.at(i), 0)) { + gestures.append(g); + if (!ignoredConflictedGestures.contains(g)) + ignoredConflictedGestures.insert(g, topmost); + } + } + + // send gesture override to the topmost item + QGestureEvent ev(gestures); + ev.t = QEvent::GestureOverride; + ev.setWidget(event->widget()); + // mark event and individual gestures as ignored + ev.ignore(); + foreach(QGesture *g, gestures) + ev.setAccepted(g, false); + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "delivering override to" + << topmost << gestures; + sendEvent(topmost, &ev); + // mark all accepted gestures to deliver them as normal gesture events + foreach (QGesture *g, gestures) { + if (ev.isAccepted() || ev.isAccepted(g)) { + conflictedGestures.remove(g->gestureType()); + gestureTargets.remove(g); + // add the gesture to the list of normal delivered gestures + normalGestures.insert(g, topmost); + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "override was accepted:" + << g << topmost; + ignoredConflictedGestures.remove(g); + } + } + // remove the item that we've already delivered from the list + for (int i = 0; i < conflictedItems.size(); ) { + QList<QGraphicsObject *> &items = conflictedItems[i]; + if (items.first() == topmost) { + items.removeFirst(); + if (items.isEmpty()) { + conflictedItems.removeAt(i); + continue; + } + } + ++i; + } + } + + // put back those started gestures that are not in the conflicted state + // and remember their targets + QHash<QGesture *, QGraphicsObject *>::const_iterator it = normalGestures.begin(), + e = normalGestures.end(); + for (; it != e; ++it) { + QGesture *g = it.key(); + QGraphicsObject *receiver = it.value(); + Q_ASSERT(!gestureTargets.contains(g)); + gestureTargets.insert(g, receiver); + gesturesPerItem[receiver].append(g); + } + it = ignoredConflictedGestures.begin(); + e = ignoredConflictedGestures.end(); + for (; it != e; ++it) { + QGesture *g = it.key(); + QGraphicsObject *receiver = it.value(); + Q_ASSERT(!gestureTargets.contains(g)); + gestureTargets.insert(g, receiver); + gesturesPerItem[receiver].append(g); + } + + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "Started gestures:" << normalGestures.keys() + << "All gestures:" << gesturesPerItem.values(); + + // deliver all events + QList<QGesture *> alreadyIgnoredGestures; + QHash<QGraphicsObject *, QSet<QGesture *> > itemIgnoredGestures; + QList<QGraphicsObject *> targetItems = gesturesPerItem.keys(); + qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst); + for (int i = 0; i < targetItems.size(); ++i) { + QGraphicsObject *item = targetItems.at(i); + QList<QGesture *> gestures = gesturesPerItem.value(item); + // remove gestures that were already delivered once and were ignored + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "already ignored gestures for item" + << item << ":" << itemIgnoredGestures.value(item); + + if (itemIgnoredGestures.contains(item)) // don't deliver twice to the same item + continue; + + QGraphicsItemPrivate *gid = item->QGraphicsItem::d_func(); + foreach(QGesture *g, alreadyIgnoredGestures) { + if (gid->gestureContext.contains(g->gestureType())) + gestures += g; + } + if (gestures.isEmpty()) + continue; + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "delivering to" + << item << gestures; + QGestureEvent ev(gestures); + ev.setWidget(event->widget()); + sendEvent(item, &ev); + QSet<QGesture *> ignoredGestures; + foreach (QGesture *g, gestures) { + if (!ev.isAccepted() && !ev.isAccepted(g)) + ignoredGestures.insert(g); + } + if (!ignoredGestures.isEmpty()) { + // get a list of items under the (current) hotspot of each ignored + // gesture and start delivery again from the beginning + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "item has ignored the event, will propagate." + << item << ignoredGestures; + itemIgnoredGestures[item] += ignoredGestures; + QMap<Qt::GestureType, QGesture *> conflictedGestures; + QList<QList<QGraphicsObject *> > itemsForConflictedGestures; + QHash<QGesture *, QGraphicsObject *> normalGestures; + getGestureTargets(ignoredGestures, viewport, + &conflictedGestures, &itemsForConflictedGestures, + &normalGestures); + QSet<QGraphicsObject *> itemsSet = targetItems.toSet(); + for (int k = 0; k < itemsForConflictedGestures.size(); ++k) + itemsSet += itemsForConflictedGestures.at(k).toSet(); + targetItems = itemsSet.toList(); + qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst); + alreadyIgnoredGestures = conflictedGestures.values(); + DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" + << "new targets:" << targetItems; + i = -1; // start delivery again + continue; + } + } + + // forget about targets for gestures that have ended + foreach (QGesture *g, allGestures) { + switch (g->state()) { + case Qt::GestureFinished: + case Qt::GestureCanceled: + gestureTargets.remove(g); + break; + default: + break; + } + } +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 8073695..cd20fd0 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -282,6 +282,13 @@ public: bool allItemsIgnoreTouchEvents; void enableTouchEventsOnViews(); + QHash<QGesture *, QGraphicsObject *> gestureTargets; + void gestureEventHandler(QGestureEvent *event); + void getGestureTargets(const QSet<QGesture *> &gestures, QWidget *viewport, + QMap<Qt::GestureType, QGesture *> *conflictedGestures, + QList<QList<QGraphicsObject *> > *conflictedItems, + QHash<QGesture *, QGraphicsObject *> *normalGestures); + void updateInputMethodSensitivityInViews(); QList<QGraphicsItem *> modalPanels; diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index e21183a..47ae3f1 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -405,70 +405,6 @@ QList<QGraphicsItem *> QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QR } /*! - Returns true if \a item1 is on top of \a item2. - - \internal -*/ -bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - // Siblings? Just check their z-values. - const QGraphicsItemPrivate *d1 = item1->d_ptr.data(); - const QGraphicsItemPrivate *d2 = item2->d_ptr.data(); - if (d1->parent == d2->parent) - return qt_closestLeaf(item1, item2); - - // Find common ancestor, and each item's ancestor closest to the common - // ancestor. - int item1Depth = d1->depth(); - int item2Depth = d2->depth(); - const QGraphicsItem *p = item1; - const QGraphicsItem *t1 = item1; - while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { - if (p == item2) { - // item2 is one of item1's ancestors; item1 is on top - return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t1 = p; - --item1Depth; - } - p = item2; - const QGraphicsItem *t2 = item2; - while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { - if (p == item1) { - // item1 is one of item2's ancestors; item1 is not on top - return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); - } - t2 = p; - --item2Depth; - } - - // item1Ancestor is now at the same level as item2Ancestor, but not the same. - const QGraphicsItem *a1 = t1; - const QGraphicsItem *a2 = t2; - while (a1) { - const QGraphicsItem *p1 = a1; - const QGraphicsItem *p2 = a2; - a1 = a1->parentItem(); - a2 = a2->parentItem(); - if (a1 && a1 == a2) - return qt_closestLeaf(p1, p2); - } - - // No common ancestor? Then just compare the items' toplevels directly. - return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); -} - -/*! - Returns true if \a item2 is on top of \a item1. - - \internal -*/ -bool QGraphicsSceneBspTreeIndexPrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) -{ - return closestItemFirst_withoutCache(item2, item1); -} - -/*! Sort a list of \a itemList in a specific \a order and use the cache if requested. \internal @@ -495,9 +431,9 @@ void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemLi } } else { if (order == Qt::DescendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); + qSort(itemList->begin(), itemList->end(), qt_closestItemFirst); } else if (order == Qt::AscendingOrder) { - qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); + qSort(itemList->begin(), itemList->end(), qt_closestItemLast); } } } diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h index 0a86bb7..c130190 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h @@ -145,8 +145,6 @@ public: QList<QGraphicsItem *> estimateItems(const QRectF &, Qt::SortOrder, bool b = false); static void climbTree(QGraphicsItem *item, int *stackingOrder); - static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); - static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) { diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index ec1a2f5..a0b5493 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -547,7 +547,9 @@ void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const return; matrix->translate(d->origin); - matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + QMatrix4x4 m; + m.rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + *matrix *= m.toTransform(1024.0f); // Project back to 2D. matrix->translate(-d->origin); } diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 32747cc..710c745 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2701,6 +2701,19 @@ bool QGraphicsView::viewportEvent(QEvent *event) return true; } + case QEvent::Gesture: + case QEvent::GestureOverride: + { + if (!isEnabled()) + return false; + + if (d->scene && d->sceneInteractionAllowed) { + QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event); + gestureEvent->setWidget(viewport()); + (void) QApplication::sendEvent(d->scene, gestureEvent); + } + return true; + } default: break; } diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 21ab40c..571ef9d 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -546,11 +546,7 @@ bool QImageData::checkForAlphaPixels() const Each pixel stored in a QImage is represented by an integer. The size of the integer varies depending on the format. QImage supports several image formats described by the \l Format - enum. The monochrome (1-bit), 8-bit and 32-bit images are - available in all versions of Qt. In addition Qt for Embedded Linux - also supports 2-bit, 4-bit, and 16-bit images. For more information - about the Qt Extended specific formats, see the documentation of the \l - Format enum. + enum. Monochrome images are stored using 1-bit indexes into a color table with at most two colors. There are two different types of @@ -707,9 +703,20 @@ bool QImageData::checkForAlphaPixels() const packed with the most significant bit (MSB) first. \value Format_MonoLSB The image is stored using 1-bit per pixel. Bytes are packed with the less significant bit (LSB) first. - \value Format_Indexed8 The image is stored using 8-bit indexes into a colormap. + + \value Format_Indexed8 The image is stored using 8-bit indexes + into a colormap. \warning Drawing into a + QImage with Indexed8 format is not + supported. + \value Format_RGB32 The image is stored using a 32-bit RGB format (0xffRRGGBB). - \value Format_ARGB32 The image is stored using a 32-bit ARGB format (0xAARRGGBB). + + \value Format_ARGB32 The image is stored using a 32-bit ARGB + format (0xAARRGGBB). \warning Do not + render into ARGB32 images using + QPainter. Format_ARGB32_Premultiplied is + significantly faster. + \value Format_ARGB32_Premultiplied The image is stored using a premultiplied 32-bit ARGB format (0xAARRGGBB), i.e. the red, green, and blue channels are multiplied @@ -718,7 +725,9 @@ bool QImageData::checkForAlphaPixels() const undefined.) Certain operations (such as image composition using alpha blending) are faster using premultiplied ARGB32 than with plain ARGB32. + \value Format_RGB16 The image is stored using a 16-bit RGB format (5-6-5). + \value Format_ARGB8565_Premultiplied The image is stored using a premultiplied 24-bit ARGB format (8-5-6-5). \value Format_RGB666 The image is stored using a 24-bit RGB format (6-6-6). diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp index d08d3ef..ac30646 100644 --- a/src/gui/image/qimagepixmapcleanuphooks.cpp +++ b/src/gui/image/qimagepixmapcleanuphooks.cpp @@ -70,19 +70,30 @@ QImagePixmapCleanupHooks *QImagePixmapCleanupHooks::instance() return qt_image_and_pixmap_cleanup_hooks; } -void QImagePixmapCleanupHooks::addPixmapHook(_qt_pixmap_cleanup_hook_pm hook) +void QImagePixmapCleanupHooks::addPixmapModificationHook(_qt_pixmap_cleanup_hook_pm hook) { - pixmapHooks.append(hook); + pixmapModificationHooks.append(hook); } +void QImagePixmapCleanupHooks::addPixmapDestructionHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapDestructionHooks.append(hook); +} + + void QImagePixmapCleanupHooks::addImageHook(_qt_image_cleanup_hook_64 hook) { imageHooks.append(hook); } -void QImagePixmapCleanupHooks::removePixmapHook(_qt_pixmap_cleanup_hook_pm hook) +void QImagePixmapCleanupHooks::removePixmapModificationHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapModificationHooks.removeAll(hook); +} + +void QImagePixmapCleanupHooks::removePixmapDestructionHook(_qt_pixmap_cleanup_hook_pm hook) { - pixmapHooks.removeAll(hook); + pixmapDestructionHooks.removeAll(hook); } void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook) @@ -91,18 +102,29 @@ void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook) } -void QImagePixmapCleanupHooks::executePixmapHooks(QPixmap* pm) +void QImagePixmapCleanupHooks::executePixmapModificationHooks(QPixmap* pm) { - for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapHooks.count(); ++i) - qt_image_and_pixmap_cleanup_hooks->pixmapHooks[i](pm); + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapModificationHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->pixmapModificationHooks[i](pm); if (qt_pixmap_cleanup_hook_64) qt_pixmap_cleanup_hook_64(pm->cacheKey()); } +void QImagePixmapCleanupHooks::executePixmapDestructionHooks(QPixmap* pm) +{ + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapDestructionHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->pixmapDestructionHooks[i](pm); + + if (qt_pixmap_cleanup_hook_64) + qt_pixmap_cleanup_hook_64(pm->cacheKey()); +} void QImagePixmapCleanupHooks::executeImageHooks(qint64 key) { + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->imageHooks.count(); ++i) qt_image_and_pixmap_cleanup_hooks->imageHooks[i](key); diff --git a/src/gui/image/qimagepixmapcleanuphooks_p.h b/src/gui/image/qimagepixmapcleanuphooks_p.h index dd2d0f7..16c8974 100644 --- a/src/gui/image/qimagepixmapcleanuphooks_p.h +++ b/src/gui/image/qimagepixmapcleanuphooks_p.h @@ -70,18 +70,27 @@ public: static QImagePixmapCleanupHooks *instance(); - void addPixmapHook(_qt_pixmap_cleanup_hook_pm); + // Gets called when a pixmap is about to be modified: + void addPixmapModificationHook(_qt_pixmap_cleanup_hook_pm); + + // Gets called when a pixmap is about to be destroyed: + void addPixmapDestructionHook(_qt_pixmap_cleanup_hook_pm); + + // Gets called when an image is about to be modified or destroyed: void addImageHook(_qt_image_cleanup_hook_64); - void removePixmapHook(_qt_pixmap_cleanup_hook_pm); + void removePixmapModificationHook(_qt_pixmap_cleanup_hook_pm); + void removePixmapDestructionHook(_qt_pixmap_cleanup_hook_pm); void removeImageHook(_qt_image_cleanup_hook_64); - static void executePixmapHooks(QPixmap*); + static void executePixmapModificationHooks(QPixmap*); + static void executePixmapDestructionHooks(QPixmap*); static void executeImageHooks(qint64 key); private: QList<_qt_image_cleanup_hook_64> imageHooks; - QList<_qt_pixmap_cleanup_hook_pm> pixmapHooks; + QList<_qt_pixmap_cleanup_hook_pm> pixmapModificationHooks; + QList<_qt_pixmap_cleanup_hook_pm> pixmapDestructionHooks; }; QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index c03a364..a3b7516 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -322,8 +322,9 @@ QPixmap::QPixmap(const char * const xpm[]) QPixmap::~QPixmap() { - if (data->is_cached && data->ref == 1) - QImagePixmapCleanupHooks::executePixmapHooks(this); + Q_ASSERT(data->ref >= 1); // Catch if ref-counting changes again + if (data->is_cached && data->ref == 1) // ref will be decrememnted after destructor returns + QImagePixmapCleanupHooks::executePixmapDestructionHooks(this); } /*! @@ -469,9 +470,11 @@ QPixmap::operator QVariant() const conversion fails. If the pixmap has 1-bit depth, the returned image will also be 1 - bit deep. If the pixmap has 2- to 8-bit depth, the returned image - has 8-bit depth. If the pixmap has greater than 8-bit depth, the - returned image has 32-bit depth. + bit deep. Images with more bits will be returned in a format + closely represents the underlying system. Usually this will be + QImage::Format_ARGB32_Premultiplied for pixmaps with an alpha and + QImage::Format_RGB32 or QImage::Format_RGB16 for pixmaps without + alpha. Note that for the moment, alpha masks on monochrome images are ignored. @@ -959,7 +962,17 @@ void QPixmap::fill(const QColor &color) return; } - detach(); + if (data->ref == 1) { + // detach() will also remove this pixmap from caches, so + // it has to be called even when ref == 1. + detach(); + } else { + // Don't bother to make a copy of the data object, since + // it will be filled with new pixel data anyway. + QPixmapData *d = data->createCompatiblePixmapData(); + d->resize(data->width(), data->height()); + data = d; + } data->fill(color); } @@ -1693,8 +1706,8 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) In addition, on Symbian, the QPixmap class supports conversion to and from CFbsBitmap: the toSymbianCFbsBitmap() function creates - CFbsBitmap equivalent to the QPixmap, based on given mode and returns - a CFbsBitmap object. The fromSymbianCFbsBitmap() function returns a + CFbsBitmap equivalent to the QPixmap, based on given mode and returns + a CFbsBitmap object. The fromSymbianCFbsBitmap() function returns a QPixmap that is equivalent to the given bitmap and given mode. \section1 Pixmap Transformations @@ -1907,7 +1920,7 @@ void QPixmap::detach() } if (data->is_cached && data->ref == 1) - QImagePixmapCleanupHooks::executePixmapHooks(this); + QImagePixmapCleanupHooks::executePixmapModificationHooks(this); #if defined(Q_WS_MAC) QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0; diff --git a/src/gui/image/qpixmap_s60.cpp b/src/gui/image/qpixmap_s60.cpp index 9ae8d72..666d608 100644 --- a/src/gui/image/qpixmap_s60.cpp +++ b/src/gui/image/qpixmap_s60.cpp @@ -496,11 +496,12 @@ void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags mode = EColor16MU; break; case QImage::Format_ARGB32_Premultiplied: -#if !defined(__SERIES60_31__) && !defined(__S60_32__) - mode = EColor16MAP; - break; -#endif - destFormat = QImage::Format_ARGB32; + if (S60->supportsPremultipliedAlpha) { + mode = Q_SYMBIAN_ECOLOR16MAP; + break; + } else { + destFormat = QImage::Format_ARGB32; + } // Fall through intended case QImage::Format_ARGB32: mode = EColor16MA; @@ -690,6 +691,10 @@ void QS60PixmapData::beginDataAccess() bytes = newBytes; TDisplayMode mode = cfbsBitmap->DisplayMode(); QImage::Format format = qt_TDisplayMode2Format(mode); + //on S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type + if (format == QImage::Format_ARGB32) + format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format + TSize size = cfbsBitmap->SizeInPixels(); QVector<QRgb> savedColorTable; @@ -794,8 +799,8 @@ void* QS60PixmapData::toNativeType(NativeType type) bool needsCopy = false; QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); - if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3) { - // Convert argb32_premultiplied to argb32 since Symbian 9.2 and Symbian 9.3 do + if (!(S60->supportsPremultipliedAlpha)) { + // Convert argb32_premultiplied to argb32 since Symbian 9.2 does // not support premultipied format. if (image.format() == QImage::Format_ARGB32_Premultiplied) { diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h index 2d6672d..8ce7c0d 100644 --- a/src/gui/image/qpixmap_x11_p.h +++ b/src/gui/image/qpixmap_x11_p.h @@ -103,6 +103,7 @@ private: friend class QRasterWindowSurface; friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags friend class QEglContext; // Needs gl_surface + friend class QX11GLPixmapData; // Needs gl_surface friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface void release(); diff --git a/src/gui/inputmethod/qinputcontext.cpp b/src/gui/inputmethod/qinputcontext.cpp index 8ee417f..ea6ed35 100644 --- a/src/gui/inputmethod/qinputcontext.cpp +++ b/src/gui/inputmethod/qinputcontext.cpp @@ -467,32 +467,32 @@ bool QInputContext::x11FilterEvent(QWidget * /*keywidget*/, XEvent * /*event*/) } #endif // Q_WS_X11 -#ifdef Q_WS_S60 +#ifdef Q_OS_SYMBIAN /*! \since 4.6 This function may be overridden only if input method is depending - on Symbian and you need raw TWsEvent. Otherwise, this function must not. + on Symbian and you need raw Symbian events. Otherwise, this function must not. - This function is designed to filter raw key events on S60, but + This function is designed to filter raw key events on Symbian, but other input methods may use this to implement some special features. Return true if the \a event has been consumed. Otherwise, the unfiltered \a event will be translated into QEvent and forwarded - to filterEvent(). Filtering at both s60FilterEvent() and + to filterEvent(). Filtering at both symbianFilterEvent() and filterEvent() in single input method is allowed. \a keywidget is a client widget into which a text is inputted. \a - event is inputted TWsEvent. + event is inputted QSymbianEvent. \sa filterEvent() */ -bool QInputContext::s60FilterEvent(QWidget * /*keywidget*/, TWsEvent * /*event*/) +bool QInputContext::symbianFilterEvent(QWidget * /*keywidget*/, const QSymbianEvent * /*event*/) { return false; } -#endif // Q_WS_S60 +#endif // Q_OS_SYMBIAN QT_END_NAMESPACE diff --git a/src/gui/inputmethod/qinputcontext.h b/src/gui/inputmethod/qinputcontext.h index 73b05d8..14096e3 100644 --- a/src/gui/inputmethod/qinputcontext.h +++ b/src/gui/inputmethod/qinputcontext.h @@ -67,10 +67,6 @@ QT_BEGIN_HEADER -#ifdef Q_WS_S60 -class TWsEvent; -#endif - QT_BEGIN_NAMESPACE QT_MODULE(Gui) @@ -79,6 +75,9 @@ class QWidget; class QFont; class QPopupMenu; class QInputContextPrivate; +#ifdef Q_OS_SYMBIAN +class QSymbianEvent; +#endif class Q_GUI_EXPORT QInputContext : public QObject { @@ -108,9 +107,9 @@ public: #if defined(Q_WS_X11) virtual bool x11FilterEvent( QWidget *keywidget, XEvent *event ); #endif // Q_WS_X11 -#if defined(Q_WS_S60) - virtual bool s60FilterEvent( QWidget *keywidget, TWsEvent *event ); -#endif // Q_WS_S60 +#if defined(Q_OS_SYMBIAN) + virtual bool symbianFilterEvent( QWidget *keywidget, const QSymbianEvent *event ); +#endif // Q_OS_SYMBIAN virtual bool filterEvent( const QEvent *event ); void sendEvent(const QInputMethodEvent &event); diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index fc9820f..3bd9a19 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -1419,7 +1419,7 @@ int QHeaderView::minimumSectionSize() const int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this); if (d->orientation == Qt::Horizontal) return qMax(strut.width(), (fontMetrics().maxWidth() + margin)); - return qMax(strut.height(), (fontMetrics().lineSpacing() + margin)); + return qMax(strut.height(), (fontMetrics().height() + margin)); } return d->minimumSectionSize; } diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 9b0b00f..f58f458 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -1105,13 +1105,13 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie ++row; if (row >= rowCount) return QModelIndex(); - return d->model->index(row, 0, d->root); + return d->model->index(row, d->column, d->root); } const QRect initialRect = rectForIndex(current); QRect rect = initialRect; if (rect.isEmpty()) { - return d->model->index(0, 0, d->root); + return d->model->index(0, d->column, d->root); } if (d->gridSize().isValid()) rect.setSize(d->gridSize()); @@ -1900,7 +1900,7 @@ void QListModeViewBase::updateVerticalScrollBar(const QSize &step) if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && ((flow() == QListView::TopToBottom && !isWrapping()) || (flow() == QListView::LeftToRight && isWrapping()))) { - const int steps = (flow() == QListView::TopToBottom ? flowPositions : segmentPositions).count() - 1; + const int steps = (flow() == QListView::TopToBottom ? scrollValueMap : segmentPositions).count() - 1; if (steps > 0) { const int pageSteps = perItemScrollingPageSteps(viewport()->height(), contentsSize.height(), isWrapping()); verticalScrollBar()->setSingleStep(1); @@ -1921,7 +1921,7 @@ void QListModeViewBase::updateHorizontalScrollBar(const QSize &step) if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && ((flow() == QListView::TopToBottom && isWrapping()) || (flow() == QListView::LeftToRight && !isWrapping()))) { - int steps = (flow() == QListView::TopToBottom ? segmentPositions : flowPositions).count() - 1; + int steps = (flow() == QListView::TopToBottom ? segmentPositions : scrollValueMap).count() - 1; if (steps > 0) { const int pageSteps = perItemScrollingPageSteps(viewport()->width(), contentsSize.width(), isWrapping()); horizontalScrollBar()->setSingleStep(1); @@ -1939,7 +1939,11 @@ int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hi bool above, bool below, const QRect &area, const QRect &rect) const { if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { - int value = qBound(0, verticalScrollBar()->value(), flowPositions.count() - 1); + int value; + if (scrollValueMap.isEmpty()) + value = 0; + else + value = qBound(0, scrollValueMap.at(verticalScrollBar()->value()), flowPositions.count() - 1); if (above) hint = QListView::PositionAtTop; else if (below) @@ -1966,8 +1970,8 @@ int QListModeViewBase::horizontalOffset() const return (isRightToLeft() ? maximum - position : position); } } else if (flow() == QListView::LeftToRight && !flowPositions.isEmpty()) { - int position = flowPositions.at(horizontalScrollBar()->value()); - int maximum = flowPositions.at(horizontalScrollBar()->maximum()); + int position = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->value())); + int maximum = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->maximum())); return (isRightToLeft() ? maximum - position : position); } } @@ -1986,9 +1990,9 @@ int QListModeViewBase::verticalOffset() const } } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { int value = verticalScrollBar()->value(); - if (value > flowPositions.count()) + if (value > scrollValueMap.count()) return 0; - return flowPositions.at(value) - spacing(); + return flowPositions.at(scrollValueMap.at(value)) - spacing(); } } return QCommonListViewBase::verticalOffset(); @@ -2000,7 +2004,11 @@ int QListModeViewBase::horizontalScrollToValue(int index, QListView::ScrollHint if (horizontalScrollMode() != QAbstractItemView::ScrollPerItem) return QCommonListViewBase::horizontalScrollToValue(index, hint, leftOf, rightOf, area, rect); - int value = qBound(0, horizontalScrollBar()->value(), flowPositions.count() - 1); + int value; + if (scrollValueMap.isEmpty()) + value = 0; + else + value = qBound(0, scrollValueMap.at(horizontalScrollBar()->value()), flowPositions.count() - 1); if (leftOf) hint = QListView::PositionAtTop; else if (rightOf) @@ -2043,14 +2051,14 @@ void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) if (vertical && flow() == QListView::TopToBottom && dy != 0) { int currentValue = qBound(0, verticalValue, max); int previousValue = qBound(0, currentValue + dy, max); - int currentCoordinate = flowPositions.at(currentValue); - int previousCoordinate = flowPositions.at(previousValue); + int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue)); + int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue)); dy = previousCoordinate - currentCoordinate; } else if (horizontal && flow() == QListView::LeftToRight && dx != 0) { int currentValue = qBound(0, horizontalValue, max); int previousValue = qBound(0, currentValue + dx, max); - int currentCoordinate = flowPositions.at(currentValue); - int previousCoordinate = flowPositions.at(previousValue); + int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue)); + int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue)); dx = previousCoordinate - currentCoordinate; } } @@ -2113,6 +2121,7 @@ QPoint QListModeViewBase::initStaticLayout(const QListViewLayoutInfo &info) segmentPositions.clear(); segmentStartRows.clear(); segmentExtents.clear(); + scrollValueMap.clear(); x = info.bounds.left() + info.spacing; y = info.bounds.top() + info.spacing; segmentPositions.append(info.flow == QListView::LeftToRight ? y : x); @@ -2204,6 +2213,7 @@ void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) deltaSegPosition = 0; } // save the flow position of this item + scrollValueMap.append(flowPositions.count()); flowPositions.append(flowPosition); // prepare for the next item deltaSegPosition = qMax(deltaSegHint, deltaSegPosition); @@ -2229,6 +2239,7 @@ void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) // if it is the last batch, save the end of the segments if (info.last == info.max) { segmentExtents.append(flowPosition); + scrollValueMap.append(flowPositions.count()); flowPositions.append(flowPosition); segmentPositions.append(info.wrap ? segPosition + deltaSegPosition : INT_MAX); } @@ -2306,7 +2317,14 @@ QRect QListModeViewBase::mapToViewport(const QRect &rect) const int QListModeViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const { - const QVector<int> positions = (wrap ? segmentPositions : flowPositions); + QVector<int> positions; + if (wrap) + positions = segmentPositions; + else if (!flowPositions.isEmpty()) { + positions.reserve(scrollValueMap.size()); + foreach (int itemShown, scrollValueMap) + positions.append(flowPositions.at(itemShown)); + } if (positions.isEmpty() || bounds <= length) return positions.count(); if (uniformItemSizes()) { @@ -2490,7 +2508,7 @@ bool QIconModeViewBase::filterDropEvent(QDropEvent *e) for (int i = 0; i < indexes.count(); ++i) { QModelIndex index = indexes.at(i); QRect rect = dd->rectForIndex(index); - viewport()->update(mapToViewport(rect)); + viewport()->update(dd->mapToViewport(rect, false)); QPoint dest = rect.topLeft() + delta; if (qq->isRightToLeft()) dest.setX(dd->flipX(dest.x()) - rect.width()); diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index b6785da..de4c7f3 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -205,6 +205,7 @@ public: QVector<int> segmentPositions; QVector<int> segmentStartRows; QVector<int> segmentExtents; + QVector<int> scrollValueMap; // used when laying out in batches int batchSavedPosition; diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index a610b73..2a937f1 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -1599,7 +1599,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi break; visualColumn = right + 1; if (visualRow == 0) { - wrapped == true; + wrapped = true; visualRow = bottom; } else { --visualRow; diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index 210534e..f37d8c7 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -2908,6 +2908,8 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) layout(item); q->setState(oldState); + if (model->canFetchMore(index)) + model->fetchMore(index); if (emitSignal) { emit q->expanded(index); #ifndef QT_NO_ANIMATION @@ -2915,8 +2917,6 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) beginAnimatedOperation(); #endif //QT_NO_ANIMATION } - if (model->canFetchMore(index)) - model->fetchMore(index); } void QTreeViewPrivate::collapse(int item, bool emitSignal) @@ -3005,10 +3005,12 @@ void QTreeViewPrivate::beginAnimatedOperation() animatedOperation.setEndValue(animatedOperation.top() + h); } - animatedOperation.after = renderTreeToPixmapForAnimation(rect); + if (!rect.isEmpty()) { + animatedOperation.after = renderTreeToPixmapForAnimation(rect); - q->setState(QAbstractItemView::AnimatingState); - animatedOperation.start(); //let's start the animation + q->setState(QAbstractItemView::AnimatingState); + animatedOperation.start(); //let's start the animation + } } void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 53c2611..8859358 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -84,6 +84,7 @@ SOURCES += \ kernel/qgesturerecognizer.cpp \ kernel/qgesturemanager.cpp \ kernel/qsoftkeymanager.cpp \ + kernel/qdesktopwidget.cpp \ kernel/qguiplatformplugin.cpp win32 { diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 6f6d706..7c38d4b 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -68,9 +68,6 @@ #include "private/qstylesheetstyle_p.h" #include "private/qstyle_p.h" #include "qmessagebox.h" -#include "qlineedit.h" -#include "qlistview.h" -#include "qtextedit.h" #include <QtGui/qgraphicsproxywidget.h> #include "qinputcontext.h" @@ -2498,9 +2495,12 @@ void QApplication::setActiveWindow(QWidget* act) /*!internal * Helper function that returns the new focus widget, but does not set the focus reason. * Returns 0 if a new focus widget could not be found. + * Shared with QGraphicsProxyWidgetPrivate::findFocusChild() */ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next) { + uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus; + QWidget *f = toplevel->focusWidget(); if (!f) f = toplevel; @@ -2508,22 +2508,11 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool QWidget *w = f; QWidget *test = f->d_func()->focus_next; while (test && test != f) { - if ((test->focusPolicy() & Qt::TabFocus) + if ((test->focusPolicy() & focus_flag) == focus_flag && !(test->d_func()->extra && test->d_func()->extra->focus_proxy) && test->isVisibleTo(toplevel) && test->isEnabled() && !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test)) - && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test)) - && (qt_tab_all_widgets -#ifndef QT_NO_LINEEDIT - || qobject_cast<QLineEdit*>(test) -#endif -#ifndef QT_NO_TEXTEDIT - || qobject_cast<QTextEdit*>(test) -#endif -#ifndef QT_NO_ITEMVIEWS - || qobject_cast<QListView*>(test) -#endif - )) { + && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))) { w = test; if (next) break; @@ -2662,7 +2651,10 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { if (!isAlien(w)) break; if (w->testAttribute(Qt::WA_SetCursor)) { - parentOfLeavingCursor = w->parentWidget(); + QWidget *parent = w->parentWidget(); + while (parent && parent->d_func()->data.in_destructor) + parent = parent->parentWidget(); + parentOfLeavingCursor = parent; //continue looping, we need to find the downest alien widget with a cursor. // (downest on the screen) } @@ -3647,8 +3639,13 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // walk through parents and check for gestures if (d->gestureManager) { - if (d->gestureManager->filterEvent(receiver, e)) - return true; + if (receiver->isWidgetType()) { + if (d->gestureManager->filterEvent(static_cast<QWidget *>(receiver), e)) + return true; + } else if (QGesture *gesture = qobject_cast<QGesture *>(receiver)) { + if (d->gestureManager->filterEvent(gesture, e)) + return true; + } } @@ -4173,40 +4170,41 @@ bool QApplication::notify(QObject *receiver, QEvent *e) if (wd->gestureContext.contains(type)) { allGestures.removeAt(i); gestures.append(g); - gestureEvent->setAccepted(g, false); } else { ++i; } } - if (!gestures.isEmpty()) { + if (!gestures.isEmpty()) { // we have gestures for this w QGestureEvent ge(gestures); ge.t = gestureEvent->t; ge.spont = gestureEvent->spont; ge.m_accept = wasAccepted; + ge.d_func()->accepted = gestureEvent->d_func()->accepted; res = d->notify_helper(w, &ge); gestureEvent->spont = false; eventAccepted = ge.isAccepted(); - if (res && eventAccepted) - break; - if (!eventAccepted) { - // ### two ways to ignore the event/gesture - - // if the whole event wasn't accepted, put back those - // gestures that were not accepted. - for (int i = 0; i < gestures.size(); ++i) { - QGesture *g = gestures.at(i); - if (!ge.isAccepted(g)) - allGestures.append(g); + for (int i = 0; i < gestures.size(); ++i) { + QGesture *g = gestures.at(i); + if ((res && eventAccepted) || (!eventAccepted && ge.isAccepted(g))) { + // if the gesture was accepted, mark the target widget for it + gestureEvent->d_func()->targetWidgets[g->gestureType()] = w; + gestureEvent->setAccepted(g, true); + } else if (!eventAccepted && !ge.isAccepted(g)) { + // if the gesture was explicitly ignored by the application, + // put it back so a parent can get it + allGestures.append(g); } } } - if (allGestures.isEmpty()) + if (allGestures.isEmpty()) // everything delivered break; if (w->isWindow()) break; w = w->parentWidget(); } - gestureEvent->m_accept = eventAccepted; + foreach (QGesture *g, allGestures) + gestureEvent->setAccepted(g, false); + gestureEvent->m_accept = false; // to make sure we check individual gestures } else { res = d->notify_helper(receiver, e); } diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index 12b398d..5a8e325 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -61,9 +61,6 @@ QT_BEGIN_HEADER -#if defined(Q_OS_SYMBIAN) -class TWsEvent; -#endif #if defined(Q_WS_S60) class CApaApplication; #endif @@ -83,6 +80,9 @@ class QLocale; #if defined(Q_WS_QWS) class QDecoration; #endif +#if defined(Q_OS_SYMBIAN) +class QSymbianEvent; +#endif class QApplication; class QApplicationPrivate; @@ -241,10 +241,8 @@ public: int x11ProcessEvent(XEvent*); #endif #if defined(Q_OS_SYMBIAN) - int s60ProcessEvent(TWsEvent *event); - virtual bool s60EventFilter(TWsEvent *aEvent); - void symbianHandleCommand(int command); - void symbianResourceChange(int type); + int symbianProcessEvent(const QSymbianEvent *event); + virtual bool symbianEventFilter(const QSymbianEvent *event); #endif #if defined(Q_WS_QWS) virtual bool qwsEventFilter(QWSEvent *); diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index f9c8aa3..771cddc 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -203,6 +203,8 @@ static EventHandlerRef tablet_proximity_handler = 0; static EventHandlerUPP tablet_proximity_UPP = 0; bool QApplicationPrivate::native_modal_dialog_active; +Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + /***************************************************************************** External functions *****************************************************************************/ @@ -222,6 +224,12 @@ extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplicatio void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); void onApplicationChangedActivation( bool activated ); +static void qt_mac_read_fontsmoothing_settings() +{ + NSInteger appleFontSmoothing = [[NSUserDefaults standardUserDefaults] integerForKey:@"AppleFontSmoothing"]; + qt_applefontsmoothing_enabled = (appleFontSmoothing > 0); +} + Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, long script_len, AEDesc *ret) { OSStatus err; AEDesc scriptTextDesc; @@ -1203,6 +1211,9 @@ void qt_init(QApplicationPrivate *priv, int) } if (QApplication::desktopSettingsAware()) QApplicationPrivate::qt_mac_apply_settings(); + + qt_mac_read_fontsmoothing_settings(); + // Cocoa application delegate #ifdef QT_MAC_USE_COCOA NSApplication *cocoaApp = [NSApplication sharedApplication]; diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 2d3d18c..65f61e9 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -503,6 +503,11 @@ public: static void setNavigationMode(Qt::NavigationMode mode); static TUint resolveS60ScanCode(TInt scanCode, TUint keysym); QSet<WId> nativeWindows; + + int symbianProcessWsEvent(const TWsEvent *event); + int symbianHandleCommand(int command); + int symbianResourceChange(int type); + #endif #if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) void sendSyntheticEnterLeave(QWidget *widget); diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index cb9dda4..689429e 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -42,6 +42,7 @@ #include "qapplication_p.h" #include "qsessionmanager.h" #include "qevent.h" +#include "qsymbianevent.h" #include "qeventdispatcher_s60_p.h" #include "qwidget.h" #include "qdesktopwidget.h" @@ -1056,6 +1057,13 @@ void qt_init(QApplicationPrivate * /* priv */, int) TDisplayMode mode = S60->screenDevice()->DisplayMode(); S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode); + //NB: RWsSession::GetColorModeList tells you what window modes are supported, + //not what bitmap formats. + if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2) + S60->supportsPremultipliedAlpha = 0; + else + S60->supportsPremultipliedAlpha = 1; + RProcess me; TSecureId securId = me.SecureId(); S60->uid = securId.operator TUid(); @@ -1084,6 +1092,13 @@ void qt_init(QApplicationPrivate * /* priv */, int) err = HAL::Get(HALData::EPen, touch); if (err != KErrNone || touchIsUnsupportedOnSystem) touch = 0; +#ifdef __WINS__ + if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) { + //for symbian SDK emulator, force values to match typical devices. + mouse = 0; + touch = touchIsUnsupportedOnSystem ? 0 : 1; + } +#endif if (mouse || machineUID == KMachineUidSamsungI8510) { S60->hasTouchscreen = false; S60->virtualMouseRequired = false; @@ -1419,43 +1434,47 @@ void QApplication::beep() \warning This function is only available on Symbian. \since 4.6 - This function processes an individual Symbian window server + This function processes an individual Symbian event \a event. It returns 1 if the event was handled, 0 if the \a event was not handled, and -1 if the event was - not handled because the event handle (\c{TWsEvent::Handle()}) - is not known to Qt. + not handled because the event is not known to Qt. */ -int QApplication::s60ProcessEvent(TWsEvent *event) + +int QApplication::symbianProcessEvent(const QSymbianEvent *event) { - bool handled = s60EventFilter(event); - if (handled) + Q_D(QApplication); + + QScopedLoopLevelCounter counter(d->threadData); + + QWidget *w = qApp ? qApp->focusWidget() : 0; + if (w) { + QInputContext *ic = w->inputContext(); + if (ic && ic->symbianFilterEvent(w, event)) + return 1; + } + + if (symbianEventFilter(event)) return 1; + switch (event->type()) { + case QSymbianEvent::WindowServerEvent: + return d->symbianProcessWsEvent(event->windowServerEvent()); + case QSymbianEvent::CommandEvent: + return d->symbianHandleCommand(event->command()); + case QSymbianEvent::ResourceChangeEvent: + return d->symbianResourceChange(event->resourceChangeType()); + default: + return -1; + } +} + +int QApplicationPrivate::symbianProcessWsEvent(const TWsEvent *event) +{ // Qt event handling. Handle some events regardless of if the handle is in our // widget map or not. CCoeControl* control = reinterpret_cast<CCoeControl*>(event->Handle()); const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control); switch (event->Type()) { -#if !defined(QT_NO_IM) && defined(Q_WS_S60) - case EEventKey: - case EEventKeyUp: - case EEventKeyDown: - { - // The control doesn't seem to be any of our widgets, so rely on the focused - // widget instead. If the user needs the control, it can be found inside the - // event structure. - QWidget *w = qApp ? qApp->focusWidget() : 0; - if (w) { - QInputContext *ic = w->inputContext(); - if (ic && ic->s60FilterEvent(w, event)) { - return 1; - } else { - return 0; - } - } - break; - } -#endif case EEventPointerEnter: if (controlInMap) return 1; // Qt::Enter will be generated in HandlePointerL @@ -1544,15 +1563,15 @@ int QApplication::s60ProcessEvent(TWsEvent *event) If you create an application that inherits QApplication and reimplement this function, you get direct access to events that the are received - from the Symbian window server. The events are passed in the TWsEvent - \a aEvent parameter. + from Symbian. The events are passed in the \a event parameter. Return true if you want to stop the event from being processed. Return - false for normal event dispatching. The default implementation - false, and does nothing with \a aEvent. + false for normal event dispatching. The default implementation returns + false, and does nothing with \a event. */ -bool QApplication::s60EventFilter(TWsEvent * /* aEvent */) +bool QApplication::symbianEventFilter(const QSymbianEvent *event) { + Q_UNUSED(event); return false; } @@ -1567,32 +1586,39 @@ bool QApplication::s60EventFilter(TWsEvent * /* aEvent */) \sa s60EventFilter(), s60ProcessEvent() */ -void QApplication::symbianHandleCommand(int command) +int QApplicationPrivate::symbianHandleCommand(int command) { - QScopedLoopLevelCounter counter(d_func()->threadData); + Q_Q(QApplication); + int ret = 0; + switch (command) { #ifdef Q_WS_S60 case EAknSoftkeyExit: { QCloseEvent ev; - QApplication::sendSpontaneousEvent(this, &ev); - if (ev.isAccepted()) - quit(); + QApplication::sendSpontaneousEvent(q, &ev); + if (ev.isAccepted()) { + q->quit(); + ret = 1; + } break; } #endif case EEikCmdExit: - quit(); + q->quit(); + ret = 1; break; default: bool handled = QSoftKeyManager::handleCommand(command); + if (handled) + ret = 1; #ifdef Q_WS_S60 - if (!handled) - QMenuBarPrivate::symbianCommands(command); -#else - Q_UNUSED(handled); + else + ret = QMenuBarPrivate::symbianCommands(command); #endif break; } + + return ret; } /*! @@ -1604,8 +1630,10 @@ void QApplication::symbianHandleCommand(int command) Currently, KEikDynamicLayoutVariantSwitch and KAknsMessageSkinChange are handled. */ -void QApplication::symbianResourceChange(int type) +int QApplicationPrivate::symbianResourceChange(int type) { + int ret = 0; + switch (type) { #ifdef Q_WS_S60 case KEikDynamicLayoutVariantSwitch: @@ -1624,22 +1652,28 @@ void QApplication::symbianResourceChange(int type) #endif s60Style = qobject_cast<QS60Style*>(QApplication::style()); - if (s60Style) + if (s60Style) { s60Style->d_func()->handleDynamicLayoutVariantSwitch(); + ret = 1; + } #endif } break; #ifndef QT_NO_STYLE_S60 case KAknsMessageSkinChange: - if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) + if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) { s60Style->d_func()->handleSkinChange(); + ret = 1; + } break; #endif #endif // Q_WS_S60 default: break; } + + return ret; } #ifndef QT_NO_WHEELEVENT diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 5bb25fa..e84985e 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -615,6 +615,8 @@ static void qt_set_windows_font_resources() if (qt_wince_is_mobile()) { smallerFont.setPointSize(systemFont.pointSize()-1); QApplication::setFont(smallerFont, "QTabBar"); + smallerFont.setBold(true); + QApplication::setFont(smallerFont, "QAbstractButton"); } #endif// Q_WS_WINCE } diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 4c2a14a..d49c150 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -51,6 +51,7 @@ #include <private/qmacinputcontext_p.h> #include <private/qmultitouch_mac_p.h> #include <private/qevent_p.h> +#include <private/qbackingstore_p.h> #include <qscrollarea.h> #include <qhash.h> @@ -503,6 +504,12 @@ extern "C" { - (void)drawRect:(NSRect)aRect { + if (QApplicationPrivate::graphicsSystem() != 0) { + if (QWidgetBackingStore *bs = qwidgetprivate->maybeBackingStore()) + bs->markDirty(qwidget->rect(), qwidget); + qwidgetprivate->syncBackingStore(qwidget->rect()); + return; + } CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; qwidgetprivate->hd = cg; CGContextSaveGState(cg); diff --git a/src/gui/kernel/qdesktopwidget.cpp b/src/gui/kernel/qdesktopwidget.cpp new file mode 100644 index 0000000..b1e1008 --- /dev/null +++ b/src/gui/kernel/qdesktopwidget.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 "qglobal.h" + +QT_BEGIN_NAMESPACE + +#include "qdesktopwidget.h" +#include "qwidget_p.h" + +const QRect QDesktopWidget::screenGeometry(const QWidget *widget) const +{ + QRect rect = QWidgetPrivate::screenGeometry(widget); + if (rect.isNull()) + return screenGeometry(screenNumber(widget)); + else return rect; +} + +const QRect QDesktopWidget::availableGeometry(const QWidget *widget) const +{ + QRect rect = QWidgetPrivate::screenGeometry(widget); + if (rect.isNull()) + return availableGeometry(screenNumber(widget)); + else + return rect; +} + +QT_END_NAMESPACE + diff --git a/src/gui/kernel/qdesktopwidget.h b/src/gui/kernel/qdesktopwidget.h index 85f479e..6e3447c 100644 --- a/src/gui/kernel/qdesktopwidget.h +++ b/src/gui/kernel/qdesktopwidget.h @@ -75,14 +75,12 @@ public: QWidget *screen(int screen = -1); const QRect screenGeometry(int screen = -1) const; - const QRect screenGeometry(const QWidget *widget) const - { return screenGeometry(screenNumber(widget)); } + const QRect screenGeometry(const QWidget *widget) const; const QRect screenGeometry(const QPoint &point) const { return screenGeometry(screenNumber(point)); } const QRect availableGeometry(int screen = -1) const; - const QRect availableGeometry(const QWidget *widget) const - { return availableGeometry(screenNumber(widget)); } + const QRect availableGeometry(const QWidget *widget) const; const QRect availableGeometry(const QPoint &point) const { return availableGeometry(screenNumber(point)); } diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 2ff6d65..065bd09 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -45,6 +45,7 @@ #include "private/qapplication_p.h" #include "private/qkeysequence_p.h" #include "qwidget.h" +#include "qgraphicsview.h" #include "qdebug.h" #include "qmime.h" #include "qdnd_p.h" @@ -4223,8 +4224,17 @@ QTouchEvent::TouchPoint &QTouchEvent::TouchPoint::operator=(const QTouchEvent::T Creates new QGestureEvent containing a list of \a gestures. */ QGestureEvent::QGestureEvent(const QList<QGesture *> &gestures) - : QEvent(QEvent::Gesture), gestures_(gestures) + : QEvent(QEvent::Gesture) { + d = reinterpret_cast<QEventPrivate *>(new QGestureEventPrivate(gestures)); +} + +/*! + Destroys QGestureEvent. +*/ +QGestureEvent::~QGestureEvent() +{ + delete reinterpret_cast<QGestureEventPrivate *>(d); } /*! @@ -4232,7 +4242,7 @@ QGestureEvent::QGestureEvent(const QList<QGesture *> &gestures) */ QList<QGesture *> QGestureEvent::allGestures() const { - return gestures_; + return d_func()->gestures; } /*! @@ -4240,9 +4250,10 @@ QList<QGesture *> QGestureEvent::allGestures() const */ QGesture *QGestureEvent::gesture(Qt::GestureType type) const { - for(int i = 0; i < gestures_.size(); ++i) - if (gestures_.at(i)->gestureType() == type) - return gestures_.at(i); + const QGestureEventPrivate *d = d_func(); + for(int i = 0; i < d->gestures.size(); ++i) + if (d->gestures.at(i)->gestureType() == type) + return d->gestures.at(i); return 0; } @@ -4251,7 +4262,7 @@ QGesture *QGestureEvent::gesture(Qt::GestureType type) const */ QList<QGesture *> QGestureEvent::activeGestures() const { - return gestures_; + return d_func()->gestures; } /*! @@ -4259,7 +4270,7 @@ QList<QGesture *> QGestureEvent::activeGestures() const */ QList<QGesture *> QGestureEvent::canceledGestures() const { - return gestures_; + return d_func()->gestures; } /*! @@ -4279,7 +4290,7 @@ void QGestureEvent::setAccepted(QGesture *gesture, bool value) { setAccepted(false); if (gesture) - gesture->d_func()->accept = value; + d_func()->accepted[gesture->gestureType()] = value; } /*! @@ -4315,7 +4326,56 @@ void QGestureEvent::ignore(QGesture *gesture) */ bool QGestureEvent::isAccepted(QGesture *gesture) const { - return gesture ? gesture->d_func()->accept : false; + return gesture ? d_func()->accepted.value(gesture->gestureType(), true) : false; +} + +/*! + Sets the widget for this event. +*/ +void QGestureEvent::setWidget(QWidget *widget) +{ + d_func()->widget = widget; +} + +/*! + Returns the widget on which the event occurred. +*/ +QWidget *QGestureEvent::widget() const +{ + return d_func()->widget; +} + +/*! + Returns the scene-local coordinates if the \a gesturePoint is inside a graphics view. + + \sa QPointF::isNull(). +*/ +QPointF QGestureEvent::mapToScene(const QPointF &gesturePoint) const +{ + QWidget *w = widget(); + if (w) // we get the viewport as widget, not the graphics view + w = w->parentWidget(); + QGraphicsView *view = qobject_cast<QGraphicsView*>(w); + if (view) { + return view->mapToScene(view->mapFromGlobal(gesturePoint.toPoint())); + } + return QPointF(); +} + +/*! + \internal +*/ +QGestureEventPrivate *QGestureEvent::d_func() +{ + return reinterpret_cast<QGestureEventPrivate *>(d); +} + +/*! + \internal +*/ +const QGestureEventPrivate *QGestureEvent::d_func() const +{ + return reinterpret_cast<const QGestureEventPrivate *>(d); } #ifdef Q_NO_USING_KEYWORD diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 3516222..b7370fd 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -820,10 +820,12 @@ protected: }; class QGesture; +class QGestureEventPrivate; class Q_GUI_EXPORT QGestureEvent : public QEvent { public: QGestureEvent(const QList<QGesture *> &gestures); + ~QGestureEvent(); QList<QGesture *> allGestures() const; QGesture *gesture(Qt::GestureType type) const; @@ -849,8 +851,17 @@ public: void ignore(QGesture *); bool isAccepted(QGesture *) const; + void setWidget(QWidget *widget); + QWidget *widget() const; + + QPointF mapToScene(const QPointF &gesturePoint) const; + private: - QList<QGesture *> gestures_; + QGestureEventPrivate *d_func(); + const QGestureEventPrivate *d_func() const; + + friend class QApplication; + friend class QGestureManager; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index c7a4975..6e6ab01 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -150,6 +150,20 @@ public: #endif }; +class QGestureEventPrivate +{ +public: + inline QGestureEventPrivate(const QList<QGesture *> &list) + : gestures(list), widget(0) + { + } + + QList<QGesture *> gestures; + QWidget *widget; + QMap<Qt::GestureType, bool> accepted; + QMap<Qt::GestureType, QWidget *> targetWidgets; +}; + QT_END_NAMESPACE #endif // QEVENT_P_H diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index fc8df49..ecdd661 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -129,6 +129,10 @@ QGesture::~QGesture() \brief The point that is used to find the receiver for the gesture event. + The hot-spot is a point in the global coordinate system, use + QWidget::mapFromGlobal() or QGestureEvent::mapToScene() to get a + local hot-spot. + If the hot-spot is not set, the targetObject is used as the receiver of the gesture event. */ @@ -154,16 +158,6 @@ Qt::GestureState QGesture::state() const return d_func()->state; } -QObject *QGesture::targetObject() const -{ - return d_func()->targetObject; -} - -void QGesture::setTargetObject(QObject *value) -{ - d_func()->targetObject = value; -} - QPointF QGesture::hotSpot() const { return d_func()->hotSpot; @@ -241,17 +235,17 @@ QPanGesture::QPanGesture(QObject *parent) d_func()->gestureType = Qt::PanGesture; } -QSizeF QPanGesture::totalOffset() const +QPointF QPanGesture::totalOffset() const { return d_func()->totalOffset; } -QSizeF QPanGesture::lastOffset() const +QPointF QPanGesture::lastOffset() const { return d_func()->lastOffset; } -QSizeF QPanGesture::offset() const +QPointF QPanGesture::offset() const { return d_func()->offset; } @@ -262,17 +256,17 @@ qreal QPanGesture::acceleration() const } -void QPanGesture::setTotalOffset(const QSizeF &value) +void QPanGesture::setTotalOffset(const QPointF &value) { d_func()->totalOffset = value; } -void QPanGesture::setLastOffset(const QSizeF &value) +void QPanGesture::setLastOffset(const QPointF &value) { d_func()->lastOffset = value; } -void QPanGesture::setOffset(const QSizeF &value) +void QPanGesture::setOffset(const QPointF &value) { d_func()->offset = value; } diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 02eb526..6469959 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -67,7 +67,6 @@ class Q_GUI_EXPORT QGesture : public QObject Q_PROPERTY(Qt::GestureType gestureType READ gestureType) Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot RESET unsetHotSpot) Q_PROPERTY(bool hasHotSpot READ hasHotSpot) - Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject) public: explicit QGesture(QObject *parent = 0); @@ -77,9 +76,6 @@ public: Qt::GestureState state() const; - QObject *targetObject() const; - void setTargetObject(QObject *value); - QPointF hotSpot() const; void setHotSpot(const QPointF &value); bool hasHotSpot() const; @@ -100,22 +96,22 @@ class Q_GUI_EXPORT QPanGesture : public QGesture Q_OBJECT Q_DECLARE_PRIVATE(QPanGesture) - Q_PROPERTY(QSizeF totalOffset READ totalOffset WRITE setTotalOffset) - Q_PROPERTY(QSizeF lastOffset READ lastOffset WRITE setLastOffset) - Q_PROPERTY(QSizeF offset READ offset WRITE setOffset) + Q_PROPERTY(QPointF totalOffset READ totalOffset WRITE setTotalOffset) + Q_PROPERTY(QPointF lastOffset READ lastOffset WRITE setLastOffset) + Q_PROPERTY(QPointF offset READ offset WRITE setOffset) Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration) public: QPanGesture(QObject *parent = 0); - QSizeF totalOffset() const; - QSizeF lastOffset() const; - QSizeF offset() const; + QPointF totalOffset() const; + QPointF lastOffset() const; + QPointF offset() const; qreal acceleration() const; - void setTotalOffset(const QSizeF &value); - void setLastOffset(const QSizeF &value); - void setOffset(const QSizeF &value); + void setTotalOffset(const QPointF &value); + void setLastOffset(const QPointF &value); + void setOffset(const QPointF &value); void setAcceleration(qreal value); friend class QPanGestureRecognizer; diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 7f69a4e..975c0c9 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -68,7 +68,7 @@ class QGesturePrivate : public QObjectPrivate public: QGesturePrivate() : gestureType(Qt::CustomGesture), state(Qt::NoGesture), isHotSpotSet(false), - targetObject(0), accept(true) + targetObject(0) { } @@ -77,7 +77,6 @@ public: QPointF hotSpot; bool isHotSpotSet; QObject *targetObject; - bool accept; }; class QPanGesturePrivate : public QGesturePrivate @@ -90,9 +89,9 @@ public: { } - QSizeF totalOffset; - QSizeF lastOffset; - QSizeF offset; + QPointF totalOffset; + QPointF lastOffset; + QPointF offset; QPoint lastPosition; qreal acceleration; }; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 0f0aef2..ed8e744 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -44,6 +44,7 @@ #include "private/qwidget_p.h" #include "private/qgesture_p.h" #include "private/qgraphicsitem_p.h" +#include "private/qevent_p.h" #include "qgesture.h" #include "qevent.h" #include "qgraphicsitem.h" @@ -88,7 +89,8 @@ Qt::GestureType QGestureManager::registerGestureRecognizer(QGestureRecognizer *r { QGesture *dummy = recognizer->createGesture(0); if (!dummy) { - qWarning("QGestureManager::registerGestureRecognizer: the recognizer doesn't provide gesture object"); + qWarning("QGestureManager::registerGestureRecognizer: " + "the recognizer fails to create a gesture object, skipping registration."); return Qt::GestureType(0); } Qt::GestureType type = dummy->gestureType(); @@ -107,7 +109,7 @@ void QGestureManager::unregisterGestureRecognizer(Qt::GestureType) } -QGesture* QGestureManager::getState(QObject *object, Qt::GestureType type) +QGesture *QGestureManager::getState(QObject *object, Qt::GestureType type) { // if the widget is being deleted we should be carefull and not to // create a new state, as it will create QWeakPointer which doesnt work @@ -115,28 +117,39 @@ QGesture* QGestureManager::getState(QObject *object, Qt::GestureType type) if (object->isWidgetType()) { if (static_cast<QWidget *>(object)->d_func()->data.in_destructor) return 0; + } else if (QGesture *g = qobject_cast<QGesture *>(object)) { + return g; + } else { + Q_ASSERT(qobject_cast<QGraphicsObject *>(object)); } - QWeakPointer<QGesture> state = objectGestures.value(QGestureManager::ObjectGesture(object, type)); + QGesture *state = + objectGestures.value(QGestureManager::ObjectGesture(object, type)); if (!state) { QGestureRecognizer *recognizer = recognizers.value(type); if (recognizer) { state = recognizer->createGesture(object); if (!state) return 0; - if (state.data()->gestureType() == Qt::CustomGesture) { + if (state->gestureType() == Qt::CustomGesture) { // if the recognizer didn't fill in the gesture type, then this // is a custom gesture with autogenerated it and we fill it. - state.data()->d_func()->gestureType = type; + state->d_func()->gestureType = type; +#if defined(GESTURE_DEBUG) + state->setObjectName(QString::number((int)type)); +#endif } objectGestures.insert(QGestureManager::ObjectGesture(object, type), state); - gestureToRecognizer[state.data()] = recognizer; + gestureToRecognizer[state] = recognizer; + gestureOwners[state] = object; } } - return state.data(); + return state; } -bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) +bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, + Qt::GestureType> &contexts, + QEvent *event) { QSet<QGesture *> triggeredGestures; QSet<QGesture *> finishedGestures; @@ -144,93 +157,23 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) QSet<QGesture *> canceledGestures; QSet<QGesture *> notGestures; - QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject *>(receiver); - if (receiver->isWidgetType() || graphicsObject) { - QMap<QObject *, Qt::GestureType> contexts; - if (receiver->isWidgetType()) { - QWidget *w = static_cast<QWidget *>(receiver); - if (!w->d_func()->gestureContext.isEmpty()) { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; - for(ContextIterator it = w->d_func()->gestureContext.begin(), - e = w->d_func()->gestureContext.end(); it != e; ++it) { - contexts.insertMulti(w, it.key()); - } - } - // find all gesture contexts for the widget tree - w = w->parentWidget(); - while (w) - { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; - for (ContextIterator it = w->d_func()->gestureContext.begin(), - e = w->d_func()->gestureContext.end(); it != e; ++it) { - if (it.value() == Qt::WidgetWithChildrenGesture) - contexts.insertMulti(w, it.key()); - } - w = w->parentWidget(); - } - } else { - QGraphicsObject *item = graphicsObject; - if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; - for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), - e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { - contexts.insertMulti(item, it.key()); - } - } - // find all gesture contexts for the widget tree - item = item->parentObject(); - while (item) - { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; - for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), - e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { - if (it.value() == Qt::WidgetWithChildrenGesture) - contexts.insertMulti(item, it.key()); - } - item = item->parentObject(); - } - } - // filter the event through recognizers - typedef QMap<QObject *, Qt::GestureType>::const_iterator ContextIterator; - for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) { - Qt::GestureType gestureType = cit.value(); - QMap<Qt::GestureType, QGestureRecognizer *>::const_iterator - rit = recognizers.lowerBound(gestureType), - re = recognizers.upperBound(gestureType); - for (; rit != re; ++rit) { - QGestureRecognizer *recognizer = rit.value(); - QObject *target = cit.key(); - QGesture *state = getState(target, gestureType); - if (!state) - continue; - QGestureRecognizer::Result result = recognizer->filterEvent(state, target, event); - QGestureRecognizer::Result type = result & QGestureRecognizer::ResultState_Mask; - if (type == QGestureRecognizer::GestureTriggered) { - DEBUG() << "QGestureManager: gesture triggered: " << state; - triggeredGestures << state; - } else if (type == QGestureRecognizer::GestureFinished) { - DEBUG() << "QGestureManager: gesture finished: " << state; - finishedGestures << state; - } else if (type == QGestureRecognizer::MaybeGesture) { - DEBUG() << "QGestureManager: maybe gesture: " << state; - newMaybeGestures << state; - } else if (type == QGestureRecognizer::NotGesture) { - DEBUG() << "QGestureManager: not gesture: " << state; - notGestures << state; - } else if (type == QGestureRecognizer::Ignore) { - DEBUG() << "QGestureManager: gesture ignored the event: " << state; - } else { - DEBUG() << "QGestureManager: hm, lets assume the recognizer ignored the event: " << state; - } - if (result & QGestureRecognizer::ConsumeEventHint) { - DEBUG() << "QGestureManager: we were asked to consume the event: " << state; - //TODO: consume events if asked - } - } - } - } else if (QGesture *state = qobject_cast<QGesture*>(receiver)) { - if (QGestureRecognizer *recognizer = gestureToRecognizer.value(state)) { - QGestureRecognizer::Result result = recognizer->filterEvent(state, state, event); + // TODO: sort contexts by the gesture type and check if one of the contexts + // is already active. + + // filter the event through recognizers + typedef QMap<QObject *, Qt::GestureType>::const_iterator ContextIterator; + for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) { + Qt::GestureType gestureType = cit.value(); + QMap<Qt::GestureType, QGestureRecognizer *>::const_iterator + rit = recognizers.lowerBound(gestureType), + re = recognizers.upperBound(gestureType); + for (; rit != re; ++rit) { + QGestureRecognizer *recognizer = rit.value(); + QObject *target = cit.key(); + QGesture *state = getState(target, gestureType); + if (!state) + continue; + QGestureRecognizer::Result result = recognizer->filterEvent(state, target, event); QGestureRecognizer::Result type = result & QGestureRecognizer::ResultState_Mask; if (type == QGestureRecognizer::GestureTriggered) { DEBUG() << "QGestureManager: gesture triggered: " << state; @@ -247,11 +190,15 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) } else if (type == QGestureRecognizer::Ignore) { DEBUG() << "QGestureManager: gesture ignored the event: " << state; } else { - DEBUG() << "QGestureManager: hm, lets assume the recognizer ignored the event: " << state; + DEBUG() << "QGestureManager: hm, lets assume the recognizer" + << "ignored the event: " << state; + } + if (result & QGestureRecognizer::ConsumeEventHint) { + DEBUG() << "QGestureManager: we were asked to consume the event: " + << state; + //TODO: consume events if asked } } - } else { - return false; } QSet<QGesture *> startedGestures = triggeredGestures - activeGestures; @@ -260,7 +207,8 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) // check if a running gesture switched back to maybe state QSet<QGesture *> activeToMaybeGestures = activeGestures & newMaybeGestures; - // check if a running gesture switched back to not gesture state, i.e. were canceled + // check if a running gesture switched back to not gesture state, + // i.e. were canceled QSet<QGesture *> activeToCancelGestures = activeGestures & notGestures; canceledGestures += activeToCancelGestures; @@ -271,7 +219,9 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) timer.start(3000, this); } // kill timers for gestures that were in maybe state - QSet<QGesture *> notMaybeGestures = (startedGestures | triggeredGestures | finishedGestures | canceledGestures | notGestures); + QSet<QGesture *> notMaybeGestures = (startedGestures | triggeredGestures + | finishedGestures | canceledGestures + | notGestures); foreach(QGesture *gesture, notMaybeGestures) { QMap<QGesture *, QBasicTimer>::iterator it = maybeGestures.find(gesture); @@ -294,7 +244,9 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) // probably those are "singleshot" gestures so we'll fake the started state. foreach (QGesture *gesture, notStarted) gesture->d_func()->state = Qt::GestureStarted; - deliverEvents(notStarted, receiver); + QSet<QGesture *> undeliveredGestures; + deliverEvents(notStarted, &undeliveredGestures); + finishedGestures -= undeliveredGestures; } activeGestures += startedGestures; @@ -328,10 +280,15 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) << "\n\tcanceled:" << canceledGestures; } - deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures, receiver); + QSet<QGesture *> undeliveredGestures; + deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures, + &undeliveredGestures); + + activeGestures -= undeliveredGestures; // reset gestures that ended - QSet<QGesture *> endedGestures = finishedGestures + canceledGestures; + QSet<QGesture *> endedGestures = + finishedGestures + canceledGestures + undeliveredGestures; foreach (QGesture *gesture, endedGestures) { if (QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0)) { recognizer->reset(gesture); @@ -341,100 +298,218 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event) return false; } -void QGestureManager::deliverEvents(const QSet<QGesture*> &gestures, QObject *lastReceiver) +bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) +{ + QSet<Qt::GestureType> types; + QMap<QObject *, Qt::GestureType> contexts; + QWidget *w = receiver; + typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; + if (!w->d_func()->gestureContext.isEmpty()) { + for(ContextIterator it = w->d_func()->gestureContext.begin(), + e = w->d_func()->gestureContext.end(); it != e; ++it) { + types.insert(it.key()); + contexts.insertMulti(w, it.key()); + } + } + // find all gesture contexts for the widget tree + w = w->isWindow() ? 0 : w->parentWidget(); + while (w) + { + for (ContextIterator it = w->d_func()->gestureContext.begin(), + e = w->d_func()->gestureContext.end(); it != e; ++it) { + if (it.value() == Qt::WidgetWithChildrenGesture) { + if (!types.contains(it.key())) { + types.insert(it.key()); + contexts.insertMulti(w, it.key()); + } + } + } + if (w->isWindow()) + break; + w = w->parentWidget(); + } + return filterEventThroughContexts(contexts, event); +} + +bool QGestureManager::filterEvent(QGraphicsObject *receiver, QEvent *event) +{ + QSet<Qt::GestureType> types; + QMap<QObject *, Qt::GestureType> contexts; + QGraphicsObject *item = receiver; + if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) { + typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; + for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), + e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { + types.insert(it.key()); + contexts.insertMulti(item, it.key()); + } + } + // find all gesture contexts for the graphics object tree + item = item->parentObject(); + while (item) + { + typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; + for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), + e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { + if (it.value() == Qt::ItemWithChildrenGesture) { + if (!types.contains(it.key())) + contexts.insertMulti(item, it.key()); + } + } + item = item->parentObject(); + } + return filterEventThroughContexts(contexts, event); +} + +bool QGestureManager::filterEvent(QGesture *state, QEvent *event) +{ + QMap<QObject *, Qt::GestureType> contexts; + contexts.insert(state, state->gestureType()); + return filterEventThroughContexts(contexts, event); +} + +void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures, + QMap<QWidget *, QList<QGesture *> > *conflicts, + QMap<QWidget *, QList<QGesture *> > *normal) +{ + typedef QHash<Qt::GestureType, QHash<QWidget *, QGesture *> > GestureByTypes; + GestureByTypes gestureByTypes; + + // sort gestures by types + foreach (QGesture *gesture, gestures) { + QWidget *receiver = gestureTargets.value(gesture, 0); + Q_ASSERT(receiver); + gestureByTypes[gesture->gestureType()].insert(receiver, gesture); + } + + // for each gesture type + foreach (Qt::GestureType type, gestureByTypes.keys()) { + QHash<QWidget *, QGesture *> gestures = gestureByTypes.value(type); + foreach (QWidget *widget, gestures.keys()) { + QWidget *w = widget->parentWidget(); + while (w) { + QMap<Qt::GestureType, Qt::GestureContext>::const_iterator it + = w->d_func()->gestureContext.find(type); + if (it != w->d_func()->gestureContext.end()) { + // i.e. 'w' listens to gesture 'type' + Qt::GestureContext context = it.value(); + if (context == Qt::WidgetWithChildrenGesture && w != widget) { + // conflicting gesture! + (*conflicts)[widget].append(gestures[widget]); + break; + } + } + if (w->isWindow()) { + w = 0; + break; + } + w = w->parentWidget(); + } + if (!w) + (*normal)[widget].append(gestures[widget]); + } + } +} + +void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures, + QSet<QGesture *> *undeliveredGestures) { if (gestures.isEmpty()) return; - // group gestures by widgets - typedef QMap<QObject *, QList<QGesture *> > GesturesPerReceiver; - GesturesPerReceiver groupedGestures; - // for conflicted gestures the key is always the innermost widget (i.e. the child) - GesturesPerReceiver conflictedGestures; - QMultiHash<QObject *, QGesture *> objectGestures; + typedef QMap<QWidget *, QList<QGesture *> > GesturesPerWidget; + GesturesPerWidget conflictedGestures; + GesturesPerWidget normalStartedGestures; - foreach (QGesture *gesture, gestures) { - QObject *target = gestureTargets.value(gesture, 0); + QSet<QGesture *> startedGestures; + // first figure out the initial receivers of gestures + for (QSet<QGesture *>::const_iterator it = gestures.begin(), + e = gestures.end(); it != e; ++it) { + QGesture *gesture = *it; + QWidget *target = gestureTargets.value(gesture, 0); if (!target) { + // the gesture has just started and doesn't have a target yet. Q_ASSERT(gesture->state() == Qt::GestureStarted); if (gesture->hasHotSpot()) { - // guess the target using the hotspot of the gesture + // guess the target widget using the hotspot of the gesture QPoint pt = gesture->hotSpot().toPoint(); - if (!pt.isNull()) { - if (QWidget *w = qApp->topLevelAt(pt)) - target = w->childAt(w->mapFromGlobal(pt)); + if (QWidget *w = qApp->topLevelAt(pt)) { + target = w->childAt(w->mapFromGlobal(pt)); } + } else { + // or use the context of the gesture + QObject *context = gestureOwners.value(gesture, 0); + if (context->isWidgetType()) + target = static_cast<QWidget *>(context); } - if (!target) { - target = gesture->targetObject(); - if (!target) - target = lastReceiver; - } + if (target) + gestureTargets.insert(gesture, target); } + + Qt::GestureType gestureType = gesture->gestureType(); + Q_ASSERT(gestureType != Qt::CustomGesture); + if (target) { - gestureTargets.insert(gesture, target); - if (target->isWidgetType()) - objectGestures.insert(target, gesture); - groupedGestures[target].append(gesture); + if (gesture->state() == Qt::GestureStarted) { + startedGestures.insert(gesture); + } else { + normalStartedGestures[target].append(gesture); + } } else { - qWarning() << "QGestureManager::deliverEvent: could not find the target for gesture" + DEBUG() << "QGestureManager::deliverEvent: could not find the target for gesture" << gesture->gestureType(); + qWarning("QGestureManager::deliverEvent: could not find the target for gesture"); + undeliveredGestures->insert(gesture); } } - typedef QMultiHash<QObject *, QGesture *>::const_iterator ObjectGesturesIterator; - for (ObjectGesturesIterator it = objectGestures.begin(), e = objectGestures.end(); it != e; ++it) { - QObject *object1 = it.key(); - QWidget *widget1 = qobject_cast<QWidget *>(object1); - QGraphicsObject *item1 = qobject_cast<QGraphicsObject *>(object1); - QGesture *gesture1 = it.value(); - ObjectGesturesIterator cit = it; - for (++cit; cit != e; ++cit) { - QObject *object2 = cit.key(); - QWidget *widget2 = qobject_cast<QWidget *>(object2); - QGraphicsObject *item2 = qobject_cast<QGraphicsObject *>(object2); - QGesture *gesture2 = cit.value(); - // TODO: ugly, rewrite this. - if ((widget1 && widget2 && widget2->isAncestorOf(widget1)) || - (item1 && item2 && item2->isAncestorOf(item1))) { - groupedGestures[object2].removeOne(gesture2); - groupedGestures[object1].removeOne(gesture1); - conflictedGestures[object1].append(gesture1); - } else if ((widget1 && widget2 && widget1->isAncestorOf(widget2)) || - (item1 && item2 && item1->isAncestorOf(item2))) { - groupedGestures[object2].removeOne(gesture2); - groupedGestures[object1].removeOne(gesture1); - conflictedGestures[object2].append(gesture2); - } - } - } - - DEBUG() << "deliverEvents: conflicted =" << conflictedGestures.values() - << " grouped =" << groupedGestures.values(); + getGestureTargets(startedGestures, &conflictedGestures, &normalStartedGestures); + DEBUG() << "QGestureManager::deliverEvents:" + << "\nstarted: " << startedGestures + << "\nconflicted: " << conflictedGestures + << "\nnormal: " << normalStartedGestures + << "\n"; // if there are conflicting gestures, send the GestureOverride event - for (GesturesPerReceiver::const_iterator it = conflictedGestures.begin(), + for (GesturesPerWidget::const_iterator it = conflictedGestures.begin(), e = conflictedGestures.end(); it != e; ++it) { + QWidget *receiver = it.key(); + QList<QGesture *> gestures = it.value(); DEBUG() << "QGestureManager::deliverEvents: sending GestureOverride to" - << it.key() - << " gestures:" << it.value(); - QGestureEvent event(it.value()); + << receiver + << "gestures:" << gestures; + QGestureEvent event(gestures); event.t = QEvent::GestureOverride; + // mark event and individual gestures as ignored event.ignore(); - QApplication::sendEvent(it.key(), &event); - if (!event.isAccepted()) { - // nobody accepted the GestureOverride, put it back to deliver to - // the closest context (i.e. to the inner-most widget). - DEBUG() <<" override was not accepted"; - groupedGestures[it.key()].append(it.value()); + foreach(QGesture *g, gestures) + event.setAccepted(g, false); + + QApplication::sendEvent(receiver, &event); + bool eventAccepted = event.isAccepted(); + foreach(QGesture *gesture, event.allGestures()) { + if (eventAccepted || event.isAccepted(gesture)) { + QWidget *w = event.d_func()->targetWidgets.value(gesture->gestureType(), 0); + Q_ASSERT(w); + DEBUG() << "override event: gesture was accepted:" << gesture << w; + QList<QGesture *> &gestures = normalStartedGestures[w]; + gestures.append(gesture); + // override the target + gestureTargets[gesture] = w; + } else { + DEBUG() << "override event: gesture wasn't accepted. putting back:" << gesture; + QList<QGesture *> &gestures = normalStartedGestures[receiver]; + gestures.append(gesture); + } } } - for (GesturesPerReceiver::const_iterator it = groupedGestures.begin(), - e = groupedGestures.end(); it != e; ++it) { + // delivering gestures that are not in conflicted state + for (GesturesPerWidget::const_iterator it = normalStartedGestures.begin(), + e = normalStartedGestures.end(); it != e; ++it) { if (!it.value().isEmpty()) { DEBUG() << "QGestureManager::deliverEvents: sending to" << it.key() - << " gestures:" << it.value(); + << "gestures:" << it.value(); QGestureEvent event(it.value()); QApplication::sendEvent(it.key(), &event); } @@ -452,7 +527,8 @@ void QGestureManager::timerEvent(QTimerEvent *event) timer.stop(); QGesture *gesture = it.key(); it = maybeGestures.erase(it); - DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:" << gesture; + DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:" + << gesture; QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0); if (recognizer) recognizer->reset(gesture); diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index c61819f..f0e7225 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QBasicTimer; +class QGraphicsObject; class QGestureManager : public QObject { Q_OBJECT @@ -71,13 +72,17 @@ public: Qt::GestureType registerGestureRecognizer(QGestureRecognizer *recognizer); void unregisterGestureRecognizer(Qt::GestureType type); - bool filterEvent(QObject *receiver, QEvent *event); + bool filterEvent(QWidget *receiver, QEvent *event); + bool filterEvent(QGesture *receiver, QEvent *event); + bool filterEvent(QGraphicsObject *receiver, QEvent *event); // declared in qapplication.cpp static QGestureManager* instance(); protected: void timerEvent(QTimerEvent *event); + bool filterEventThroughContexts(const QMap<QObject *, Qt::GestureType> &contexts, + QEvent *event); private: QMultiMap<Qt::GestureType, QGestureRecognizer *> recognizers; @@ -109,15 +114,20 @@ private: } }; - QMap<ObjectGesture, QWeakPointer<QGesture> > objectGestures; + QMap<ObjectGesture, QGesture *> objectGestures; QMap<QGesture *, QGestureRecognizer *> gestureToRecognizer; + QHash<QGesture *, QObject *> gestureOwners; - QHash<QGesture *, QObject *> gestureTargets; + QHash<QGesture *, QWidget *> gestureTargets; int lastCustomGestureId; QGesture *getState(QObject *widget, Qt::GestureType gesture); - void deliverEvents(const QSet<QGesture *> &gestures, QObject *lastReceiver); + void deliverEvents(const QSet<QGesture *> &gestures, + QSet<QGesture *> *undeliveredGestures); + void getGestureTargets(const QSet<QGesture*> &gestures, + QMap<QWidget *, QList<QGesture *> > *conflicts, + QMap<QWidget *, QList<QGesture *> > *normal); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 9de3bcc..ba3a750 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -186,7 +186,7 @@ void QGestureRecognizer::reset(QGesture *gesture) \fn QGestureRecognizer::filterEvent(QGesture *gesture, QObject *watched, QEvent *event) Handles the given \a event for the \a watched object, updating the state of the \a gesture - object as required, and returns a suitable Result for the current recognition step. + object as required, and returns a suitable result for the current recognition step. This function is called by the framework to allow the recognizer to filter input events dispatched to QWidget or QGraphicsObject instances that it is monitoring. diff --git a/src/gui/kernel/qkeymapper_win.cpp b/src/gui/kernel/qkeymapper_win.cpp index be207df..f95efa2 100644 --- a/src/gui/kernel/qkeymapper_win.cpp +++ b/src/gui/kernel/qkeymapper_win.cpp @@ -438,10 +438,10 @@ static const Qt::KeyboardModifiers ModsTbl[] = { */ inline int winceKeyBend(int keyCode) { -#ifdef Q_OS_WINCE_WM +#if defined(Q_OS_WINCE_WM) && defined(QT_KEYPAD_NAVIGATION) // remap return or action key to select key for windows mobile. // will be changed to a table remapping function in the next version (4.6/7). - if (keyCode == 13) + if (keyCode == VK_RETURN && QApplication::keypadNavigationEnabled()) return Qt::Key_Select; else return KeyTbl[keyCode]; diff --git a/src/gui/kernel/qmacgesturerecognizer_mac.mm b/src/gui/kernel/qmacgesturerecognizer_mac.mm index 7b19a54..7019580 100644 --- a/src/gui/kernel/qmacgesturerecognizer_mac.mm +++ b/src/gui/kernel/qmacgesturerecognizer_mac.mm @@ -218,7 +218,7 @@ QMacPanGestureRecognizer::filterEvent(QGesture *gesture, QObject *target, QEvent const QPointF p = QCursor::pos(); const QPointF posOffset = p - _lastPos; g->setLastOffset(g->offset()); - g->setOffset(QSizeF(posOffset.x(), posOffset.y())); + g->setOffset(QPointF(posOffset.x(), posOffset.y())); g->setTotalOffset(g->lastOffset() + g->offset()); _lastPos = p; return QGestureRecognizer::GestureTriggered; @@ -256,9 +256,9 @@ void QMacPanGestureRecognizer::reset(QGesture *gesture) _startPos = QPointF(); _lastPos = QPointF(); _panCanceled = true; - g->setOffset(QSizeF(0, 0)); - g->setLastOffset(QSizeF(0, 0)); - g->setTotalOffset(QSizeF(0, 0)); + g->setOffset(QPointF(0, 0)); + g->setLastOffset(QPointF(0, 0)); + g->setTotalOffset(QPointF(0, 0)); g->setAcceleration(qreal(1)); QGestureRecognizer::reset(gesture); } diff --git a/src/gui/kernel/qstandardgestures.cpp b/src/gui/kernel/qstandardgestures.cpp index dfc3499..a136379 100644 --- a/src/gui/kernel/qstandardgestures.cpp +++ b/src/gui/kernel/qstandardgestures.cpp @@ -73,7 +73,7 @@ QGestureRecognizer::Result QPanGestureRecognizer::filterEvent(QGesture *state, Q result = QGestureRecognizer::MaybeGesture; QTouchEvent::TouchPoint p = ev->touchPoints().at(0); d->lastPosition = p.pos().toPoint(); - d->lastOffset = d->totalOffset = d->offset = QSize(); + d->lastOffset = d->totalOffset = d->offset = QPointF(); break; } case QEvent::TouchEnd: { @@ -83,7 +83,7 @@ QGestureRecognizer::Result QPanGestureRecognizer::filterEvent(QGesture *state, Q QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); d->lastOffset = d->offset; d->offset = - QSize(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(), + QPointF(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(), p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2; d->totalOffset += d->offset; } @@ -99,11 +99,11 @@ QGestureRecognizer::Result QPanGestureRecognizer::filterEvent(QGesture *state, Q QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); d->lastOffset = d->offset; d->offset = - QSize(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(), + QPointF(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(), p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2; d->totalOffset += d->offset; - if (d->totalOffset.width() > 10 || d->totalOffset.height() > 10 || - d->totalOffset.width() < -10 || d->totalOffset.height() < -10) { + if (d->totalOffset.x() > 10 || d->totalOffset.y() > 10 || + d->totalOffset.x() < -10 || d->totalOffset.y() < -10) { result = QGestureRecognizer::GestureTriggered; } else { result = QGestureRecognizer::MaybeGesture; @@ -128,7 +128,7 @@ void QPanGestureRecognizer::reset(QGesture *state) QPanGesture *pan = static_cast<QPanGesture*>(state); QPanGesturePrivate *d = pan->d_func(); - d->totalOffset = d->lastOffset = d->offset = QSizeF(); + d->totalOffset = d->lastOffset = d->offset = QPointF(); d->lastPosition = QPoint(); d->acceleration = 0; diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index d33791b..e25bc81 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -81,6 +81,9 @@ QT_BEGIN_NAMESPACE // system events seems to start with 0x10 const TInt KInternalStatusPaneChange = 0x50000000; +//this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2 +#define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13) + class QS60Data { public: @@ -108,6 +111,7 @@ public: int mouseInteractionEnabled : 1; int virtualMouseRequired : 1; int qtOwnsS60Environment : 1; + int supportsPremultipliedAlpha : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type static inline void updateScreenSize(); static inline RWsSession& wsSession(); @@ -199,7 +203,7 @@ inline void QS60Data::updateScreenSize() S60->screenHeightInPixels = params.iPixelSize.iHeight; S60->screenWidthInTwips = params.iTwipsSize.iWidth; S60->screenHeightInTwips = params.iTwipsSize.iHeight; - + S60->virtualMouseMaxAccel = qMax(S60->screenHeightInPixels, S60->screenWidthInPixels) / 20; TReal inches = S60->screenHeightInTwips / (TReal)KTwipsPerInch; @@ -302,11 +306,9 @@ static inline QImage::Format qt_TDisplayMode2Format(TDisplayMode mode) case EColor16MA: format = QImage::Format_ARGB32; break; -#if !defined(__SERIES60_31__) && !defined(__S60_32__) - case EColor16MAP: + case Q_SYMBIAN_ECOLOR16MAP: format = QImage::Format_ARGB32_Premultiplied; break; -#endif default: format = QImage::Format_Invalid; break; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index de08312..5fa9a92 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -93,6 +93,7 @@ # include "qx11info_x11.h" #endif +#include <private/qgraphicseffect_p.h> #include <private/qwindowsurface_p.h> #include <private/qbackingstore_p.h> #ifdef Q_WS_MAC @@ -149,24 +150,6 @@ static inline bool hasBackingStoreSupport() #endif } -/*! - \internal - - Returns true if \a p or any of its parents enable the - Qt::BypassGraphicsProxyWidget window flag. Used in QWidget::show() and - QWidget::setParent() to determine whether it's necessary to embed the - widget into a QGraphicsProxyWidget or not. -*/ -static inline bool bypassGraphicsProxyWidget(QWidget *p) -{ - while (p) { - if (p->windowFlags() & Qt::BypassGraphicsProxyWidget) - return true; - p = p->parentWidget(); - } - return false; -} - #ifdef Q_WS_MAC # define QT_NO_PAINT_DEBUG #endif @@ -1806,12 +1789,29 @@ QRegion QWidgetPrivate::clipRegion() const return r; } +void QWidgetPrivate::invalidateGraphicsEffectsRecursively() +{ + Q_Q(QWidget); + QWidget *w = q; + do { + if (w->graphicsEffect()) { + QWidgetEffectSourcePrivate *sourced = + static_cast<QWidgetEffectSourcePrivate *>(w->graphicsEffect()->source()->d_func()); + if (!sourced->updateDueToGraphicsEffect) + w->graphicsEffect()->source()->d_func()->invalidateCache(); + } + w = w->parentWidget(); + } while (w); +} + void QWidgetPrivate::setDirtyOpaqueRegion() { Q_Q(QWidget); dirtyOpaqueChildren = true; + invalidateGraphicsEffectsRecursively(); + if (q->isWindow()) return; @@ -5215,6 +5215,10 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP paintEngine->d_func()->systemClip = QRegion(); } else { context.painter = sharedPainter; + if (sharedPainter->worldTransform() != sourced->lastEffectTransform) { + sourced->invalidateCache(); + sourced->lastEffectTransform = sharedPainter->worldTransform(); + } sharedPainter->save(); sharedPainter->translate(offset); graphicsEffect->draw(sharedPainter, source); @@ -5487,6 +5491,7 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * return pixmap; } +#ifndef QT_NO_GRAPHICSVIEW /*! \internal @@ -5495,7 +5500,7 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * If successful, the function returns the proxy that embeds the widget, or 0 if no embedded widget was found. */ -QGraphicsProxyWidget * QWidgetPrivate::nearestGraphicsProxyWidget(QWidget *origin) +QGraphicsProxyWidget * QWidgetPrivate::nearestGraphicsProxyWidget(const QWidget *origin) { if (origin) { QWExtra *extra = origin->d_func()->extra; @@ -5505,6 +5510,7 @@ QGraphicsProxyWidget * QWidgetPrivate::nearestGraphicsProxyWidget(QWidget *origi } return 0; } +#endif /*! \property QWidget::locale @@ -7314,7 +7320,7 @@ void QWidget::setVisible(bool visible) break; parent = parent->parentWidget(); } - if (parent && !d->getOpaqueRegion().isEmpty()) + if (parent) parent->d_func()->setDirtyOpaqueRegion(); } @@ -7739,6 +7745,10 @@ void QWidget::adjustSize() Q_D(QWidget); ensurePolished(); QSize s = d->adjustedSize(); + + if (d->layout) + d->layout->activate(); + if (s.isValid()) resize(s); } diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index c966aa3..d08f8a9 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -305,6 +305,8 @@ bool qt_mac_insideKeyWindow(const QWidget *w) { #ifdef QT_MAC_USE_COCOA return [[reinterpret_cast<NSView *>(w->winId()) window] isKeyWindow]; +#else + Q_UNUSED(w); #endif return false; } @@ -3046,6 +3048,7 @@ void QWidget::grabMouse() } } +#ifndef QT_NO_CURSOR void QWidget::grabMouse(const QCursor &) { if(isVisible() && !qt_nograb()) { @@ -3054,6 +3057,7 @@ void QWidget::grabMouse(const QCursor &) mac_mouse_grabber=this; } } +#endif void QWidget::releaseMouse() { @@ -3301,7 +3305,11 @@ void QWidgetPrivate::show_sys() [window miniaturize:window]; #endif } else if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) { +#ifndef QT_MAC_USE_COCOA qt_event_request_activate(q); +#else + [qt_mac_window_for(q) makeKeyWindow]; +#endif } } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) { #ifndef QT_MAC_USE_COCOA @@ -3404,8 +3412,13 @@ void QWidgetPrivate::hide_sys() } #endif } - if(w && w->isVisible() && !w->isMinimized()) - qt_event_request_activate(w); + if(w && w->isVisible() && !w->isMinimized()) { +#ifndef QT_MAC_USE_COCOA + qt_event_request_activate(w); +#else + [qt_mac_window_for(w) makeKeyWindow]; +#endif + } } } else { invalidateBuffer(q->rect()); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index f7c2712..159a3f2 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -63,7 +63,9 @@ #include "QtGui/qstyle.h" #include "QtGui/qapplication.h" #include <private/qgraphicseffect_p.h> - +#include "QtGui/qgraphicsproxywidget.h" +#include "QtGui/qgraphicsscene.h" +#include "QtGui/qgraphicsview.h" #include <private/qgesture_p.h> #ifdef Q_WS_WIN @@ -180,7 +182,9 @@ struct QWExtra { // Regular pointers (keep them together to avoid gaps on 64 bits architectures). void *glContext; // if the widget is hijacked by QGLWindowSurface QTLWExtra *topextra; // only useful for TLWs +#ifndef QT_NO_GRAPHICSVIEW QGraphicsProxyWidget *proxyWidget; // if the widget is embedded +#endif #ifndef QT_NO_CURSOR QCursor *curs; #endif @@ -235,6 +239,24 @@ struct QWExtra { #endif }; +/*! + \internal + + Returns true if \a p or any of its parents enable the + Qt::BypassGraphicsProxyWidget window flag. Used in QWidget::show() and + QWidget::setParent() to determine whether it's necessary to embed the + widget into a QGraphicsProxyWidget or not. +*/ +static inline bool bypassGraphicsProxyWidget(const QWidget *p) +{ + while (p) { + if (p->windowFlags() & Qt::BypassGraphicsProxyWidget) + return true; + p = p->parentWidget(); + } + return false; +} + class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QWidget) @@ -345,7 +367,9 @@ public: QPainter *beginSharedPainter(); bool endSharedPainter(); - static QGraphicsProxyWidget * nearestGraphicsProxyWidget(QWidget *origin); +#ifndef QT_NO_GRAPHICSVIEW + static QGraphicsProxyWidget * nearestGraphicsProxyWidget(const QWidget *origin); +#endif QWindowSurface *createDefaultWindowSurface(); QWindowSurface *createDefaultWindowSurface_sys(); void repaint_sys(const QRegion &rgn); @@ -360,6 +384,7 @@ public: void setOpaque(bool opaque); void updateIsTranslucent(); bool paintOnScreen() const; + void invalidateGraphicsEffectsRecursively(); QRegion getOpaqueRegion() const; const QRegion &getOpaqueChildren() const; @@ -442,6 +467,31 @@ public: void setModal_sys(); + // This is an helper function that return the available geometry for + // a widget and takes care is this one is in QGraphicsView. + // If the widget is not embed in a scene then the geometry available is + // null, we let QDesktopWidget decide for us. + static QRect screenGeometry(const QWidget *widget) + { + QRect screen; +#ifndef QT_NO_GRAPHICSVIEW + QGraphicsProxyWidget *ancestorProxy = widget->d_func()->nearestGraphicsProxyWidget(widget); + //It's embedded if it has an ancestor + if (ancestorProxy) { + if (!bypassGraphicsProxyWidget(widget)) { + // One view, let be smart and return the viewport rect then the popup is aligned + if (ancestorProxy->scene()->views().size() == 1) { + QGraphicsView *view = ancestorProxy->scene()->views().at(0); + screen = view->mapToScene(view->viewport()->rect()).boundingRect().toRect(); + } else { + screen = ancestorProxy->scene()->sceneRect().toRect(); + } + } + } +#endif + return screen; + } + inline void setRedirected(QPaintDevice *replacement, const QPoint &offset) { Q_ASSERT(q_func()->testAttribute(Qt::WA_WState_InPaintEvent)); @@ -729,7 +779,7 @@ class QWidgetEffectSourcePrivate : public QGraphicsEffectSourcePrivate { public: QWidgetEffectSourcePrivate(QWidget *widget) - : QGraphicsEffectSourcePrivate(), m_widget(widget), context(0) + : QGraphicsEffectSourcePrivate(), m_widget(widget), context(0), updateDueToGraphicsEffect(false) {} inline void detach() @@ -742,7 +792,11 @@ public: { return m_widget; } inline void update() - { m_widget->update(); } + { + updateDueToGraphicsEffect = true; + m_widget->update(); + updateDueToGraphicsEffect = false; + } inline bool isPixmap() const { return false; } @@ -754,7 +808,7 @@ public: if (QWidget *parent = m_widget->parentWidget()) parent->update(); else - m_widget->update(); + update(); } inline const QStyleOption *styleOption() const @@ -769,6 +823,8 @@ public: QWidget *m_widget; QWidgetPaintContext *context; + QTransform lastEffectTransform; + bool updateDueToGraphicsEffect; }; inline QWExtra *QWidgetPrivate::extraData() const diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 2b11bec..fa12b0d 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -851,10 +851,13 @@ void QWidget::grabMouse() Q_ASSERT(testAttribute(Qt::WA_WState_Created)); SetCapture(effectiveWinId()); mouseGrb = this; +#ifndef QT_NO_CURSOR mouseGrbCur = new QCursor(mouseGrb->cursor()); +#endif } } +#ifndef QT_NO_CURSOR void QWidget::grabMouse(const QCursor &cursor) { if (!qt_nograb()) { @@ -868,6 +871,7 @@ void QWidget::grabMouse(const QCursor &cursor) mouseGrb = this; } } +#endif void QWidget::releaseMouse() { diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 663178f..28676da 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -950,7 +950,7 @@ static void qt_x11_recreateWidget(QWidget *widget) static void qt_x11_recreateNativeWidgetsRecursive(QWidget *widget) { - if (widget->testAttribute(Qt::WA_NativeWindow)) + if (widget->internalWinId()) qt_x11_recreateWidget(widget); const QObjectList &children = widget->children(); diff --git a/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp b/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp index 4619594..12d3058 100644 --- a/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp +++ b/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp @@ -97,11 +97,11 @@ QGestureRecognizer::Result QWinNativePanGestureRecognizer::filterEvent(QGesture return QGestureRecognizer::Ignore; } if (q->state() == Qt::NoGesture) { - d->lastOffset = d->totalOffset = d->offset = QSize(); + d->lastOffset = d->totalOffset = d->offset = QPointF(); } else { d->lastOffset = d->offset; - d->offset = QSize(ev->position.x() - d->lastPosition.x(), - ev->position.y() - d->lastPosition.y()); + d->offset = QPointF(ev->position.x() - d->lastPosition.x(), + ev->position.y() - d->lastPosition.y()); d->totalOffset += d->offset; } d->lastPosition = ev->position; @@ -114,7 +114,7 @@ void QWinNativePanGestureRecognizer::reset(QGesture *state) QPanGesture *pan = static_cast<QPanGesture*>(state); QPanGesturePrivate *d = pan->d_func(); - d->totalOffset = d->lastOffset = d->offset = QSizeF(); + d->totalOffset = d->lastOffset = d->offset = QPointF(); d->lastPosition = QPoint(); d->acceleration = 0; diff --git a/src/gui/kernel/symbian.pri b/src/gui/kernel/symbian.pri index 5497ccb..69422dd 100644 --- a/src/gui/kernel/symbian.pri +++ b/src/gui/kernel/symbian.pri @@ -1,4 +1,7 @@ symbian { contains(QT_CONFIG, s60): LIBS+= $$QMAKE_LIBS_S60 RESOURCES += symbian/symbianresources.qrc + + HEADERS += symbian/qsymbianevent.h + SOURCES += symbian/qsymbianevent.cpp } diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index ed1b13d..00e8f15 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -1430,6 +1430,24 @@ QMatrix QMatrix4x4::toAffine() const m[3][0], m[3][1]); } +/*! + Returns the conventional Qt 2D transformation matrix that + corresponds to this matrix. + + The returned QTransform is formed by simply dropping the + third row and third column of the QMatrix4x4. This is suitable + for implementing orthographic projections where the z co-ordinate + should be dropped rather than projected. + + \sa toAffine() +*/ +QTransform QMatrix4x4::toTransform() const +{ + return QTransform(m[0][0], m[0][1], m[0][3], + m[1][0], m[1][1], m[1][3], + m[3][0], m[3][1], m[3][3]); +} + static const qreal inv_dist_to_plane = 1. / 1024.; /*! @@ -1437,8 +1455,8 @@ static const qreal inv_dist_to_plane = 1. / 1024.; corresponds to this matrix. If \a distanceToPlane is non-zero, it indicates a projection - factor to use to adjust for the z co-ordinate. The default - value of 1024 corresponds to the projection factor used + factor to use to adjust for the z co-ordinate. The value of + 1024 corresponds to the projection factor used by QTransform::rotate() for the x and y axes. If \a distanceToPlane is zero, then the returned QTransform diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index b32e00a..42d992e 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -159,7 +159,8 @@ public: void toValueArray(qreal *values) const; QMatrix toAffine() const; - QTransform toTransform(qreal distanceToPlane = 1024.0f) const; + QTransform toTransform() const; + QTransform toTransform(qreal distanceToPlane) const; QPoint map(const QPoint& point) const; QPointF map(const QPointF& point) const; diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 7c07df8..3cd1402 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -56,6 +56,7 @@ #include <private/qwindowsurface_raster_p.h> #include <private/qapplication_p.h> #include <private/qpaintengine_raster_p.h> +#include <private/qgraphicseffect_p.h> #include "qgraphicssystem_p.h" @@ -540,6 +541,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up Q_ASSERT(widget->window() == tlw); Q_ASSERT(!rgn.isEmpty()); + widget->d_func()->invalidateGraphicsEffectsRecursively(); + if (widget->d_func()->paintOnScreen()) { if (widget->d_func()->dirty.isEmpty()) { widget->d_func()->dirty = rgn; @@ -615,6 +618,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd Q_ASSERT(widget->window() == tlw); Q_ASSERT(!rect.isEmpty()); + widget->d_func()->invalidateGraphicsEffectsRecursively(); + if (widget->d_func()->paintOnScreen()) { if (widget->d_func()->dirty.isEmpty()) { widget->d_func()->dirty = QRegion(rect); diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h index 94d756e..3288dae 100644 --- a/src/gui/painting/qbackingstore_p.h +++ b/src/gui/painting/qbackingstore_p.h @@ -97,6 +97,12 @@ public: ); } + // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). + void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false, + bool invalidateBuffer = false); + void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false, + bool invalidateBuffer = false); + private: QWidget *tlw; QRegion dirtyOnScreen; // needsFlush @@ -126,11 +132,6 @@ private: QRegion dirtyRegion(QWidget *widget = 0) const; QRegion staticContents(QWidget *widget = 0, const QRect &withinClipRect = QRect()) const; - // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). - void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false, - bool invalidateBuffer = false); - void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false, - bool invalidateBuffer = false); void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); void removeDirtyWidget(QWidget *w); diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 5b82e7b..bf4b4ea 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -278,6 +278,7 @@ private: friend class QWin32PaintEnginePrivate; friend class QMacCGContext; friend class QPreviewPaintEngine; + friend class QX11GLPixmapData; }; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index fab2d8d..fd0e810 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -132,6 +132,10 @@ static const qreal aliasedCoordinateDelta = 0.5 - 0.015625; extern bool qt_cleartype_enabled; #endif +#ifdef Q_WS_MAC +extern bool qt_applefontsmoothing_enabled; +#endif + /******************************************************************************** * Span functions @@ -508,7 +512,7 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) #if defined(Q_WS_WIN) else if (qt_cleartype_enabled) #elif defined (Q_WS_MAC) - else if (true) + else if (qt_applefontsmoothing_enabled) #else else if (false) #endif diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 9e5707d..a192e87 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -229,16 +229,9 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) int QImageTextureGlyphCache::glyphMargin() const { -#ifdef Q_WS_MAC - -#ifdef QT_MAC_USE_COCOA - // For cocoa the margin is built into the glyph it seems.. +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) return 0; #else - return 2; -#endif - -#else return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0; #endif } diff --git a/src/gui/s60framework/qs60mainappui.cpp b/src/gui/s60framework/qs60mainappui.cpp index e630253..4ad78f9 100644 --- a/src/gui/s60framework/qs60mainappui.cpp +++ b/src/gui/s60framework/qs60mainappui.cpp @@ -50,6 +50,7 @@ #include "qs60mainappui.h" #include <QtGui/qapplication.h> +#include <QtGui/qsymbianevent.h> #include <QtGui/qmenu.h> #include <private/qmenu_p.h> #include <private/qt_s60_p.h> @@ -134,8 +135,10 @@ QS60MainAppUi::~QS60MainAppUi() */ void QS60MainAppUi::HandleCommandL(TInt command) { - if (qApp) - QT_TRYCATCH_LEAVING(qApp->symbianHandleCommand(command)); + if (qApp) { + QSymbianEvent event(QSymbianEvent::CommandEvent, command); + QT_TRYCATCH_LEAVING(qApp->symbianProcessEvent(&event)); + } } /*! @@ -151,8 +154,10 @@ void QS60MainAppUi::HandleResourceChangeL(TInt type) { CAknAppUi::HandleResourceChangeL(type); - if (qApp) - QT_TRYCATCH_LEAVING(qApp->symbianResourceChange(type)); + if (qApp) { + QSymbianEvent event(QSymbianEvent::ResourceChangeEvent, type); + QT_TRYCATCH_LEAVING(qApp->symbianProcessEvent(&event)); + } } /*! @@ -164,16 +169,18 @@ void QS60MainAppUi::HandleResourceChangeL(TInt type) * If you override this function, you should call the base class implementation if you do not * handle the event. */ -void QS60MainAppUi::HandleWsEventL(const TWsEvent& event, CCoeControl *destination) +void QS60MainAppUi::HandleWsEventL(const TWsEvent& wsEvent, CCoeControl *destination) { int result = 0; - if (qApp) + if (qApp) { + QSymbianEvent event(&wsEvent); QT_TRYCATCH_LEAVING( - result = qApp->s60ProcessEvent(const_cast<TWsEvent*>(&event)) + result = qApp->symbianProcessEvent(&event) ); + } if (result <= 0) - CAknAppUi::HandleWsEventL(event, destination); + CAknAppUi::HandleWsEventL(wsEvent, destination); } diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp index fabd7ca..fc12cfe 100644 --- a/src/gui/styles/qcleanlooksstyle.cpp +++ b/src/gui/styles/qcleanlooksstyle.cpp @@ -3866,7 +3866,7 @@ QSize QCleanlooksStyle::sizeFromContents(ContentsType type, const QStyleOption * if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { if (!menuItem->text.isEmpty()) { - newSize.setHeight(menuItem->fontMetrics.lineSpacing()); + newSize.setHeight(menuItem->fontMetrics.height()); } } #ifndef QT_NO_COMBOBOX diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp index 5886512..70d130a 100644 --- a/src/gui/styles/qcommonstyle.cpp +++ b/src/gui/styles/qcommonstyle.cpp @@ -4396,13 +4396,13 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid case PM_TitleBarHeight: { if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) { - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 16); + ret = qMax(widget ? widget->fontMetrics().height() : opt->fontMetrics.height(), 16); #ifndef QT_NO_DOCKWIDGET } else if (qobject_cast<const QDockWidget*>(widget)) { - ret = qMax(widget->fontMetrics().lineSpacing(), int(QStyleHelper::dpiScaled(13))); + ret = qMax(widget->fontMetrics().height(), int(QStyleHelper::dpiScaled(13))); #endif } else { - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 18); + ret = qMax(widget ? widget->fontMetrics().height() : opt->fontMetrics.height(), 18); } } else { ret = int(QStyleHelper::dpiScaled(18.)); diff --git a/src/gui/styles/qmotifstyle.cpp b/src/gui/styles/qmotifstyle.cpp index e6c60cf..b65d45c 100644 --- a/src/gui/styles/qmotifstyle.cpp +++ b/src/gui/styles/qmotifstyle.cpp @@ -1154,7 +1154,7 @@ void QMotifStyle::drawControl(ControlElement element, const QStyleOption *opt, Q menuitem->palette, menuitem->state & State_Enabled, menuitem->text, QPalette::Text); textWidth = menuitem->fontMetrics.width(menuitem->text) + 10; - y += menuitem->fontMetrics.lineSpacing() / 2; + y += menuitem->fontMetrics.height() / 2; p->setFont(oldFont); } p->setPen(opt->palette.dark().color()); @@ -2056,7 +2056,7 @@ QMotifStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (mi->menuItemType == QStyleOptionMenuItem::Separator) { w = 10; - h = (mi->text.isEmpty()) ? motifSepHeight : mi->fontMetrics.lineSpacing(); + h = (mi->text.isEmpty()) ? motifSepHeight : mi->fontMetrics.height(); } // a little bit of border can never harm diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp index ce2109a..09f5d36 100644 --- a/src/gui/styles/qplastiquestyle.cpp +++ b/src/gui/styles/qplastiquestyle.cpp @@ -5027,7 +5027,7 @@ QSize QPlastiqueStyle::sizeFromContents(ContentsType type, const QStyleOption *o case CT_MenuItem: if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) - newSize.setHeight(menuItem->text.isEmpty() ? 2 : menuItem->fontMetrics.lineSpacing()); + newSize.setHeight(menuItem->text.isEmpty() ? 2 : menuItem->fontMetrics.height()); } break; case CT_MenuBarItem: @@ -5607,11 +5607,11 @@ int QPlastiqueStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, #ifdef QT3_SUPPORT if (widget && widget->inherits("Q3DockWindowTitleBar")) { // Q3DockWindow has smaller title bars than QDockWidget - ret = qMax(widget->fontMetrics().lineSpacing(), 20); + ret = qMax(widget->fontMetrics().height(), 20); } else #endif - ret = qMax(widget ? widget->fontMetrics().lineSpacing() : - (option ? option->fontMetrics.lineSpacing() : 0), 30); + ret = qMax(widget ? widget->fontMetrics().height() : + (option ? option->fontMetrics.height() : 0), 30); break; case PM_MaximumDragDistance: return -1; diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index 4fa1d03..4c6bc46 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -1313,7 +1313,7 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, painter->save(); painter->setClipRect(voptAdj.rect); - const bool isSelected = (voptAdj.state & QStyle::State_HasFocus); + const bool isSelected = (vopt->state & QStyle::State_Selected); bool isVisible = false; int scrollBarWidth = 0; @@ -1358,7 +1358,27 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, // draw the focus rect if (isSelected) { - const QRect highlightRect = option->rect.adjusted(1,1,-1,-1); + QRect highlightRect = option->rect.adjusted(1,1,-1,-1); + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); + if (view && view->selectionBehavior() != QAbstractItemView::SelectItems) { + // set highlight rect so that it is continuous from cell to cell, yet sligthly + // smaller than cell rect + int xBeginning = 0, yBeginning = 0, xEnd = 0, yEnd = 0; + if (view->selectionBehavior() == QAbstractItemView::SelectRows) { + yBeginning = 1; yEnd = -1; + if (vopt->viewItemPosition == QStyleOptionViewItemV4::Beginning) + xBeginning = 1; + else if (vopt->viewItemPosition == QStyleOptionViewItemV4::End) + xEnd = -1; + } else if (view->selectionBehavior() == QAbstractItemView::SelectColumns) { + xBeginning = 1; xEnd = -1; + if (vopt->viewItemPosition == QStyleOptionViewItemV4::Beginning) + yBeginning = 1; + else if (vopt->viewItemPosition == QStyleOptionViewItemV4::End) + yEnd = -1; + } + highlightRect = option->rect.adjusted(xBeginning, yBeginning, xEnd, xBeginning); + } QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_ListHighlight, painter, highlightRect, flags); } @@ -1824,8 +1844,8 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, //todo: update to horizontal table graphic QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_TableHeaderItem, painter, option->rect, flags | QS60StylePrivate::SF_PointWest); } - } else if (qobject_cast<const QFrame *>(widget)) { - QCommonStyle::drawControl(element, option, painter, widget); + } else if (qobject_cast<const QFrame *>(widget)) { + QCommonStyle::drawControl(element, option, painter, widget); } break; case CE_MenuScroller: @@ -2196,6 +2216,12 @@ QSize QS60Style::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) sz += QSize(2*f->lineWidth, 4*f->lineWidth); break; + case CT_TabBarTab: + QSize naviPaneSize = QS60StylePrivate::naviPaneSize(); + sz = QCommonStyle::sizeFromContents( ct, opt, csz, widget); + if (naviPaneSize.height() > sz.height()) + sz.setHeight(naviPaneSize.height()); + break; default: sz = QCommonStyle::sizeFromContents( ct, opt, csz, widget); break; @@ -2405,8 +2431,8 @@ QRect QS60Style::subControlRect(ComplexControl control, const QStyleOptionComple case SC_ComboBoxArrow: ret.setRect( ret.x() + ret.width() - buttonMargin - buttonWidth, - ret.y() + buttonMargin, - buttonWidth, + ret.y() + buttonMargin, + buttonWidth, height - 2*buttonMargin); break; case SC_ComboBoxEditField: { diff --git a/src/gui/styles/qs60style.h b/src/gui/styles/qs60style.h index 6be3197..ab10792 100644 --- a/src/gui/styles/qs60style.h +++ b/src/gui/styles/qs60style.h @@ -101,7 +101,7 @@ protected Q_SLOTS: private: Q_DISABLE_COPY(QS60Style) friend class QStyleFactory; - friend class QApplication; + friend class QApplicationPrivate; }; #endif // QT_NO_STYLE_S60 diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index 5422ff6..8e53eee 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -460,6 +460,8 @@ public: void handleSkinChange(); #endif // Q_WS_S60 + static QSize naviPaneSize(); + private: static void drawPart(QS60StyleEnums::SkinParts part, QPainter *painter, const QRect &rect, SkinElementFlags flags = KDefaultSkinElementFlags); diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index cde48d8..9765066 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -61,6 +61,7 @@ #include <AknFontAccess.h> #include <AknLayoutFont.h> #include <aknutils.h> +#include <aknnavi.h> #if !defined(QT_NO_STYLE_S60) || defined(QT_PLUGIN) @@ -104,6 +105,7 @@ public: static bool disabledPartGraphic(QS60StyleEnums::SkinParts &part); static bool disabledFrameGraphic(QS60StylePrivate::SkinFrameElements &frame); static QPixmap generateMissingThemeGraphic(QS60StyleEnums::SkinParts &part, const QSize &size, QS60StylePrivate::SkinElementFlags flags); + static QSize naviPaneSize(); private: static QPixmap createSkinnedGraphicsLX(QS60StyleEnums::SkinParts part, @@ -747,9 +749,8 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX(QS60StylePrivate::SkinFr QPixmap result; // QS60WindowSurface::unlockBitmapHeap(); - static const bool canDoEColor16MAP = !(QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2); - static const TDisplayMode displayMode = canDoEColor16MAP ? TDisplayMode(13) : EColor16MA; // 13 = EColor16MAP - static const TInt drawParam = canDoEColor16MAP ? KAknsDrawParamDefault : KAknsDrawParamNoClearUnderImage|KAknsDrawParamRGBOnly; + static const TDisplayMode displayMode = S60->supportsPremultipliedAlpha ? Q_SYMBIAN_ECOLOR16MAP : EColor16MA; + static const TInt drawParam = S60->supportsPremultipliedAlpha ? KAknsDrawParamDefault : KAknsDrawParamNoClearUnderImage|KAknsDrawParamRGBOnly; CFbsBitmap *frame = new (ELeave) CFbsBitmap(); //offscreen CleanupStack::PushL(frame); @@ -776,7 +777,7 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX(QS60StylePrivate::SkinFr frameSkinID, centerSkinID, drawParam ); - if (canDoEColor16MAP) { + if (S60->supportsPremultipliedAlpha) { if (drawn) result = fromFbsBitmap(frame, NULL, flags, QImage::Format_ARGB32_Premultiplied); } else { @@ -1389,6 +1390,24 @@ void QS60StylePrivate::handleSkinChange() topLevelWidget->ensurePolished(); } } + +QSize QS60StylePrivate::naviPaneSize() +{ + return QS60StyleModeSpecifics::naviPaneSize(); +} + +QSize QS60StyleModeSpecifics::naviPaneSize() +{ + CAknNavigationControlContainer* naviContainer; + if (S60->statusPane()) + naviContainer = static_cast<CAknNavigationControlContainer*> + (S60->statusPane()->ControlL(TUid::Uid(EEikStatusPaneUidNavi))); + if (naviContainer) + return QSize(naviContainer->Size().iWidth, naviContainer->Size().iHeight); + else + return QSize(0,0); +} + #endif // Q_WS_S60 QT_END_NAMESPACE diff --git a/src/gui/styles/qs60style_simulated.cpp b/src/gui/styles/qs60style_simulated.cpp index 89a9158..8a2616d 100644 --- a/src/gui/styles/qs60style_simulated.cpp +++ b/src/gui/styles/qs60style_simulated.cpp @@ -326,6 +326,10 @@ QPixmap QS60StylePrivate::backgroundTexture() return *m_background; } +QSize QS60StylePrivate::naviPaneSize() +{ + return QSize(0, 0); +} bool QS60StylePrivate::isTouchSupported() { diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index 707b05e..2d90aa1 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -4722,7 +4722,7 @@ int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const return subRule.size().height(); else if (subRule.hasBox() || subRule.hasBorder()) { QFontMetrics fm = opt ? opt->fontMetrics : w->fontMetrics(); - return subRule.size(QSize(0, fm.lineSpacing())).height(); + return subRule.size(QSize(0, fm.height())).height(); } break; } diff --git a/src/gui/styles/qwindowsmobilestyle.cpp b/src/gui/styles/qwindowsmobilestyle.cpp index a617102..7ed187f 100644 --- a/src/gui/styles/qwindowsmobilestyle.cpp +++ b/src/gui/styles/qwindowsmobilestyle.cpp @@ -5347,10 +5347,8 @@ void QWindowsMobileStyle::drawPrimitive(PrimitiveElement element, const QStyleOp painter->drawLines(a); break; } case PE_Frame: - if (d->doubleControls) - qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),2,&option->palette.light()); - else - qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),1,&option->palette.light()); + qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), + d->doubleControls ? 2 : 1, &option->palette.background()); break; case PE_FrameLineEdit: case PE_FrameMenu: @@ -5592,8 +5590,43 @@ void QWindowsMobileStyle::drawControl(ControlElement element, const QStyleOption painter->drawLine(rect.bottomLeft(), rect.bottomRight()); } #endif // QT_NO_SCROLLAREA - break; } - + break; + } +#ifndef QT_NO_COMBOBOX + case CE_ComboBoxLabel: + // This is copied from qcommonstyle.cpp with the difference, that + // the editRect isn't adjusted when calling drawItemText. + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + QRect editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget); + painter->save(); + painter->setClipRect(editRect); + if (!cb->currentIcon.isNull()) { + QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal + : QIcon::Disabled; + QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode); + QRect iconRect(editRect); + iconRect.setWidth(cb->iconSize.width() + 4); + iconRect = alignedRect(cb->direction, + Qt::AlignLeft | Qt::AlignVCenter, + iconRect.size(), editRect); + if (cb->editable) + painter->fillRect(iconRect, option->palette.brush(QPalette::Base)); + proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); + + if (cb->direction == Qt::RightToLeft) + editRect.translate(-4 - cb->iconSize.width(), 0); + else + editRect.translate(cb->iconSize.width() + 4, 0); + } + if (!cb->currentText.isEmpty() && !cb->editable) { + proxy()->drawItemText(painter, editRect, + visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter), + cb->palette, cb->state & State_Enabled, cb->currentText); + } + painter->restore(); + } + break; +#endif // QT_NO_COMBOBOX #ifndef QT_NO_DOCKWIDGET case CE_DockWidgetTitle: if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { @@ -6154,24 +6187,24 @@ void QWindowsMobileStyle::drawComplexControl(ComplexControl control, const QStyl qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), proxy()->pixelMetric(PM_ComboBoxFrameWidth, option, widget), &editBrush); else painter->fillRect(option->rect, editBrush); - State flags = State_None; - QRect ar = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget); - if ((option->state & State_On)) { - painter->fillRect(ar.adjusted(0, 0, 1, 1),cmb->palette.brush(QPalette::Shadow)); - } - if (d->doubleControls) - ar.adjust(5, 0, 5, 0); - else - ar.adjust(2, 0, -2, 0); - if (option->state & State_Enabled) - flags |= State_Enabled; - if (option->state & State_On) - flags |= State_Sunken; - QStyleOption arrowOpt(0); - arrowOpt.rect = ar; - arrowOpt.palette = cmb->palette; - arrowOpt.state = flags; - proxy()->drawPrimitive(PrimitiveElement(PE_IndicatorArrowDownBig), &arrowOpt, painter, widget); + State flags = State_None; + QRect ar = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget); + if ((option->state & State_On)) { + painter->fillRect(ar.adjusted(0, 0, 1, 1),cmb->palette.brush(QPalette::Shadow)); + } + if (d->doubleControls) + ar.adjust(5, 0, 5, 0); + else + ar.adjust(2, 0, -2, 0); + if (option->state & State_Enabled) + flags |= State_Enabled; + if (option->state & State_On) + flags |= State_Sunken; + QStyleOption arrowOpt(0); + arrowOpt.rect = ar; + arrowOpt.palette = cmb->palette; + arrowOpt.state = flags; + proxy()->drawPrimitive(PrimitiveElement(PE_IndicatorArrowDownBig), &arrowOpt, painter, widget); if (cmb->subControls & SC_ComboBoxEditField) { QRect re = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget); if (cmb->state & State_HasFocus && !cmb->editable) @@ -6335,7 +6368,7 @@ QSize QWindowsMobileStyle::sizeFromContents(ContentsType type, const QStyleOptio case CT_ComboBox: if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { int fw = comboBox->frame ? proxy()->pixelMetric(PM_ComboBoxFrameWidth, option, widget) * 2 : 0; - newSize = QSize(newSize.width() + fw + 9, newSize.height() + fw-4); //Nine is a magic Number - See CommonStyle for real magic (23) + newSize = QSize(newSize.width() + fw + 9, newSize.height() + fw); //Nine is a magic Number - See CommonStyle for real magic (23) } break; #endif @@ -6618,14 +6651,21 @@ QRect QWindowsMobileStyle::subControlRect(ComplexControl control, const QStyleOp switch (subControl) { case SC_ComboBoxArrow: rect.setRect(xpos, y + bmarg, he - 2*bmarg, he - 2*bmarg); - rect.setRect(xpos, y + bmarg, int((he - 2*bmarg)), he - 2*bmarg); break; - case SC_ComboBoxEditField: - rect.setRect(x + margin+4, y + margin+2, wi - 4 * margin - int((he - 2*bmarg) * 0.84f) -2, he - 2 * margin-4); - break; - case SC_ComboBoxFrame: - rect = comboBox->rect; - break; + case SC_ComboBoxEditField: + rect.setRect(x + margin, y + margin, wi - 2 * margin - int((he - 2*bmarg) * 0.84f), he - 2 * margin); + if (d->doubleControls) { + if (comboBox->editable) + rect.adjust(2, 0, 0, 0); + else + rect.adjust(4, 2, 0, -2); + } else if (!comboBox->editable) { + rect.adjust(2, 1, 0, -1); + } + break; + case SC_ComboBoxFrame: + rect = comboBox->rect; + break; default: break; } @@ -6798,34 +6838,11 @@ void QWindowsMobileStyle::polish(QWidget *widget) { else #endif //QT_NO_TOOLBAR -#ifndef QT_NO_PROPERTIES - if (QAbstractButton *pushButton = qobject_cast<QAbstractButton*>(widget)) { - QVariant oldFont = widget->property("_q_styleWindowsMobileFont"); - if (!oldFont.isValid()) { - QFont f = pushButton->font(); - widget->setProperty("_q_styleWindowsMobileFont", f); - f.setBold(true); - int p = f.pointSize(); - if (p > 2) - f.setPointSize(p-1); - pushButton->setFont(f); - } - } -#endif - QWindowsStyle::polish(widget); + QWindowsStyle::polish(widget); } void QWindowsMobileStyle::unpolish(QWidget *widget) { -#ifndef QT_NO_PROPERTIES - if (QAbstractButton *pushButton = qobject_cast<QAbstractButton*>(widget)) { - QVariant oldFont = widget->property("_q_styleWindowsMobileFont"); - if (oldFont.isValid()) { - widget->setFont(qVariantValue<QFont>(oldFont)); - widget->setProperty("_q_styleWindowsMobileFont", QVariant()); - } - } -#endif QWindowsStyle::unpolish(widget); } @@ -6999,7 +7016,7 @@ int QWindowsMobileStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, co } else { d->doubleControls ? ret = 36 : ret = 18; } - break; + break; case PM_ScrollBarExtent: { if (d->smartphone) @@ -7055,7 +7072,7 @@ int QWindowsMobileStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, co break; case PM_TextCursorWidth: ret = 2; - break; + break; case PM_TabBar_ScrollButtonOverlap: ret = 0; break; @@ -7089,7 +7106,7 @@ int QWindowsMobileStyle::styleHint(StyleHint hint, const QStyleOption *opt, cons #endif case SH_ToolBar_Movable: ret = false; - break; + break; case SH_ScrollBar_ContextMenu: ret = false; break; diff --git a/src/gui/symbian/qsymbianevent.cpp b/src/gui/symbian/qsymbianevent.cpp new file mode 100644 index 0000000..af2c861 --- /dev/null +++ b/src/gui/symbian/qsymbianevent.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 "qsymbianevent.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSymbianEvent + \brief The QSymbianEvent class contains a Symbian event of any type. + \since 4.6 + + The class is used as a generic container type for all types of Symbian + events. + + \note This class is only available on Symbian. + + \sa QApplication::symbianEventFilter() +*/ + +/*! + \enum QSymbianEvent::Type + + \value InvalidEvent The event is not valid. + \value WindowServerEvent Indicates an event of type \c TWsEvent. + \value CommandEvent Indicates that the event is a Symbian command. + \value ResourceChangeEvent Indicates that the event is a Symbian resource change type. +*/ + +/*! + \fn QSymbianEvent::type() + + Returns the event type contained in the QSymbianEvent instance. +*/ + +/*! + \fn QSymbianEvent::isValid() + + Returns whether this QSymbianEvent instance contains a valid event. +*/ + +/*! + Constructs a QSymbianEvent containing the given window server event + \a windowServerEvent. +*/ +QSymbianEvent::QSymbianEvent(const TWsEvent *windowServerEvent) + : m_type(WindowServerEvent) + , m_eventPtr(windowServerEvent) +{ +} + +/*! + Constructs a QSymbianEvent containing the given event value + \a value. The type of event is controlled by the \a eventType parameter. +*/ +QSymbianEvent::QSymbianEvent(QSymbianEvent::Type eventType, int value) +{ + switch (eventType) { + case CommandEvent: + case ResourceChangeEvent: + m_type = eventType; + m_eventValue = value; + break; + default: + m_type = InvalidEvent; + m_eventValue = 0; + break; + } +} + +/*! + Destroys the QSymbianEvent. +*/ +QSymbianEvent::~QSymbianEvent() +{ +} + +/*! + Returns the window server event contained in the class instance, or 0 if the event type + is not \c WindowServerEvent. +*/ +const TWsEvent *QSymbianEvent::windowServerEvent() const +{ + return (m_type == WindowServerEvent) ? static_cast<const TWsEvent *>(m_eventPtr) : 0; +} + +/*! + Returns the command contained in the class instance, or 0 if the event type + is not \c CommandEvent. +*/ +int QSymbianEvent::command() const +{ + return (m_type == CommandEvent) ? m_eventValue : 0; +} + +/*! + Returns the resource change type contained in the class instance, or 0 if the event type + is not \c ResourceChangeEvent. +*/ +int QSymbianEvent::resourceChangeType() const +{ + return (m_type == ResourceChangeEvent) ? m_eventValue : 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/symbian/qsymbianevent.h b/src/gui/symbian/qsymbianevent.h new file mode 100644 index 0000000..74aa5d0 --- /dev/null +++ b/src/gui/symbian/qsymbianevent.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 QSYMBIANEVENT_H +#define QSYMBIANEVENT_H + +#include <QtCore/qglobal.h> + +#ifdef Q_OS_SYMBIAN + +class TWsEvent; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QSymbianEvent +{ +public: + enum Type { + InvalidEvent, + WindowServerEvent, + CommandEvent, + ResourceChangeEvent + }; + + QSymbianEvent(const TWsEvent *windowServerEvent); + QSymbianEvent(Type eventType, int value); + ~QSymbianEvent(); + + Type type() const; + bool isValid() const; + + const TWsEvent *windowServerEvent() const; + int command() const; + int resourceChangeType() const; + +private: + Type m_type; + union { + const void *m_eventPtr; + int m_eventValue; + + qint64 m_reserved; + }; +}; + +inline QSymbianEvent::Type QSymbianEvent::type() const +{ + return m_type; +} + +inline bool QSymbianEvent::isValid() const +{ + return m_type != InvalidEvent; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q_OS_SYMBIAN + +#endif // QSYMBIANEVENT_H diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 1285935..1b4c380 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2632,7 +2632,7 @@ QFontCache::~QFontCache() while (it != end) { if (--it.value().data->cache_count == 0) { if (it.value().data->ref == 0) { - FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %d %d %d %d)", + FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %g %d %d %d)", it.value().data, it.key().script, it.key().def.pointSize, it.key().def.pixelSize, it.key().def.weight, it.key().def.style, it.key().def.fixedPitch); diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index d74f0b4..144a82d 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -86,7 +86,7 @@ struct QFontDef #endif // Q_WS_X11 qreal pointSize; - int pixelSize; + qreal pixelSize; uint styleStrategy : 16; uint styleHint : 8; diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 738e36a..fb8444e 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -1331,7 +1331,7 @@ static void match(int script, const QFontDef &request, " family: %s [%s], script: %d\n" " weight: %d, style: %d\n" " stretch: %d\n" - " pixelSize: %d\n" + " pixelSize: %g\n" " pitch: %c", family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(), foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index ae26dab..6cde9ed 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qt_windows.h" +#include <qmath.h> #include <private/qapplication_p.h> #include "qfont_p.h" #include "qfontengine_p.h" @@ -670,7 +671,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ break; } - lf.lfHeight = -request.pixelSize; + lf.lfHeight = -qRound(request.pixelSize); lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; @@ -899,7 +900,6 @@ static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &r return fe; } - void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks @@ -910,8 +910,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // normalize the request to get better caching QFontDef req = d->request; if (req.pixelSize <= 0) - req.pixelSize = qMax(1, qRound(req.pointSize * d->dpi / 72.)); - req.pointSize = 0; + req.pixelSize = qreal((req.pointSize * d->dpi) / 72.); + if (req.pixelSize < 1) + req.pixelSize = 1; if (req.weight == 0) req.weight = QFont::Normal; if (req.stretch == 0) @@ -928,7 +929,8 @@ void QFontDatabase::load(const QFontPrivate *d, int script) QFontEngine *fe = QFontCache::instance()->findEngine(key); // set it to the actual pointsize, so QFontInfo will do the right thing - req.pointSize = req.pixelSize*72./d->dpi; + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72./d->dpi; if (!fe) { if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index 382c4fe..b582e4a 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -51,6 +51,7 @@ #include <qfile.h> #include <qtemporaryfile.h> #include <qabstractfileengine.h> +#include <qmath.h> #include <ctype.h> #include <stdlib.h> @@ -752,12 +753,12 @@ QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request) if (X11->display) dpi = QX11Info::appDpiY(); else - dpi = 96; // #### + dpi = qt_defaultDpiY(); } double size; if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) - fontDef.pixelSize = qRound(size); + fontDef.pixelSize = size; else fontDef.pixelSize = 12; @@ -1455,7 +1456,7 @@ void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontD slant_value = FC_SLANT_OBLIQUE; FcPatternAddInteger(pattern, FC_SLANT, slant_value); - double size_value = qMax(1, request.pixelSize); + double size_value = qMax(1., request.pixelSize); FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); int stretch = request.stretch; @@ -1893,8 +1894,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // normalize the request to get better caching QFontDef req = d->request; if (req.pixelSize <= 0) - req.pixelSize = qRound(qt_pixelSize(req.pointSize, d->dpi)); - req.pointSize = 0; + req.pixelSize = floor(qt_pixelSize(req.pointSize, d->dpi) * 100 + 0.5) / 100; + if (req.pixelSize < 1) + req.pixelSize = 1; if (req.weight == 0) req.weight = QFont::Normal; if (req.stretch == 0) @@ -1909,7 +1911,9 @@ void QFontDatabase::load(const QFontPrivate *d, int script) return; // set it to the actual pointsize, so QFontInfo will do the right thing - req.pointSize = qt_pointSize(req.pixelSize, d->dpi); + if (req.pointSize < 0) + req.pointSize = qt_pointSize(req.pixelSize, d->dpi); + QFontEngine *fe = QFontCache::instance()->findEngine(key); diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 3da1593..4041717 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -327,7 +327,7 @@ void QFreetypeFace::release(const QFontEngine::FaceId &face_id) void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing) { - *ysize = fontDef.pixelSize << 6; + *ysize = qRound(fontDef.pixelSize * 64); *xsize = *ysize * fontDef.stretch / 100; *outline_drawing = false; @@ -387,7 +387,9 @@ QFontEngine::Properties QFreetypeFace::properties() const p.descent = QFixed::fromFixed(-face->size->metrics.descender); p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); p.emSquare = face->size->metrics.y_ppem; - p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); +// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); + p.boundingBox = QRectF(0, -p.ascent.toReal(), + face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); } p.italicAngle = 0; p.capHeight = p.ascent; @@ -709,6 +711,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) hbFace = freetype->hbFace; metrics = face->size->metrics; + #if defined(Q_WS_QWS) /* TrueType fonts with embedded bitmaps may have a bitmap font specific @@ -1219,7 +1222,8 @@ QFixed QFontEngineFT::ascent() const QFixed QFontEngineFT::descent() const { - return QFixed::fromFixed(-metrics.descender); + // subtract a pixel to work around QFontMetrics's built-in + 1 + return QFixed::fromFixed(-metrics.descender - 64); } QFixed QFontEngineFT::leading() const diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index d11083f..8ce437d 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -830,7 +830,6 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); } - Q_ASSERT(*nfo->numGlyphs == item->length - surrogates); #endif for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop) if (item->charAttributes[nextCharStop].charStop) @@ -856,10 +855,13 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos); if (glyphId != 0xffff || i == 0) { - nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24); + if (i < nfo->glyphs->numGlyphs) + { + nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24); - nfo->glyphs->advances_y[i] = yAdvance; - nfo->glyphs->advances_x[i] = xAdvance; + nfo->glyphs->advances_y[i] = yAdvance; + nfo->glyphs->advances_x[i] = xAdvance; + } } else { // ATSUI gives us 0xffff as glyph id at the index in the glyph array for // a character position that maps to a ligtature. Such a glyph id does not @@ -1029,6 +1031,8 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph nfo.flags = flags; nfo.shaperItem = shaperItem; + int prevNumGlyphs = *nglyphs; + QVarLengthArray<int> mappedFonts(len); for (int i = 0; i < len; ++i) mappedFonts[i] = 0; @@ -1140,6 +1144,8 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph } ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning); + if (prevNumGlyphs < *nfo.numGlyphs) + return false; return true; } diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index ef3f2ae..6ff0fbd 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -819,7 +819,7 @@ FT_Face QFontEngineQPF::lockFace() const FT_Face face = freetype->face; // ### not perfect - const int ysize = fontDef.pixelSize << 6; + const int ysize = int(fontDef.pixelSize) << 6; const int xsize = ysize; if (freetype->xsize != xsize || freetype->ysize != ysize) { diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index cc555a3..fd34d0f 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -125,6 +125,7 @@ HDC shared_dc() } #endif +#ifndef Q_WS_WINCE typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); static PtrGetCharWidthI ptrGetCharWidthI = 0; static bool resolvedGetCharWidthI = false; @@ -136,6 +137,7 @@ static void resolveGetCharWidthI() resolvedGetCharWidthI = true; ptrGetCharWidthI = (PtrGetCharWidthI)QLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI"); } +#endif // !defined(Q_WS_WINCE) // defined in qtextengine_win.cpp typedef void *SCRIPT_CACHE; @@ -340,8 +342,10 @@ QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont designAdvances = 0; designAdvancesSize = 0; +#ifndef Q_WS_WINCE if (!resolvedGetCharWidthI) resolveGetCharWidthI(); +#endif } QFontEngineWin::~QFontEngineWin() @@ -381,80 +385,18 @@ bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph if (flags & QTextEngine::GlyphIndicesOnly) return true; -#if defined(Q_WS_WINCE) - HDC hdc = shared_dc(); - if (flags & QTextEngine::DesignMetrics) { - HGDIOBJ oldFont = 0; - int glyph_pos = 0; - for(register int i = 0; i < len; i++) { - bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 - && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); - unsigned int glyph = glyphs->glyphs[glyph_pos]; - if(int(glyph) >= designAdvancesSize) { - int newSize = (glyph + 256) >> 8 << 8; - designAdvances = q_check_ptr((QFixed *)realloc(designAdvances, newSize*sizeof(QFixed))); - for(int i = designAdvancesSize; i < newSize; ++i) - designAdvances[i] = -1000000; - designAdvancesSize = newSize; - } - if(designAdvances[glyph] < -999999) { - if(!oldFont) - oldFont = selectDesignFont(); - SIZE size = {0, 0}; - GetTextExtentPoint32(hdc, (wchar_t *)(str+i), surrogate ? 2 : 1, &size); - designAdvances[glyph] = QFixed((int)size.cx)/designToDevice; - } - glyphs->advances_x[glyph_pos] = designAdvances[glyph]; - glyphs->advances_y[glyph_pos] = 0; - if (surrogate) - ++i; - ++glyph_pos; - } - if(oldFont) - DeleteObject(SelectObject(hdc, oldFont)); - } else { - int glyph_pos = 0; - HGDIOBJ oldFont = 0; - - for(register int i = 0; i < len; i++) { - bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 - && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); - unsigned int glyph = glyphs->glyphs[glyph_pos]; - - glyphs->advances_y[glyph_pos] = 0; - - if (glyph >= widthCacheSize) { - int newSize = (glyph + 256) >> 8 << 8; - widthCache = q_check_ptr((unsigned char *)realloc(widthCache, - newSize*sizeof(QFixed))); - memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); - widthCacheSize = newSize; - } - glyphs->advances_x[glyph_pos] = widthCache[glyph]; - // font-width cache failed - if (glyphs->advances_x[glyph_pos] == 0) { - SIZE size = {0, 0}; - if (!oldFont) - oldFont = SelectObject(hdc, hfont); - GetTextExtentPoint32(hdc, (wchar_t *)str + i, surrogate ? 2 : 1, &size); - glyphs->advances_x[glyph_pos] = size.cx; - // if glyph's within cache range, store it for later - if (size.cx > 0 && size.cx < 0x100) - widthCache[glyph] = size.cx; - } - - if (surrogate) - ++i; - ++glyph_pos; - } + recalcAdvances(glyphs, flags); + return true; +} - if (oldFont) - SelectObject(hdc, oldFont); - } +inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) +{ +#if defined(Q_WS_WINCE) + GetCharWidth32(hdc, glyph, glyph, &width); #else - recalcAdvances(glyphs, flags); + if (ptrGetCharWidthI) + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); #endif - return true; } void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const @@ -477,8 +419,7 @@ void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla oldFont = selectDesignFont(); int width = 0; - if (ptrGetCharWidthI) - ptrGetCharWidthI(hdc, glyph, 1, 0, &width); + calculateTTFGlyphWidth(hdc, glyph, width); designAdvances[glyph] = QFixed(width) / designToDevice; } glyphs->advances_x[i] = designAdvances[glyph]; @@ -517,8 +458,8 @@ void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla SIZE size = {0, 0}; GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size); width = size.cx; - } else if (ptrGetCharWidthI) { - ptrGetCharWidthI(hdc, glyph, 1, 0, &width); + } else { + calculateTTFGlyphWidth(hdc, glyph, width); } glyphs->advances_x[i] = width; // if glyph's within cache range, store it for later @@ -636,7 +577,9 @@ QFixed QFontEngineWin::ascent() const QFixed QFontEngineWin::descent() const { - return tm.tmDescent; + // ### we substract 1 to even out the historical +1 in QFontMetrics's + // ### height=asc+desc+1 equation. Fix in Qt5. + return tm.tmDescent - 1; } QFixed QFontEngineWin::leading() const diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index de83d39..73434b1 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -1437,7 +1437,9 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p option.setTextDirection(dir); layout.setTextOption(option); layout.beginLayout(); - layout.createLine(); + QTextLine line = layout.createLine(); + if (line.isValid()) + line.setLeadingIncluded(true); layout.endLayout(); layout.draw(painter, QPointF(r.left(), pos.y())); break; @@ -2579,6 +2581,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi QTextLine line = tl->createLine(); if (!line.isValid()) break; + line.setLeadingIncluded(true); QFixed left, right; floatMargins(layoutStruct->y, layoutStruct, &left, &right); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 81c9142..a91408f 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1042,7 +1042,7 @@ void QTextEngine::shapeTextWithCE(int item) const QScriptItem &si = layoutData->items[item]; si.glyph_data_offset = layoutData->used; - QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent, &si.leading); QTextEngine::ShaperFlags flags; if (si.analysis.bidiLevel % 2) @@ -1119,7 +1119,7 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const si.glyph_data_offset = layoutData->used; - QFontEngine *font = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading); bool kerningEnabled = this->font(si).d->kerning; @@ -1350,8 +1350,11 @@ void QTextEngine::shape(int item) const layoutData->items[item].position + block.position(), format); } } else if (layoutData->items[item].analysis.flags == QScriptAnalysis::Tab) { - // set up at least the ascent/descent of the script item for the tab - fontEngine(layoutData->items[item], &layoutData->items[item].ascent, &layoutData->items[item].descent); + // set up at least the ascent/descent/leading of the script item for the tab + fontEngine(layoutData->items[item], + &layoutData->items[item].ascent, + &layoutData->items[item].descent, + &layoutData->items[item].leading); } else { shapeText(item); } @@ -1737,7 +1740,7 @@ QFont QTextEngine::font(const QScriptItem &si) const return font; } -QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent) const +QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent, QFixed *leading) const { QFontEngine *engine = 0; QFontEngine *scaledEngine = 0; @@ -1777,6 +1780,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix if (ascent) { *ascent = engine->ascent(); *descent = engine->descent(); + *leading = engine->leading(); } if (scaledEngine) @@ -2009,8 +2013,12 @@ void QScriptLine::setDefaultHeight(QTextEngine *eng) e = eng->fnt.d->engineForScript(QUnicodeTables::Common); } - ascent = qMax(ascent, e->ascent()); - descent = qMax(descent, e->descent()); + QFixed other_ascent = e->ascent(); + QFixed other_descent = e->descent(); + QFixed other_leading = e->leading(); + leading = qMax(leading + ascent, other_leading + other_ascent) - qMax(ascent, other_ascent); + ascent = qMax(ascent, other_ascent); + descent = qMax(descent, other_descent); } QTextEngine::LayoutData::LayoutData() diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp index 4f20094..eeccc72 100644 --- a/src/gui/text/qtextengine_mac.cpp +++ b/src/gui/text/qtextengine_mac.cpp @@ -50,7 +50,6 @@ static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayou { // ### zeroWidth and justification are missing here!!!!! - Q_ASSERT(num_glyphs <= length); Q_UNUSED(num_glyphs); // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs); @@ -558,7 +557,7 @@ void QTextEngine::shapeTextMac(int item) const si.glyph_data_offset = layoutData->used; - QFontEngine *font = fontEngine(si, &si.ascent, &si.descent); + QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading); if (font->type() != QFontEngine::Multi) { shapeTextWithHarfbuzz(item); return; @@ -596,7 +595,7 @@ void QTextEngine::shapeTextMac(int item) const } while (true) { - ensureSpace(num_glyphs); + ensureSpace(num_glyphs); num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; QGlyphLayout g = availableGlyphs(&si); @@ -611,9 +610,9 @@ void QTextEngine::shapeTextMac(int item) const log_clusters, attributes())) { - heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); - break; - } + heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); + break; + } } si.num_glyphs = num_glyphs; diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 85c6928..a1d363b 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -345,11 +345,11 @@ struct Q_AUTOTEST_EXPORT QScriptItem { inline QScriptItem() : position(0), - num_glyphs(0), descent(-1), ascent(-1), width(-1), + num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1), glyph_data_offset(0) {} inline QScriptItem(int p, const QScriptAnalysis &a) : position(p), analysis(a), - num_glyphs(0), descent(-1), ascent(-1), width(-1), + num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1), glyph_data_offset(0) {} int position; @@ -357,6 +357,7 @@ struct Q_AUTOTEST_EXPORT QScriptItem unsigned short num_glyphs; QFixed descent; QFixed ascent; + QFixed leading; QFixed width; int glyph_data_offset; QFixed height() const { return ascent + descent + 1; } @@ -373,9 +374,10 @@ struct Q_AUTOTEST_EXPORT QScriptLine QScriptLine() : from(0), length(0), justified(0), gridfitted(0), - hasTrailingSpaces(0) {} + hasTrailingSpaces(0), leadingIncluded(0) {} QFixed descent; QFixed ascent; + QFixed leading; QFixed x; QFixed y; QFixed width; @@ -385,7 +387,11 @@ struct Q_AUTOTEST_EXPORT QScriptLine mutable uint justified : 1; mutable uint gridfitted : 1; uint hasTrailingSpaces : 1; - QFixed height() const { return ascent + descent + 1; } + uint leadingIncluded : 1; + QFixed height() const { return ascent + descent + 1 + + (leadingIncluded? qMax(QFixed(),leading) : QFixed()); } + QFixed base() const { return ascent + + (leadingIncluded ? qMax(QFixed(),leading) : QFixed()); } void setDefaultHeight(QTextEngine *eng); void operator+=(const QScriptLine &other); }; @@ -394,6 +400,7 @@ Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE); inline void QScriptLine::operator+=(const QScriptLine &other) { + leading= qMax(leading + ascent, other.leading + other.ascent) - qMax(ascent, other.ascent); descent = qMax(descent, other.descent); ascent = qMax(ascent, other.ascent); textWidth += other.textWidth; @@ -476,7 +483,7 @@ public: return end - si->position; } - QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0) const; + QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0, QFixed *leading = 0) const; QFont font(const QScriptItem &si) const; inline QFont font() const { return fnt; } diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index c5f0e35..4600a29 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -860,7 +860,7 @@ QRectF QTextLayout::boundingRect() const ymin = qMin(ymin, si.y); xmax = qMax(xmax, si.x+qMax(si.width, si.textWidth)); // ### shouldn't the ascent be used in ymin??? - ymax = qMax(ymax, si.y+si.ascent+si.descent+1); + ymax = qMax(ymax, si.y+si.height()); } return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal()); } @@ -1071,10 +1071,10 @@ static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPo QTextLineItemIterator iterator(eng, lineNumber, pos, selection); - const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; + + const qreal selectionY = pos.y() + line.y.toReal(); const qreal lineHeight = line.height().toReal(); - const qreal selectionY = (y - line.ascent).toReal(); QFixed lastSelectionX = iterator.x; QFixed lastSelectionWidth; @@ -1334,23 +1334,23 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition const qreal x = position.x() + l.cursorToX(cursorPosition); int itm = d->findItem(cursorPosition - 1); - QFixed ascent = sl.ascent; + QFixed base = sl.base(); QFixed descent = sl.descent; bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft); if (itm >= 0) { const QScriptItem &si = d->layoutData->items.at(itm); if (si.ascent > 0) - ascent = si.ascent; + base = si.ascent; if (si.descent > 0) descent = si.descent; rightToLeft = si.analysis.bidiLevel % 2; } - qreal y = position.y() + (sl.y + sl.ascent - ascent).toReal(); + qreal y = position.y() + (sl.y + sl.base() - base).toReal(); bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) && (p->transform().type() > QTransform::TxTranslate); if (toggleAntialiasing) p->setRenderHint(QPainter::Antialiasing); - p->fillRect(QRectF(x, y, qreal(width), (ascent + descent).toReal()), p->pen().brush()); + p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush()); if (toggleAntialiasing) p->setRenderHint(QPainter::Antialiasing, false); if (d->layoutData->hasBidi) { @@ -1500,9 +1500,11 @@ qreal QTextLine::descent() const } /*! - Returns the line's height. This is equal to ascent() + descent() + 1. + Returns the line's height. This is equal to ascent() + descent() + 1 + if leading is not included. If leading is included, this equals to + ascent() + descent() + leading() + 1. - \sa ascent() descent() + \sa ascent() descent() leading() setLeadingIncluded() */ qreal QTextLine::height() const { @@ -1510,6 +1512,51 @@ qreal QTextLine::height() const } /*! + \since 4.6 + + Returns the line's leading. + + \sa ascent() descent() height() +*/ +qreal QTextLine::leading() const +{ + return eng->lines[i].leading.toReal(); +} + +/*! \since 4.6 + + Includes positive leading into the line's height if \a included is true; + otherwise does not include leading. + + By default, leading is not included. + + Note that negative leading is ignored, it must be handled + in the code using the text lines by letting the lines overlap. + + \sa leadingIncluded() + +*/ +void QTextLine::setLeadingIncluded(bool included) +{ + eng->lines[i].leadingIncluded= included; + +} + +/*! \since 4.6 + + Returns true if positive leading is included into the line's height; otherwise returns false. + + By default, leading is not included. + + \sa setLeadingIncluded() +*/ +bool QTextLine::leadingIncluded() const +{ + return eng->lines[i].leadingIncluded; +} + + +/*! Returns the width of the line that is occupied by text. This is always \<= to width(), and is the minimum width that could be used by layout() without changing the line break position. @@ -1712,6 +1759,9 @@ void QTextLine::layout_helper(int maxGlyphs) } const QScriptItem ¤t = eng->layoutData->items[item]; + lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent, + current.leading + current.ascent) - qMax(lbh.tmpData.ascent, + current.ascent); lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent); lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent); @@ -2042,7 +2092,9 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR QTextLineItemIterator iterator(eng, i, pos, selection); - const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; + QFixed lineBase = line.base(); + + const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase; bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors); while (!iterator.atEnd()) { @@ -2065,7 +2117,7 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR if (selection) format.merge(selection->format); - setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(), + setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), iterator.itemWidth.toReal(), line.height().toReal())); QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); @@ -2086,7 +2138,7 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) { QFixed itemY = y - si.ascent; if (format.verticalAlignment() == QTextCharFormat::AlignTop) { - itemY = y - line.ascent; + itemY = y - lineBase; } QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index 90afac8..9f170f5 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -196,6 +196,10 @@ public: qreal ascent() const; qreal descent() const; qreal height() const; + qreal leading() const; + + void setLeadingIncluded(bool included); + bool leadingIncluded() const; qreal naturalTextWidth() const; QRectF naturalTextRect() const; diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index b28ecd7..b7615a4 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -78,6 +78,7 @@ win32 { unix:x11 { HEADERS += \ text/qfontengine_x11_p.h \ + text/qfontdatabase_x11.cpp \ text/qfontengine_ft_p.h SOURCES += \ text/qfont_x11.cpp \ diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 0e888d6..05e1e74 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -314,7 +314,7 @@ QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const // height - sh.setHeight(qMax(fm.lineSpacing(), 14) + 2); + sh.setHeight(qMax(fm.height(), 14) + 2); if (hasIcon) { sh.setHeight(qMax(sh.height(), iconSize.height() + 2)); } diff --git a/src/gui/widgets/qdialogbuttonbox.cpp b/src/gui/widgets/qdialogbuttonbox.cpp index 10f8db8..2231b98 100644 --- a/src/gui/widgets/qdialogbuttonbox.cpp +++ b/src/gui/widgets/qdialogbuttonbox.cpp @@ -873,6 +873,11 @@ void QDialogButtonBox::setOrientation(Qt::Orientation orientation) void QDialogButtonBox::clear() { Q_D(QDialogButtonBox); +#ifdef QT_SOFTKEYS_ENABLED + // Delete softkey actions as they have the buttons as parents + qDeleteAll(d->softKeyActions.values()); + d->softKeyActions.clear(); +#endif // Remove the created standard buttons, they should be in the other lists, which will // do the deletion d->standardButtonHash.clear(); @@ -1025,6 +1030,11 @@ QPushButton *QDialogButtonBox::addButton(StandardButton button) void QDialogButtonBox::setStandardButtons(StandardButtons buttons) { Q_D(QDialogButtonBox); +#ifdef QT_SOFTKEYS_ENABLED + // Delete softkey actions since they have the buttons as parents + qDeleteAll(d->softKeyActions.values()); + d->softKeyActions.clear(); +#endif // Clear out all the old standard buttons, then recreate them. qDeleteAll(d->standardButtonHash.keys()); d->standardButtonHash.clear(); diff --git a/src/gui/widgets/qdockwidget.cpp b/src/gui/widgets/qdockwidget.cpp index a574262f..6710275 100644 --- a/src/gui/widgets/qdockwidget.cpp +++ b/src/gui/widgets/qdockwidget.cpp @@ -456,7 +456,7 @@ int QDockWidgetLayout::titleHeight() const int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); - return qMax(buttonHeight + 2, titleFontMetrics.lineSpacing() + 2*mw); + return qMax(buttonHeight + 2, titleFontMetrics.height() + 2*mw); } void QDockWidgetLayout::setGeometry(const QRect &geometry) diff --git a/src/gui/widgets/qfontcombobox.cpp b/src/gui/widgets/qfontcombobox.cpp index 7b39823..806db59 100644 --- a/src/gui/widgets/qfontcombobox.cpp +++ b/src/gui/widgets/qfontcombobox.cpp @@ -194,7 +194,7 @@ QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option, // font.setFamily(text); font.setPointSize(QFontInfo(font).pointSize() * 3/2); QFontMetrics fontMetrics(font); - return QSize(fontMetrics.width(text), fontMetrics.lineSpacing()); + return QSize(fontMetrics.width(text), fontMetrics.height()); } diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp index 629e839..e4252b5 100644 --- a/src/gui/widgets/qlineedit.cpp +++ b/src/gui/widgets/qlineedit.cpp @@ -624,7 +624,7 @@ QSize QLineEdit::sizeHint() const Q_D(const QLineEdit); ensurePolished(); QFontMetrics fm(font()); - int h = qMax(fm.lineSpacing(), 14) + 2*d->verticalMargin + int h = qMax(fm.height(), 14) + 2*d->verticalMargin + d->topTextMargin + d->bottomTextMargin + d->topmargin + d->bottommargin; int w = fm.width(QLatin1Char('x')) * 17 + 2*d->horizontalMargin diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 687e1bc..ea25901 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -180,6 +180,21 @@ int QMenuPrivate::scrollerHeight() const } //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't +QRect QMenuPrivate::popupGeometry(const QWidget *widget) const +{ +#ifdef Q_WS_WIN + return QApplication::desktop()->screenGeometry(widget); +#elif defined Q_WS_X11 + if (X11->desktopEnvironment == DE_KDE) + return QApplication::desktop()->screenGeometry(widget); + else + return QApplication::desktop()->availableGeometry(widget); +#else + return QApplication::desktop()->availableGeometry(widget); +#endif +} + +//Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't QRect QMenuPrivate::popupGeometry(int screen) const { #ifdef Q_WS_WIN @@ -234,7 +249,7 @@ void QMenuPrivate::updateActionRects() const } int max_column_width = 0, - dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(), + dh = popupGeometry(q).height(), y = 0; QStyle *style = q->style(); QStyleOption opt; @@ -744,7 +759,7 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc if (newScrollFlags & QMenuScroller::ScrollUp) newOffset -= vmargin; - QRect screen = popupGeometry(QApplication::desktop()->screenNumber(q)); + QRect screen = popupGeometry(q); const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q); if (q->height() < screen.height()-(desktopFrame*2)-1) { QRect geom = q->geometry(); @@ -960,10 +975,19 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) return false; } +class ExceptionGuard +{ +public: + inline ExceptionGuard(bool *w = 0) : watched(w) { Q_ASSERT(!(*watched)); *watched = true; } + inline ~ExceptionGuard() { *watched = false; } + inline operator bool() { return *watched; } +private: + bool *watched; +}; + void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self) { - Q_ASSERT(!activationRecursionGuard); - activationRecursionGuard = true; + ExceptionGuard guard(&activationRecursionGuard); #ifdef QT3_SUPPORT const int actionId = q_func()->findIdForAction(action); #endif @@ -1008,7 +1032,6 @@ void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedSt #endif } } - activationRecursionGuard = false; } void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self) @@ -1789,7 +1812,15 @@ void QMenu::popup(const QPoint &p, QAction *atAction) d->updateActionRects(); QPoint pos = p; QSize size = sizeHint(); - QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); + QRect screen; +#ifndef QT_NO_GRAPHICSVIEW + bool isEmbedded = d->nearestGraphicsProxyWidget(this); + if (isEmbedded) + screen = d->popupGeometry(this); + else +#endif + screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); + const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this); bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen); #ifdef QT_KEYPAD_NAVIGATION @@ -2927,7 +2958,7 @@ void QMenu::internalDelayedPopup() QPoint pos(rightPos); QMenu *caused = qobject_cast<QMenu*>(d->activeMenu->d_func()->causedPopup.widget); - const QRect availGeometry(d->popupGeometry(QApplication::desktop()->screenNumber(caused))); + const QRect availGeometry(d->popupGeometry(caused)); if (isRightToLeft()) { pos = leftPos; if ((caused && caused->x() < x()) || pos.x() < availGeometry.left()) { diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm index cee38ee..b238faf 100644 --- a/src/gui/widgets/qmenu_mac.mm +++ b/src/gui/widgets/qmenu_mac.mm @@ -1771,6 +1771,16 @@ QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action) actionItems.removeAll(action); } +bool QMenuBarPrivate::macWidgetHasNativeMenubar(QWidget *widget) +{ + // This function is different from q->isNativeMenuBar(), as + // it returns true only if a native menu bar is actually + // _created_. + if (!widget) + return false; + return menubars()->contains(widget->window()); +} + void QMenuBarPrivate::macCreateMenuBar(QWidget *parent) { @@ -1778,16 +1788,22 @@ QMenuBarPrivate::macCreateMenuBar(QWidget *parent) static int dontUseNativeMenuBar = -1; // We call the isNativeMenuBar function here // because that will make sure that local overrides - // are dealt with correctly. + // are dealt with correctly. q->isNativeMenuBar() will, if not + // overridden, depend on the attribute Qt::AA_DontUseNativeMenuBar: bool qt_mac_no_native_menubar = !q->isNativeMenuBar(); if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) { + // The menubar is set to be native. Let's check (one time only + // for all menubars) if this is OK with the rest of the environment. + // As a result, Qt::AA_DontUseNativeMenuBar is set. NB: the application + // might still choose to not respect, or change, this flag. bool isPlugin = QApplication::testAttribute(Qt::AA_MacPluginApplication); bool environmentSaysNo = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty(); dontUseNativeMenuBar = isPlugin || environmentSaysNo; QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar); qt_mac_no_native_menubar = !q->isNativeMenuBar(); } - if (!qt_mac_no_native_menubar) { + if (qt_mac_no_native_menubar == false) { + // INVARIANT: Use native menubar. extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp qt_event_request_menubarupdate(); if (!parent && !fallback) { diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h index 9c4f260..9348f7b 100644 --- a/src/gui/widgets/qmenu_p.h +++ b/src/gui/widgets/qmenu_p.h @@ -192,11 +192,12 @@ public: mutable QVector<QRect> actionRects; mutable QWidgetList widgetItems; void updateActionRects() const; - QRect popupGeometry(int screen=-1) const; + QRect popupGeometry(const QWidget *widget) const; + QRect popupGeometry(int screen = -1) const; mutable uint ncols : 4; //4 bits is probably plenty uint collapsibleSeparators : 1; - uint activationRecursionGuard : 1; + bool activationRecursionGuard; //selection static QPointer<QMenu> mouseDown; diff --git a/src/gui/widgets/qmenu_symbian.cpp b/src/gui/widgets/qmenu_symbian.cpp index d757f98..94c4177 100644 --- a/src/gui/widgets/qmenu_symbian.cpp +++ b/src/gui/widgets/qmenu_symbian.cpp @@ -243,11 +243,14 @@ void qt_symbian_show_submenu( CEikMenuPane* menuPane, int id) } #endif // Q_WS_S60 -void QMenuBarPrivate::symbianCommands(int command) +int QMenuBarPrivate::symbianCommands(int command) { + int ret = 0; + if (command == contexMenuCommand && !widgetWithContextMenu.isNull()) { QContextMenuEvent* event = new QContextMenuEvent(QContextMenuEvent::Keyboard, QPoint(0,0)); QCoreApplication::postEvent(widgetWithContextMenu, event); + ret = 1; } int size = nativeMenuBars.size(); @@ -258,8 +261,11 @@ void QMenuBarPrivate::symbianCommands(int command) emit nativeMenuBars.at(i)->triggered(menu->action); menu->action->activate(QAction::Trigger); + ret = 1; break; } + + return ret; } void QMenuBarPrivate::symbianCreateMenuBar(QWidget *parent) diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index 13e7de4..f2f0722 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -1370,8 +1370,13 @@ void QMenuBarPrivate::handleReparent() oldWindow = newWindow; #ifdef Q_WS_MAC - macDestroyMenuBar(); - macCreateMenuBar(newParent); + if (q->isNativeMenuBar() && !macWidgetHasNativeMenubar(newParent)) { + // If the new parent got a native menubar from before, keep that + // menubar rather than replace it with this one (because a parents + // menubar has precedence over children menubars). + macDestroyMenuBar(); + macCreateMenuBar(newParent); + } #endif #ifdef Q_WS_WINCE diff --git a/src/gui/widgets/qmenubar_p.h b/src/gui/widgets/qmenubar_p.h index 0b27b97..da2b8d7 100644 --- a/src/gui/widgets/qmenubar_p.h +++ b/src/gui/widgets/qmenubar_p.h @@ -196,6 +196,7 @@ public: return 0; } } *mac_menubar; + bool macWidgetHasNativeMenubar(QWidget *widget); void macCreateMenuBar(QWidget *); void macDestroyMenuBar(); OSMenuRef macMenu(); @@ -265,7 +266,7 @@ public: void insertNativeMenuItems(const QList<QAction*> &actions); } *symbian_menubar; - static void symbianCommands(int command); + static int symbianCommands(int command); #endif }; diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index 22438bf..fc61889 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -357,10 +357,8 @@ void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) Q_D(QPlainTextDocumentLayout); QTextDocument *doc = document(); qreal margin = doc->documentMargin(); - QFontMetrics fm(doc->defaultFont()); qreal blockMaximumWidth = 0; - int leading = qMax(0, fm.leading()); qreal height = 0; QTextLayout *tl = block.layout(); QTextOption option = doc->defaultTextOption(); @@ -381,9 +379,8 @@ void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) QTextLine line = tl->createLine(); if (!line.isValid()) break; + line.setLeadingIncluded(true); line.setLineWidth(availableWidth); - - height += leading; line.setPosition(QPointF(margin, height)); height += line.height(); blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin); diff --git a/src/gui/widgets/qpushbutton.cpp b/src/gui/widgets/qpushbutton.cpp index 1352e1b..eb34336 100644 --- a/src/gui/widgets/qpushbutton.cpp +++ b/src/gui/widgets/qpushbutton.cpp @@ -590,7 +590,7 @@ void QPushButtonPrivate::_q_popupPressed() int x = globalPos.x(); int y = globalPos.y(); if (horizontal) { - if (globalPos.y() + rect.height() + menuSize.height() <= QApplication::desktop()->height()) { + if (globalPos.y() + rect.height() + menuSize.height() <= QApplication::desktop()->availableGeometry(q).height()) { y += rect.height(); } else { y -= menuSize.height(); @@ -598,7 +598,7 @@ void QPushButtonPrivate::_q_popupPressed() if (q->layoutDirection() == Qt::RightToLeft) x += rect.width() - menuSize.width(); } else { - if (globalPos.x() + rect.width() + menu->sizeHint().width() <= QApplication::desktop()->width()) + if (globalPos.x() + rect.width() + menu->sizeHint().width() <= QApplication::desktop()->availableGeometry(q).width()) x += rect.width(); else x -= menuSize.width(); diff --git a/src/gui/widgets/qtextedit.cpp b/src/gui/widgets/qtextedit.cpp index b894aa8..f477fee 100644 --- a/src/gui/widgets/qtextedit.cpp +++ b/src/gui/widgets/qtextedit.cpp @@ -2079,8 +2079,8 @@ void QTextEdit::setReadOnly(bool ro) } else { flags = Qt::TextEditorInteraction; } - setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); d->control->setTextInteractionFlags(flags); + setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); } /*! |