From 9f1aa866bda7678261f2f441d4cfd5bb524c2411 Mon Sep 17 00:00:00 2001 From: Jo Asplin Date: Thu, 20 Oct 2011 13:17:26 +0200 Subject: Moved tests into integrationtests/ and widgets/ Task-number: QTBUG-19013 Change-Id: Ibb776f5967c0645ce6d22ef7afdc40657c575461 Reviewed-by: Holger Ihrig --- .../qgraphicsitem/tst_qgraphicsitem.cpp | 11402 +++++++++++++++++++ 1 file changed, 11402 insertions(+) create mode 100644 tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp (limited to 'tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp') diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp new file mode 100644 index 0000000000..8ac1f6b5c5 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp @@ -0,0 +1,11402 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QPainterPath) +Q_DECLARE_METATYPE(QPointF) +Q_DECLARE_METATYPE(QRectF) + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +#include +#define Q_CHECK_PAINTEVENTS \ + if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ + QSKIP("The Graphics View doesn't get the paint events", SkipSingle); +#else +#define Q_CHECK_PAINTEVENTS +#endif + +#if defined(Q_WS_MAC) +// On mac (cocoa) we always get full update. +// So check that the expected region is contained inside the actual +#define COMPARE_REGIONS(ACTUAL, EXPECTED) QVERIFY((EXPECTED).subtracted(ACTUAL).isEmpty()) +#else +#define COMPARE_REGIONS QTRY_COMPARE +#endif + +#include "../../../platformquirks.h" + +static QGraphicsRectItem staticItem; //QTBUG-7629, we should not crash at exit. + +static void sendMousePress(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton) +{ + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(point); + event.setButton(button); + event.setButtons(button); + QApplication::sendEvent(scene, &event); +} + +static void sendMouseMove(QGraphicsScene *scene, const QPointF &point, + Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons /* buttons */ = 0) +{ + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(point); + event.setButton(button); + event.setButtons(button); + QApplication::sendEvent(scene, &event); +} + +static void sendMouseRelease(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton) +{ + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(point); + event.setButton(button); + QApplication::sendEvent(scene, &event); +} + +static void sendMouseClick(QGraphicsScene *scene, const QPointF &point, Qt::MouseButton button = Qt::LeftButton) +{ + sendMousePress(scene, point, button); + sendMouseRelease(scene, point, button); +} + +static void sendKeyPress(QGraphicsScene *scene, Qt::Key key) +{ + QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier); + QApplication::sendEvent(scene, &keyEvent); +} + +static void sendKeyRelease(QGraphicsScene *scene, Qt::Key key) +{ + QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier); + QApplication::sendEvent(scene, &keyEvent); +} + +static void sendKeyClick(QGraphicsScene *scene, Qt::Key key) +{ + sendKeyPress(scene, key); + sendKeyRelease(scene, key); +} + +class EventSpy : public QGraphicsWidget +{ + Q_OBJECT +public: + EventSpy(QObject *watched, QEvent::Type type) + : _count(0), spied(type) + { + watched->installEventFilter(this); + } + + EventSpy(QGraphicsScene *scene, QGraphicsItem *watched, QEvent::Type type) + : _count(0), spied(type) + { + scene->addItem(this); + watched->installSceneEventFilter(this); + } + + int count() const { return _count; } + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + if (event->type() == spied) + ++_count; + return false; + } + + int _count; + QEvent::Type spied; +}; + +class EventSpy2 : public QGraphicsWidget +{ + Q_OBJECT +public: + EventSpy2(QObject *watched) + { + watched->installEventFilter(this); + } + + EventSpy2(QGraphicsScene *scene, QGraphicsItem *watched) + { + scene->addItem(this); + watched->installSceneEventFilter(this); + } + + QMap counts; + +protected: + bool eventFilter(QObject *watched, QEvent *event) + { + Q_UNUSED(watched); + ++counts[event->type()]; + return false; + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + Q_UNUSED(watched); + ++counts[event->type()]; + return false; + } +}; + +class EventTester : public QGraphicsItem +{ +public: + EventTester(QGraphicsItem *parent = 0) : QGraphicsItem(parent), repaints(0) + { br = QRectF(-10, -10, 20, 20); } + + void setGeometry(const QRectF &rect) + { + prepareGeometryChange(); + br = rect; + update(); + } + + QRectF boundingRect() const + { return br; } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *o, QWidget *) + { + hints = painter->renderHints(); + painter->setBrush(brush); + painter->drawRect(boundingRect()); + lastExposedRect = o->exposedRect; + ++repaints; + } + + bool sceneEvent(QEvent *event) + { + events << event->type(); + return QGraphicsItem::sceneEvent(event); + } + + void reset() + { + events.clear(); + hints = QPainter::RenderHints(0); + repaints = 0; + lastExposedRect = QRectF(); + } + + QList events; + QPainter::RenderHints hints; + int repaints; + QRectF br; + QRectF lastExposedRect; + QBrush brush; +}; + +class MyGraphicsView : public QGraphicsView +{ +public: + int repaints; + QRegion paintedRegion; + MyGraphicsView(QGraphicsScene *scene, QWidget *parent=0) : QGraphicsView(scene,parent), repaints(0) {} + void paintEvent(QPaintEvent *e) + { + paintedRegion += e->region(); + ++repaints; + QGraphicsView::paintEvent(e); + } + void reset() { repaints = 0; paintedRegion = QRegion(); } +}; + +class tst_QGraphicsItem : public QObject +{ + Q_OBJECT + +public slots: + void init(); + +private slots: + void construction(); + void constructionWithParent(); + void destruction(); + void deleteChildItem(); + void scene(); + void parentItem(); + void setParentItem(); + void children(); + void flags(); + void inputMethodHints(); + void toolTip(); + void visible(); + void isVisibleTo(); + void explicitlyVisible(); + void enabled(); + void explicitlyEnabled(); + void selected(); + void selected2(); + void selected_group(); + void selected_textItem(); + void selected_multi(); + void acceptedMouseButtons(); + void acceptsHoverEvents(); + void childAcceptsHoverEvents(); + void hasFocus(); + void pos(); + void scenePos(); + void matrix(); + void sceneMatrix(); + void setMatrix(); + void zValue(); + void shape(); + void contains(); + void collidesWith_item(); + void collidesWith_path_data(); + void collidesWith_path(); + void collidesWithItemWithClip(); + void isObscuredBy(); + void isObscured(); + void mapFromToParent(); + void mapFromToScene(); + void mapFromToItem(); + void mapRectFromToParent_data(); + void mapRectFromToParent(); + void isAncestorOf(); + void commonAncestorItem(); + void data(); + void type(); + void graphicsitem_cast(); + void hoverEventsGenerateRepaints(); + void boundingRects_data(); + void boundingRects(); + void boundingRects2(); + void sceneBoundingRect(); + void childrenBoundingRect(); + void childrenBoundingRectTransformed(); + void childrenBoundingRect2(); + void childrenBoundingRect3(); + void childrenBoundingRect4(); + void childrenBoundingRect5(); + void group(); + void setGroup(); + void setGroup2(); + void nestedGroups(); + void warpChildrenIntoGroup(); + void removeFromGroup(); + void handlesChildEvents(); + void handlesChildEvents2(); + void handlesChildEvents3(); + void filtersChildEvents(); + void filtersChildEvents2(); + void ensureVisible(); + void cursor(); + //void textControlGetterSetter(); + void defaultItemTest_QGraphicsLineItem(); + void defaultItemTest_QGraphicsPixmapItem(); + void defaultItemTest_QGraphicsTextItem(); + void defaultItemTest_QGraphicsEllipseItem(); + void itemChange(); + void sceneEventFilter(); + void prepareGeometryChange(); + void paint(); + void deleteItemInEventHandlers(); + void itemClipsToShape(); + void itemClipsChildrenToShape(); + void itemClipsChildrenToShape2(); + void itemClipsChildrenToShape3(); + void itemClipsChildrenToShape4(); + void itemClipsChildrenToShape5(); + void itemClipsTextChildToShape(); + void itemClippingDiscovery(); + void ancestorFlags(); + void untransformable(); + void contextMenuEventPropagation(); + void itemIsMovable(); + void boundingRegion_data(); + void boundingRegion(); + void itemTransform_parentChild(); + void itemTransform_siblings(); + void itemTransform_unrelated(); + void opacity_data(); + void opacity(); + void opacity2(); + void opacityZeroUpdates(); + void itemStacksBehindParent(); + void nestedClipping(); + void nestedClippingTransforms(); + void sceneTransformCache(); + void tabChangesFocus(); + void tabChangesFocus_data(); + void cacheMode(); + void cacheMode2(); + void updateCachedItemAfterMove(); + void deviceTransform_data(); + void deviceTransform(); + void update(); + void setTransformProperties_data(); + void setTransformProperties(); + void itemUsesExtendedStyleOption(); + void itemSendsGeometryChanges(); + void moveItem(); + void moveLineItem(); + void sorting_data(); + void sorting(); + void itemHasNoContents(); + void hitTestUntransformableItem(); + void hitTestGraphicsEffectItem(); + void focusProxy(); + void subFocus(); + void focusProxyDeletion(); + void negativeZStacksBehindParent(); + void setGraphicsEffect(); + void panel(); + void addPanelToActiveScene(); + void panelWithFocusItem(); + void activate(); + void setActivePanelOnInactiveScene(); + void activationOnShowHide(); + void moveWhileDeleting(); + void ensureDirtySceneTransform(); + void focusScope(); + void focusScope2(); + void stackBefore(); + void sceneModality(); + void panelModality(); + void mixedModality(); + void modality_hover(); + void modality_mouseGrabber(); + void modality_clickFocus(); + void modality_keyEvents(); + void itemIsInFront(); + void scenePosChange(); + void updateMicroFocus(); + void textItem_shortcuts(); + void scroll(); + void focusHandling_data(); + void focusHandling(); + void touchEventPropagation_data(); + void touchEventPropagation(); + void deviceCoordinateCache_simpleRotations(); + + // task specific tests below me + void task141694_textItemEnsureVisible(); + void task128696_textItemEnsureMovable(); + void ensureUpdateOnTextItem(); + void task177918_lineItemUndetected(); + void task240400_clickOnTextItem_data(); + void task240400_clickOnTextItem(); + void task243707_addChildBeforeParent(); + void task197802_childrenVisibility(); + void QTBUG_4233_updateCachedWithSceneRect(); + void QTBUG_5418_textItemSetDefaultColor(); + void QTBUG_6738_missingUpdateWithSetParent(); + void QTBUG_7714_fullUpdateDiscardingOpacityUpdate2(); + void QT_2653_fullUpdateDiscardingOpacityUpdate(); + void QT_2649_focusScope(); + void sortItemsWhileAdding(); + void doNotMarkFullUpdateIfNotInScene(); + void itemDiesDuringDraggingOperation(); + void QTBUG_12112_focusItem(); + void QTBUG_13473_sceneposchange(); + void QTBUG_16374_crashInDestructor(); + void QTBUG_20699_focusScopeCrash(); + +private: + QList paintedItems; +}; + +void tst_QGraphicsItem::init() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QGraphicsItem::construction() +{ + for (int i = 0; i < 7; ++i) { + QGraphicsItem *item; + switch (i) { + case 0: + item = new QGraphicsEllipseItem; + QCOMPARE(int(item->type()), int(QGraphicsEllipseItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsEllipseItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 1: + item = new QGraphicsLineItem; + QCOMPARE(int(item->type()), int(QGraphicsLineItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsLineItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 2: + item = new QGraphicsPathItem; + QCOMPARE(int(item->type()), int(QGraphicsPathItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsPathItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 3: + item = new QGraphicsPixmapItem; + QCOMPARE(int(item->type()), int(QGraphicsPixmapItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsPixmapItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 4: + item = new QGraphicsPolygonItem; + QCOMPARE(int(item->type()), int(QGraphicsPolygonItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsPolygonItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 5: + item = new QGraphicsRectItem; + QCOMPARE(int(item->type()), int(QGraphicsRectItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsLineItem *)0); + QCOMPARE(item->flags(), 0); + break; + case 6: + item = new QGraphicsTextItem; + QCOMPARE(int(item->type()), int(QGraphicsTextItem::Type)); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsTextItem *)item); + QCOMPARE(qgraphicsitem_cast(item), (QGraphicsRectItem *)0); + // This is the only item that uses an extended style option. + QCOMPARE(item->flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + break; + default: + qFatal("You broke the logic, please fix!"); + break; + } + + QCOMPARE(item->scene(), (QGraphicsScene *)0); + QCOMPARE(item->parentItem(), (QGraphicsItem *)0); + QVERIFY(item->children().isEmpty()); + QVERIFY(item->isVisible()); + QVERIFY(item->isEnabled()); + QVERIFY(!item->isSelected()); + QCOMPARE(item->acceptedMouseButtons(), Qt::MouseButtons(0x1f)); + if (item->type() == QGraphicsTextItem::Type) + QVERIFY(item->acceptsHoverEvents()); + else + QVERIFY(!item->acceptsHoverEvents()); + QVERIFY(!item->hasFocus()); + QCOMPARE(item->pos(), QPointF()); + QCOMPARE(item->matrix(), QMatrix()); + QCOMPARE(item->sceneMatrix(), QMatrix()); + QCOMPARE(item->zValue(), qreal(0)); + QCOMPARE(item->sceneBoundingRect(), QRectF()); + QCOMPARE(item->shape(), QPainterPath()); + QVERIFY(!item->contains(QPointF(0, 0))); + QVERIFY(!item->collidesWithItem(0)); + QVERIFY(item->collidesWithItem(item)); + QVERIFY(!item->collidesWithPath(QPainterPath())); + QVERIFY(!item->isAncestorOf(0)); + QVERIFY(!item->isAncestorOf(item)); + QCOMPARE(item->data(0), QVariant()); + delete item; + } +} + +class BoundingRectItem : public QGraphicsRectItem +{ +public: + BoundingRectItem(QGraphicsItem *parent = 0) + : QGraphicsRectItem(0, 0, parent ? 200 : 100, parent ? 200 : 100, + parent) + {} + + QRectF boundingRect() const + { + QRectF tmp = QGraphicsRectItem::boundingRect(); + foreach (QGraphicsItem *child, children()) + tmp |= child->boundingRect(); // <- might be pure virtual + return tmp; + } +}; + +void tst_QGraphicsItem::constructionWithParent() +{ + // This test causes a crash if item1 calls item2's pure virtuals before the + // object has been constructed. + QGraphicsItem *item0 = new BoundingRectItem; + QGraphicsItem *item1 = new BoundingRectItem; + QGraphicsScene scene; + scene.addItem(item0); + scene.addItem(item1); + QGraphicsItem *item2 = new BoundingRectItem(item1); + QCOMPARE(item1->children(), QList() << item2); + QCOMPARE(item1->boundingRect(), QRectF(0, 0, 200, 200)); + + item2->setParentItem(item0); + QCOMPARE(item0->children(), QList() << item2); + QCOMPARE(item0->boundingRect(), QRectF(0, 0, 200, 200)); +} + +static int itemDeleted = 0; +class Item : public QGraphicsRectItem +{ +public: + ~Item() + { ++itemDeleted; } +}; + +void tst_QGraphicsItem::destruction() +{ + QCOMPARE(itemDeleted, 0); + { + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + QCOMPARE(child->parentItem(), parent); + delete parent; + QCOMPARE(itemDeleted, 1); + } + { + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + QCOMPARE(parent->children().size(), 1); + delete child; + QCOMPARE(parent->children().size(), 0); + delete parent; + QCOMPARE(itemDeleted, 2); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + QCOMPARE(child->parentItem(), (QGraphicsItem *)0); + child->setParentItem(parent); + QCOMPARE(child->parentItem(), parent); + scene.addItem(parent); + QCOMPARE(child->parentItem(), parent); + delete parent; + QCOMPARE(itemDeleted, 3); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + QCOMPARE(parent->children().size(), 1); + delete child; + QCOMPARE(parent->children().size(), 0); + delete parent; + QCOMPARE(itemDeleted, 4); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + scene.removeItem(parent); + QCOMPARE(child->scene(), (QGraphicsScene *)0); + delete parent; + QCOMPARE(itemDeleted, 5); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + QCOMPARE(child->scene(), (QGraphicsScene *)0); + QCOMPARE(parent->scene(), (QGraphicsScene *)0); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + scene.removeItem(child); + QCOMPARE(child->scene(), (QGraphicsScene *)0); + QCOMPARE(parent->scene(), &scene); + QCOMPARE(child->parentItem(), (QGraphicsItem *)0); + QVERIFY(parent->children().isEmpty()); + delete parent; + QCOMPARE(itemDeleted, 5); + delete child; + QCOMPARE(itemDeleted, 6); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + scene.removeItem(child); + scene.removeItem(parent); + delete child; + delete parent; + QCOMPARE(itemDeleted, 7); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + QGraphicsScene scene2; + scene2.addItem(parent); + delete parent; + QCOMPARE(itemDeleted, 8); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + QGraphicsScene scene2; + scene2.addItem(parent); + QCOMPARE(child->scene(), &scene2); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + scene2.addItem(parent); + QCOMPARE(child->scene(), &scene2); + delete parent; + QCOMPARE(itemDeleted, 9); + } + { + QGraphicsScene scene; + QGraphicsItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + scene.addItem(parent); + QCOMPARE(child->scene(), &scene); + QGraphicsScene scene2; + scene2.addItem(child); + QCOMPARE(child->scene(), &scene2); + delete parent; + QCOMPARE(itemDeleted, 9); + delete child; + QCOMPARE(itemDeleted, 10); + } + { + QGraphicsScene scene; + QGraphicsItem *root = new QGraphicsRectItem; + QGraphicsItem *parent = root; + QGraphicsItem *middleItem = 0; + for (int i = 0; i < 99; ++i) { + Item *child = new Item; + child->setParentItem(parent); + parent = child; + if (i == 50) + middleItem = parent; + } + scene.addItem(root); + + QCOMPARE(scene.items().size(), 100); + + QGraphicsScene scene2; + scene2.addItem(middleItem); + + delete middleItem; + QCOMPARE(itemDeleted, 59); + } + QCOMPARE(itemDeleted, 109); + { + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsRectItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + parent->setVisible(false); + scene->addItem(parent); + QCOMPARE(child->parentItem(), static_cast(parent)); + delete scene; + QCOMPARE(itemDeleted, 110); + } +} + +void tst_QGraphicsItem::deleteChildItem() +{ + QGraphicsScene scene; + QGraphicsItem *rect = scene.addRect(QRectF()); + QGraphicsItem *child1 = new QGraphicsRectItem(rect); + QGraphicsItem *child2 = new QGraphicsRectItem(rect); + QGraphicsItem *child3 = new QGraphicsRectItem(rect); + Q_UNUSED(child3); + delete child1; + child2->setParentItem(0); + delete child2; +} + +void tst_QGraphicsItem::scene() +{ + QGraphicsRectItem *item = new QGraphicsRectItem; + QCOMPARE(item->scene(), (QGraphicsScene *)0); + + QGraphicsScene scene; + scene.addItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)&scene); + + QGraphicsScene scene2; + scene2.addItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)&scene2); + + scene2.removeItem(item); + QCOMPARE(item->scene(), (QGraphicsScene *)0); + + delete item; +} + +void tst_QGraphicsItem::parentItem() +{ + QGraphicsRectItem item; + QCOMPARE(item.parentItem(), (QGraphicsItem *)0); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)&item); + item2->setParentItem(&item); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)&item); + item2->setParentItem(0); + QCOMPARE(item2->parentItem(), (QGraphicsItem *)0); + + delete item2; +} + +void tst_QGraphicsItem::setParentItem() +{ + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 10, 10)); + QCOMPARE(item->scene(), &scene); + + QGraphicsRectItem *child = new QGraphicsRectItem; + QCOMPARE(child->scene(), (QGraphicsScene *)0); + + // This implicitly adds the item to the parent's scene + child->setParentItem(item); + QCOMPARE(child->scene(), &scene); + + // This just makes it a toplevel + child->setParentItem(0); + QCOMPARE(child->scene(), &scene); + + // Add the child back to the parent, then remove the parent from the scene + child->setParentItem(item); + scene.removeItem(item); + QCOMPARE(child->scene(), (QGraphicsScene *)0); +} + +void tst_QGraphicsItem::children() +{ + QGraphicsRectItem item; + QVERIFY(item.children().isEmpty()); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(), &item); + QCOMPARE(item.children().size(), 1); + QCOMPARE(item.children().first(), (QGraphicsItem *)item2); + QVERIFY(item2->children().isEmpty()); + + delete item2; + QVERIFY(item.children().isEmpty()); +} + +void tst_QGraphicsItem::flags() +{ + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20)); + QCOMPARE(item->flags(), 0); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(item); + + { + // Focus + item->setFlag(QGraphicsItem::ItemIsFocusable, false); + QVERIFY(!item->hasFocus()); + item->setFocus(); + QVERIFY(!item->hasFocus()); + + item->setFlag(QGraphicsItem::ItemIsFocusable, true); + QVERIFY(!item->hasFocus()); + item->setFocus(); + QVERIFY(item->hasFocus()); + QVERIFY(scene.hasFocus()); + + item->setFlag(QGraphicsItem::ItemIsFocusable, false); + QVERIFY(!item->hasFocus()); + QVERIFY(scene.hasFocus()); + } + { + // Selectable + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + QVERIFY(!item->isSelected()); + item->setSelected(true); + QVERIFY(!item->isSelected()); + + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + QVERIFY(!item->isSelected()); + item->setSelected(true); + QVERIFY(item->isSelected()); + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + QVERIFY(!item->isSelected()); + } + { + // Movable + item->setFlag(QGraphicsItem::ItemIsMovable, false); + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(0, 0)); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); // mouse grabber is reset + + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(10, 10)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event2); + QCOMPARE(item->pos(), QPointF()); + + QGraphicsSceneMouseEvent event3(QEvent::GraphicsSceneMouseRelease); + event3.setScenePos(QPointF(10, 10)); + event3.setButtons(0); + QApplication::sendEvent(&scene, &event3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + item->setFlag(QGraphicsItem::ItemIsMovable, true); + QGraphicsSceneMouseEvent event4(QEvent::GraphicsSceneMousePress); + event4.setScenePos(QPointF(0, 0)); + event4.setButton(Qt::LeftButton); + event4.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + QGraphicsSceneMouseEvent event5(QEvent::GraphicsSceneMouseMove); + event5.setScenePos(QPointF(10, 10)); + event5.setButton(Qt::LeftButton); + event5.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event5); + QCOMPARE(item->pos(), QPointF(10, 10)); + } + { + QGraphicsItem* clippingParent = new QGraphicsRectItem; + clippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + + QGraphicsItem* nonClippingParent = new QGraphicsRectItem; + nonClippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + + QGraphicsItem* child = new QGraphicsRectItem(nonClippingParent); + QVERIFY(!child->isClipped()); + + child->setParentItem(clippingParent); + QVERIFY(child->isClipped()); + + child->setParentItem(nonClippingParent); + QVERIFY(!child->isClipped()); + } +} + +class ImhTester : public QGraphicsItem +{ + QRectF boundingRect() const { return QRectF(); } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} +}; + +void tst_QGraphicsItem::inputMethodHints() +{ + ImhTester *item = new ImhTester; + item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + item->setFlag(QGraphicsItem::ItemIsFocusable, true); + QCOMPARE(item->inputMethodHints(), Qt::ImhNone); + ImhTester *item2 = new ImhTester; + item2->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + item2->setFlag(QGraphicsItem::ItemIsFocusable, true); + Qt::InputMethodHints imHints = item2->inputMethodHints(); + imHints |= Qt::ImhHiddenText; + item2->setInputMethodHints(imHints); + QGraphicsScene scene; + scene.addItem(item); + scene.addItem(item2); + QGraphicsView view(&scene); + QApplication::setActiveWindow(&view); + view.show(); + QTest::qWaitForWindowShown(&view); + item->setFocus(); + QTRY_VERIFY(item->hasFocus()); + QCOMPARE(view.inputMethodHints(), item->inputMethodHints()); + item2->setFocus(); + QTRY_VERIFY(item2->hasFocus()); + QCOMPARE(view.inputMethodHints(), item2->inputMethodHints()); + item->setFlag(QGraphicsItem::ItemAcceptsInputMethod, false); + item->setFocus(); + QTRY_VERIFY(item->hasFocus()); + //Focus has changed but the new item doesn't accept input method, no hints. + QCOMPARE(view.inputMethodHints(), 0); + item2->setFocus(); + QTRY_VERIFY(item2->hasFocus()); + QCOMPARE(view.inputMethodHints(), item2->inputMethodHints()); + imHints = item2->inputMethodHints(); + imHints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); + item2->setInputMethodHints(imHints); + QCOMPARE(view.inputMethodHints(), item2->inputMethodHints()); + QGraphicsProxyWidget *widget = new QGraphicsProxyWidget; + QLineEdit *edit = new QLineEdit; + edit->setEchoMode(QLineEdit::Password); + scene.addItem(widget); + widget->setFocus(); + QTRY_VERIFY(widget->hasFocus()); + //No widget on the proxy, so no hints + QCOMPARE(view.inputMethodHints(), 0); + widget->setWidget(edit); + //View should match with the line edit + QCOMPARE(view.inputMethodHints(), edit->inputMethodHints()); +} + +void tst_QGraphicsItem::toolTip() +{ + QString toolTip = "Qt rocks!"; + + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + item->setPen(QPen(Qt::red, 1)); + item->setBrush(QBrush(Qt::blue)); + QVERIFY(item->toolTip().isEmpty()); + item->setToolTip(toolTip); + QCOMPARE(item->toolTip(), toolTip); + + QGraphicsScene scene; + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.show(); + QTest::qWait(250); + { + QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), + view.viewport()->mapToGlobal(view.viewport()->rect().topLeft())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(250); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget == &view) + foundView = true; + if (widget->inherits("QTipLabel")) + foundTipLabel = true; + } + QVERIFY(foundView); + QVERIFY(!foundTipLabel); + } + + { + QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().center(), + view.viewport()->mapToGlobal(view.viewport()->rect().center())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(250); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget == &view) + foundView = true; + if (widget->inherits("QTipLabel")) + foundTipLabel = true; + } + QVERIFY(foundView); + QVERIFY(foundTipLabel); + } + + { + QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), + view.viewport()->mapToGlobal(view.viewport()->rect().topLeft())); + QApplication::sendEvent(view.viewport(), &helpEvent); + QTest::qWait(1000); + + bool foundView = false; + bool foundTipLabel = false; + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget == &view) + foundView = true; + if (widget->inherits("QTipLabel") && widget->isVisible()) + foundTipLabel = true; + } + QVERIFY(foundView); + QVERIFY(!foundTipLabel); + } +} + +void tst_QGraphicsItem::visible() +{ + QGraphicsItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20)); + item->setFlag(QGraphicsItem::ItemIsMovable); + QVERIFY(item->isVisible()); + item->setVisible(false); + QVERIFY(!item->isVisible()); + item->setVisible(true); + QVERIFY(item->isVisible()); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(item); + QVERIFY(item->isVisible()); + QCOMPARE(scene.itemAt(0, 0), item); + item->setVisible(false); + QCOMPARE(scene.itemAt(0, 0), (QGraphicsItem *)0); + item->setVisible(true); + QCOMPARE(scene.itemAt(0, 0), item); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), item); + item->setVisible(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + item->setVisible(true); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + item->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocus(); + QVERIFY(item->hasFocus()); + item->setVisible(false); + QVERIFY(!item->hasFocus()); + item->setVisible(true); + QVERIFY(!item->hasFocus()); +} + +void tst_QGraphicsItem::isVisibleTo() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50)); + QGraphicsItem *grandChild = scene.addRect(QRectF(50, 50, 50, 50)); + QGraphicsItem *stranger = scene.addRect(100, 100, 100, 100); + + child->setParentItem(parent); + grandChild->setParentItem(child); + + QVERIFY(grandChild->isVisible()); + QVERIFY(grandChild->isVisibleTo(grandChild)); + QVERIFY(grandChild->isVisibleTo(child)); + QVERIFY(grandChild->isVisibleTo(parent)); + QVERIFY(grandChild->isVisibleTo(0)); + QVERIFY(child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + QVERIFY(!parent->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(grandChild)); + QVERIFY(!grandChild->isVisibleTo(stranger)); + QVERIFY(!child->isVisibleTo(stranger)); + QVERIFY(!parent->isVisibleTo(stranger)); + QVERIFY(!stranger->isVisibleTo(grandChild)); + QVERIFY(!stranger->isVisibleTo(child)); + QVERIFY(!stranger->isVisibleTo(parent)); + + // Case 1: only parent is explicitly hidden + parent->hide(); + + QVERIFY(!grandChild->isVisible()); + QVERIFY(grandChild->isVisibleTo(grandChild)); + QVERIFY(grandChild->isVisibleTo(child)); + QVERIFY(grandChild->isVisibleTo(parent)); + QVERIFY(!grandChild->isVisibleTo(0)); + QVERIFY(!child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(!child->isVisibleTo(0)); + QVERIFY(!parent->isVisible()); + QVERIFY(!parent->isVisibleTo(parent)); + QVERIFY(!parent->isVisibleTo(0)); + QVERIFY(!parent->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(grandChild)); + QVERIFY(!grandChild->isVisibleTo(stranger)); + QVERIFY(!child->isVisibleTo(stranger)); + QVERIFY(!parent->isVisibleTo(stranger)); + QVERIFY(!stranger->isVisibleTo(grandChild)); + QVERIFY(!stranger->isVisibleTo(child)); + QVERIFY(!stranger->isVisibleTo(parent)); + + // Case 2: only child is hidden + parent->show(); + child->hide(); + + QVERIFY(!grandChild->isVisible()); + QVERIFY(grandChild->isVisibleTo(grandChild)); + QVERIFY(grandChild->isVisibleTo(child)); + QVERIFY(!grandChild->isVisibleTo(parent)); + QVERIFY(!grandChild->isVisibleTo(0)); + QVERIFY(!child->isVisible()); + QVERIFY(!child->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(parent)); + QVERIFY(!child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + QVERIFY(!parent->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(grandChild)); + QVERIFY(!grandChild->isVisibleTo(stranger)); + QVERIFY(!child->isVisibleTo(stranger)); + QVERIFY(!parent->isVisibleTo(stranger)); + QVERIFY(!stranger->isVisibleTo(grandChild)); + QVERIFY(!stranger->isVisibleTo(child)); + QVERIFY(!stranger->isVisibleTo(parent)); + + // Case 3: only grand child is hidden + child->show(); + grandChild->hide(); + + QVERIFY(!grandChild->isVisible()); + QVERIFY(!grandChild->isVisibleTo(grandChild)); + QVERIFY(!grandChild->isVisibleTo(child)); + QVERIFY(!grandChild->isVisibleTo(parent)); + QVERIFY(!grandChild->isVisibleTo(0)); + QVERIFY(child->isVisible()); + QVERIFY(child->isVisibleTo(child)); + QVERIFY(child->isVisibleTo(parent)); + QVERIFY(child->isVisibleTo(0)); + QVERIFY(parent->isVisible()); + QVERIFY(parent->isVisibleTo(parent)); + QVERIFY(parent->isVisibleTo(0)); + QVERIFY(!parent->isVisibleTo(child)); + QVERIFY(!child->isVisibleTo(grandChild)); + QVERIFY(!grandChild->isVisibleTo(stranger)); + QVERIFY(!child->isVisibleTo(stranger)); + QVERIFY(!parent->isVisibleTo(stranger)); + QVERIFY(!stranger->isVisibleTo(grandChild)); + QVERIFY(!stranger->isVisibleTo(child)); + QVERIFY(!stranger->isVisibleTo(parent)); +} + +void tst_QGraphicsItem::explicitlyVisible() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50)); + child->setParentItem(parent); + + QVERIFY(parent->isVisible()); + QVERIFY(child->isVisible()); + + parent->hide(); + + QVERIFY(!parent->isVisible()); + QVERIFY(!child->isVisible()); + + parent->show(); + child->hide(); + + QVERIFY(parent->isVisible()); + QVERIFY(!child->isVisible()); + + parent->hide(); + + QVERIFY(!parent->isVisible()); + QVERIFY(!child->isVisible()); + + parent->show(); + + QVERIFY(parent->isVisible()); + QVERIFY(!child->isVisible()); // <- explicitly hidden + + child->show(); + + QVERIFY(child->isVisible()); + + parent->hide(); + + QVERIFY(!parent->isVisible()); + QVERIFY(!child->isVisible()); // <- explicit show doesn't work + + parent->show(); + + QVERIFY(parent->isVisible()); + QVERIFY(child->isVisible()); // <- no longer explicitly hidden + + // ------------------- Reparenting ------------------------------ + + QGraphicsItem *parent2 = scene.addRect(-50, -50, 200, 200); + QVERIFY(parent2->isVisible()); + + // Reparent implicitly hidden item to a visible parent. + parent->hide(); + QVERIFY(!parent->isVisible()); + QVERIFY(!child->isVisible()); + child->setParentItem(parent2); + QVERIFY(parent2->isVisible()); + QVERIFY(child->isVisible()); + + // Reparent implicitly hidden item to a hidden parent. + child->setParentItem(parent); + parent2->hide(); + child->setParentItem(parent2); + QVERIFY(!parent2->isVisible()); + QVERIFY(!child->isVisible()); + + // Reparent explicitly hidden item to a visible parent. + child->hide(); + parent->show(); + child->setParentItem(parent); + QVERIFY(parent->isVisible()); + QVERIFY(!child->isVisible()); + + // Reparent explicitly hidden item to a hidden parent. + child->setParentItem(parent2); + QVERIFY(!parent2->isVisible()); + QVERIFY(!child->isVisible()); + + // Reparent explicitly hidden item to a visible parent. + parent->show(); + child->setParentItem(parent); + QVERIFY(parent->isVisible()); + QVERIFY(!child->isVisible()); + + // Reparent visible item to a hidden parent. + child->show(); + parent2->hide(); + child->setParentItem(parent2); + QVERIFY(!parent2->isVisible()); + QVERIFY(!child->isVisible()); + parent2->show(); + QVERIFY(parent2->isVisible()); + QVERIFY(child->isVisible()); + + // Reparent implicitly hidden child to root. + parent2->hide(); + QVERIFY(!child->isVisible()); + child->setParentItem(0); + QVERIFY(child->isVisible()); + + // Reparent an explicitly hidden child to root. + child->hide(); + child->setParentItem(parent2); + parent2->show(); + QVERIFY(!child->isVisible()); + child->setParentItem(0); + QVERIFY(!child->isVisible()); +} + +void tst_QGraphicsItem::enabled() +{ + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(-10, -10, 20, 20)); + item->setFlag(QGraphicsItem::ItemIsMovable); + QVERIFY(item->isEnabled()); + item->setEnabled(false); + QVERIFY(!item->isEnabled()); + item->setEnabled(true); + QVERIFY(item->isEnabled()); + item->setEnabled(false); + item->setFlag(QGraphicsItem::ItemIsFocusable); + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(item); + item->setFocus(); + QVERIFY(!item->hasFocus()); + item->setEnabled(true); + item->setFocus(); + QVERIFY(item->hasFocus()); + item->setEnabled(false); + QVERIFY(!item->hasFocus()); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + item->setEnabled(true); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + item->setEnabled(false); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::explicitlyEnabled() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsItem *child = scene.addRect(QRectF(25, 25, 50, 50)); + child->setParentItem(parent); + + QVERIFY(parent->isEnabled()); + QVERIFY(child->isEnabled()); + + parent->setEnabled(false); + + QVERIFY(!parent->isEnabled()); + QVERIFY(!child->isEnabled()); + + parent->setEnabled(true); + child->setEnabled(false); + + QVERIFY(parent->isEnabled()); + QVERIFY(!child->isEnabled()); + + parent->setEnabled(false); + + QVERIFY(!parent->isEnabled()); + QVERIFY(!child->isEnabled()); + + parent->setEnabled(true); + + QVERIFY(parent->isEnabled()); + QVERIFY(!child->isEnabled()); // <- explicitly disabled + + child->setEnabled(true); + + QVERIFY(child->isEnabled()); + + parent->setEnabled(false); + + QVERIFY(!parent->isEnabled()); + QVERIFY(!child->isEnabled()); // <- explicit enabled doesn't work + + parent->setEnabled(true); + + QVERIFY(parent->isEnabled()); + QVERIFY(child->isEnabled()); // <- no longer explicitly disabled + + // ------------------- Reparenting ------------------------------ + + QGraphicsItem *parent2 = scene.addRect(-50, -50, 200, 200); + QVERIFY(parent2->isEnabled()); + + // Reparent implicitly hidden item to a enabled parent. + parent->setEnabled(false); + QVERIFY(!parent->isEnabled()); + QVERIFY(!child->isEnabled()); + child->setParentItem(parent2); + QVERIFY(parent2->isEnabled()); + QVERIFY(child->isEnabled()); + + // Reparent implicitly hidden item to a hidden parent. + child->setParentItem(parent); + parent2->setEnabled(false); + child->setParentItem(parent2); + QVERIFY(!parent2->isEnabled()); + QVERIFY(!child->isEnabled()); + + // Reparent explicitly hidden item to a enabled parent. + child->setEnabled(false); + parent->setEnabled(true); + child->setParentItem(parent); + QVERIFY(parent->isEnabled()); + QVERIFY(!child->isEnabled()); + + // Reparent explicitly hidden item to a hidden parent. + child->setParentItem(parent2); + QVERIFY(!parent2->isEnabled()); + QVERIFY(!child->isEnabled()); + + // Reparent explicitly hidden item to a enabled parent. + parent->setEnabled(true); + child->setParentItem(parent); + QVERIFY(parent->isEnabled()); + QVERIFY(!child->isEnabled()); + + // Reparent enabled item to a hidden parent. + child->setEnabled(true); + parent2->setEnabled(false); + child->setParentItem(parent2); + QVERIFY(!parent2->isEnabled()); + QVERIFY(!child->isEnabled()); + parent2->setEnabled(true); + QVERIFY(parent2->isEnabled()); + QVERIFY(child->isEnabled()); + + // Reparent implicitly hidden child to root. + parent2->setEnabled(false); + QVERIFY(!child->isEnabled()); + child->setParentItem(0); + QVERIFY(child->isEnabled()); + + // Reparent an explicitly hidden child to root. + child->setEnabled(false); + child->setParentItem(parent2); + parent2->setEnabled(true); + QVERIFY(!child->isEnabled()); + child->setParentItem(0); + QVERIFY(!child->isEnabled()); +} + +class SelectChangeItem : public QGraphicsRectItem +{ +public: + SelectChangeItem() : QGraphicsRectItem(-50, -50, 100, 100) { setBrush(Qt::blue); } + QList values; + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + if (change == ItemSelectedChange) + values << value.toBool(); + return QGraphicsRectItem::itemChange(change, value); + } +}; + +void tst_QGraphicsItem::selected() +{ + SelectChangeItem *item = new SelectChangeItem; + item->setFlag(QGraphicsItem::ItemIsSelectable); + QVERIFY(!item->isSelected()); + QVERIFY(item->values.isEmpty()); + item->setSelected(true); + QCOMPARE(item->values.size(), 1); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + item->setSelected(false); + QCOMPARE(item->values.size(), 2); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setSelected(true); + QCOMPARE(item->values.size(), 3); + item->setEnabled(false); + QCOMPARE(item->values.size(), 4); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setEnabled(true); + QCOMPARE(item->values.size(), 4); + item->setSelected(true); + QCOMPARE(item->values.size(), 5); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + item->setVisible(false); + QCOMPARE(item->values.size(), 6); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + item->setVisible(true); + QCOMPARE(item->values.size(), 6); + item->setSelected(true); + QCOMPARE(item->values.size(), 7); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + QGraphicsScene scene(-100, -100, 200, 200); + scene.addItem(item); + QCOMPARE(scene.selectedItems(), QList() << item); + item->setSelected(false); + QVERIFY(scene.selectedItems().isEmpty()); + item->setSelected(true); + QCOMPARE(scene.selectedItems(), QList() << item); + item->setSelected(false); + QVERIFY(scene.selectedItems().isEmpty()); + + // Interactive selection + QGraphicsView view(&scene); + view.setFixedSize(250, 250); + view.show(); + + QTest::qWaitForWindowShown(&view); + qApp->processEvents(); + qApp->processEvents(); + + scene.clearSelection(); + QCOMPARE(item->values.size(), 10); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + + // Click inside and check that it's selected + QTest::mouseMove(view.viewport()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + QCOMPARE(item->values.size(), 11); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + // Click outside and check that it's not selected + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos() + QPointF(item->boundingRect().width(), item->boundingRect().height()))); + QCOMPARE(item->values.size(), 12); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + + SelectChangeItem *item2 = new SelectChangeItem; + item2->setFlag(QGraphicsItem::ItemIsSelectable); + item2->setPos(100, 0); + scene.addItem(item2); + + // Click inside and check that it's selected + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + QCOMPARE(item->values.size(), 13); + QCOMPARE(item->values.last(), true); + QVERIFY(item->isSelected()); + + // Click inside item2 and check that it's selected, and item is not + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QCOMPARE(item->values.size(), 14); + QCOMPARE(item->values.last(), false); + QVERIFY(!item->isSelected()); + QCOMPARE(item2->values.size(), 1); + QCOMPARE(item2->values.last(), true); + QVERIFY(item2->isSelected()); +} + +void tst_QGraphicsItem::selected2() +{ + // Selecting an item, then moving another previously caused a crash. + QGraphicsScene scene; + QGraphicsItem *line1 = scene.addRect(QRectF(0, 0, 100, 100)); + line1->setPos(-105, 0); + line1->setFlag(QGraphicsItem::ItemIsSelectable); + + QGraphicsItem *line2 = scene.addRect(QRectF(0, 0, 100, 100)); + line2->setFlag(QGraphicsItem::ItemIsMovable); + + line1->setSelected(true); + + { + QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress); + mousePress.setScenePos(QPointF(50, 50)); + mousePress.setButton(Qt::LeftButton); + QApplication::sendEvent(&scene, &mousePress); + QVERIFY(mousePress.isAccepted()); + } + { + QGraphicsSceneMouseEvent mouseMove(QEvent::GraphicsSceneMouseMove); + mouseMove.setScenePos(QPointF(60, 60)); + mouseMove.setButton(Qt::LeftButton); + mouseMove.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &mouseMove); + QVERIFY(mouseMove.isAccepted()); + } +} + +void tst_QGraphicsItem::selected_group() +{ + QGraphicsScene scene; + QGraphicsItem *item1 = scene.addRect(QRectF()); + QGraphicsItem *item2 = scene.addRect(QRectF()); + item1->setFlag(QGraphicsItem::ItemIsSelectable); + item2->setFlag(QGraphicsItem::ItemIsSelectable); + scene.addRect(QRectF())->setParentItem(item1); + QGraphicsItem *leaf = scene.addRect(QRectF()); + leaf->setFlag(QGraphicsItem::ItemIsSelectable); + leaf->setParentItem(item2); + + QGraphicsItemGroup *group = scene.createItemGroup(QList() << item1 << item2); + QCOMPARE(group->scene(), &scene); + group->setFlag(QGraphicsItem::ItemIsSelectable); + foreach (QGraphicsItem *item, scene.items()) { + if (item == group) + QVERIFY(!item->group()); + else + QCOMPARE(item->group(), group); + } + + QVERIFY(group->handlesChildEvents()); + QVERIFY(!group->isSelected()); + group->setSelected(false); + QVERIFY(!group->isSelected()); + group->setSelected(true); + QVERIFY(group->isSelected()); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(item->isSelected()); + group->setSelected(false); + QVERIFY(!group->isSelected()); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(!item->isSelected()); + leaf->setSelected(true); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(item->isSelected()); + leaf->setSelected(false); + foreach (QGraphicsItem *item, scene.items()) + QVERIFY(!item->isSelected()); + + leaf->setSelected(true); + QGraphicsScene scene2; + scene2.addItem(item1); + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); +} + +void tst_QGraphicsItem::selected_textItem() +{ + QGraphicsScene scene; + QGraphicsTextItem *text = scene.addText(QLatin1String("Text")); + text->setFlag(QGraphicsItem::ItemIsSelectable); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QTRY_VERIFY(!text->isSelected()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(text->mapToScene(0, 0))); + QTRY_VERIFY(text->isSelected()); + + text->setSelected(false); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(text->mapToScene(0, 0))); + QTRY_VERIFY(text->isSelected()); +} + +void tst_QGraphicsItem::selected_multi() +{ + // Test multiselection behavior + QGraphicsScene scene; + + // Create two disjoint items + QGraphicsItem *item1 = scene.addRect(QRectF(-10, -10, 20, 20)); + QGraphicsItem *item2 = scene.addRect(QRectF(-10, -10, 20, 20)); + item1->setPos(-15, 0); + item2->setPos(15, 20); + + // Make both items selectable + item1->setFlag(QGraphicsItem::ItemIsSelectable); + item2->setFlag(QGraphicsItem::ItemIsSelectable); + + // Create and show a view + QGraphicsView view(&scene); + view.show(); + view.fitInView(scene.sceneRect()); + qApp->processEvents(); + + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Start clicking + QTest::qWait(200); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Ctrl-click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(item1->isSelected()); + + // Ctrl-click on item1 again + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Ctrl-click on item2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item2->isSelected()); + QVERIFY(!item1->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on scene + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(0, 0)); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-click on scene + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(0, 0)); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Press on item2 + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + + // Release on item2 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item2->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(item2->isSelected()); + + // Click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-click on item1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + // Ctrl-press on item1 + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + { + // Ctrl-move on item1 + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->scenePos()) + QPoint(1, 0), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + QApplication::sendEvent(view.viewport(), &event); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + } + + // Release on item1 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + + item1->setFlag(QGraphicsItem::ItemIsMovable); + item1->setSelected(false); + + // Ctrl-press on item1 + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(!item1->isSelected()); + QVERIFY(!item2->isSelected()); + + { + // Ctrl-move on item1 + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->scenePos()) + QPoint(1, 0), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + QApplication::sendEvent(view.viewport(), &event); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); + } + + // Release on item1 + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.mapFromScene(item1->scenePos())); + QTest::qWait(20); + QVERIFY(item1->isSelected()); + QVERIFY(!item2->isSelected()); +} + +void tst_QGraphicsItem::acceptedMouseButtons() +{ + QGraphicsScene scene; + QGraphicsRectItem *item1 = scene.addRect(QRectF(-10, -10, 20, 20)); + QGraphicsRectItem *item2 = scene.addRect(QRectF(-10, -10, 20, 20)); + item2->setZValue(1); + + item1->setFlag(QGraphicsItem::ItemIsMovable); + item2->setFlag(QGraphicsItem::ItemIsMovable); + + QCOMPARE(item1->acceptedMouseButtons(), Qt::MouseButtons(0x1f)); + QCOMPARE(item2->acceptedMouseButtons(), Qt::MouseButtons(0x1f)); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item2); + item2->setAcceptedMouseButtons(0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QApplication::sendEvent(&scene, &event); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item1); +} + +class HoverItem : public QGraphicsRectItem +{ +public: + HoverItem(const QRectF &rect) + : QGraphicsRectItem(rect), hoverInCount(0), + hoverMoveCount(0), hoverOutCount(0) + { } + + int hoverInCount; + int hoverMoveCount; + int hoverOutCount; +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent *) + { ++hoverInCount; } + + void hoverMoveEvent(QGraphicsSceneHoverEvent *) + { ++hoverMoveCount; } + + void hoverLeaveEvent(QGraphicsSceneHoverEvent *) + { ++hoverOutCount; } +}; + +void tst_QGraphicsItem::acceptsHoverEvents() +{ + QGraphicsScene scene; + HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20)); + HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10)); + scene.addItem(item1); + scene.addItem(item2); + item2->setZValue(1); + + QVERIFY(!item1->acceptsHoverEvents()); + QVERIFY(!item2->acceptsHoverEvents()); + item1->setAcceptsHoverEvents(true); + item2->setAcceptsHoverEvents(true); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item2->hoverInCount, 1); + + item1->setAcceptsHoverEvents(false); + item2->setAcceptsHoverEvents(false); + + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item2->hoverInCount, 1); + + item1->setAcceptsHoverEvents(true); + item2->setAcceptsHoverEvents(false); + + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item1->hoverInCount, 1); + QCOMPARE(item2->hoverInCount, 1); +} + +void tst_QGraphicsItem::childAcceptsHoverEvents() +{ + QGraphicsScene scene; + HoverItem *item1 = new HoverItem(QRectF(-10, -10, 20, 20)); + HoverItem *item2 = new HoverItem(QRectF(-5, -5, 10, 10)); + + scene.addItem(item1); + scene.addItem(item2); + item2->setParentItem(item1); + item2->setAcceptHoverEvents(true); + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + QCOMPARE(item2->hoverInCount, 0); + QCOMPARE(item2->hoverMoveCount, 0); + QCOMPARE(item2->hoverOutCount, 0); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + + event.setScenePos(QPointF(-2.5, -2.5)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item2->hoverInCount, 1); + QCOMPARE(item2->hoverMoveCount, 1); + QCOMPARE(item2->hoverOutCount, 0); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item2->hoverInCount, 1); + QCOMPARE(item2->hoverMoveCount, 2); + QCOMPARE(item2->hoverOutCount, 0); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + + event.setScenePos(QPointF(-7, -7)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item2->hoverInCount, 1); + QCOMPARE(item2->hoverMoveCount, 2); + QCOMPARE(item2->hoverOutCount, 1); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + + event.setScenePos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item2->hoverInCount, 2); + QCOMPARE(item2->hoverMoveCount, 3); + QCOMPARE(item2->hoverOutCount, 1); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + + HoverItem *item0 = new HoverItem(QRectF(-20, -20, 20, 20)); + scene.addItem(item0); + item1->setParentItem(item0); + item0->setAcceptHoverEvents(true); + + event.setScenePos(QPointF(-100, -100)); + QApplication::sendEvent(&scene, &event); + + event.setScenePos(QPointF(-15, -15)); + QApplication::sendEvent(&scene, &event); + + QCOMPARE(item2->hoverInCount, 2); + QCOMPARE(item2->hoverMoveCount, 3); + QCOMPARE(item2->hoverOutCount, 2); + QCOMPARE(item1->hoverInCount, 0); + QCOMPARE(item1->hoverMoveCount, 0); + QCOMPARE(item1->hoverOutCount, 0); + QCOMPARE(item0->hoverInCount, 1); + QCOMPARE(item0->hoverMoveCount, 1); + QCOMPARE(item0->hoverOutCount, 0); +} + +void tst_QGraphicsItem::hasFocus() +{ + QGraphicsLineItem *line = new QGraphicsLineItem; + QVERIFY(!line->hasFocus()); + line->setFocus(); + QVERIFY(!line->hasFocus()); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(line); + + line->setFocus(); + QVERIFY(!line->hasFocus()); + line->setFlag(QGraphicsItem::ItemIsFocusable); + line->setFocus(); + QVERIFY(line->hasFocus()); + + QGraphicsScene scene2; + QApplication::sendEvent(&scene2, &activate); + + scene2.addItem(line); + QVERIFY(!line->hasFocus()); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)0); + QCOMPARE(scene2.focusItem(), (QGraphicsItem *)0); + + line->setFocus(); + QVERIFY(line->hasFocus()); + line->clearFocus(); + QVERIFY(!line->hasFocus()); + + QGraphicsLineItem *line2 = new QGraphicsLineItem; + line2->setFlag(QGraphicsItem::ItemIsFocusable); + scene2.addItem(line2); + + line2->setFocus(); + QVERIFY(!line->hasFocus()); + QVERIFY(line2->hasFocus()); + line->setFocus(); + QVERIFY(line->hasFocus()); + QVERIFY(!line2->hasFocus()); +} + +void tst_QGraphicsItem::pos() +{ + QGraphicsItem *child = new QGraphicsLineItem; + QGraphicsItem *parent = new QGraphicsLineItem; + + QCOMPARE(child->pos(), QPointF()); + QCOMPARE(parent->pos(), QPointF()); + + child->setParentItem(parent); + child->setPos(10, 10); + + QCOMPARE(child->pos(), QPointF(10, 10)); + + parent->setPos(10, 10); + + QCOMPARE(parent->pos(), QPointF(10, 10)); + QCOMPARE(child->pos(), QPointF(10, 10)); + + delete child; + delete parent; +} + +void tst_QGraphicsItem::scenePos() +{ + QGraphicsItem *child = new QGraphicsLineItem; + QGraphicsItem *parent = new QGraphicsLineItem; + + QCOMPARE(child->scenePos(), QPointF()); + QCOMPARE(parent->scenePos(), QPointF()); + + child->setParentItem(parent); + child->setPos(10, 10); + + QCOMPARE(child->scenePos(), QPointF(10, 10)); + + parent->setPos(10, 10); + + QCOMPARE(parent->scenePos(), QPointF(10, 10)); + QCOMPARE(child->scenePos(), QPointF(20, 20)); + + parent->setPos(20, 20); + + QCOMPARE(parent->scenePos(), QPointF(20, 20)); + QCOMPARE(child->scenePos(), QPointF(30, 30)); + + delete child; + delete parent; +} + +void tst_QGraphicsItem::matrix() +{ + QGraphicsLineItem line; + QCOMPARE(line.matrix(), QMatrix()); + line.setMatrix(QMatrix().rotate(90)); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.setMatrix(QMatrix().rotate(90)); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.setMatrix(QMatrix().rotate(90), true); + QCOMPARE(line.matrix(), QMatrix().rotate(180)); + line.setMatrix(QMatrix().rotate(-90), true); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.resetMatrix(); + QCOMPARE(line.matrix(), QMatrix()); + + line.rotate(90); + QCOMPARE(line.matrix(), QMatrix().rotate(90)); + line.rotate(90); + QCOMPARE(line.matrix(), QMatrix().rotate(90).rotate(90)); + line.resetMatrix(); + + line.scale(2, 4); + QCOMPARE(line.matrix(), QMatrix().scale(2, 4)); + line.scale(2, 4); + QCOMPARE(line.matrix(), QMatrix().scale(2, 4).scale(2, 4)); + line.resetMatrix(); + + line.shear(2, 4); + QCOMPARE(line.matrix(), QMatrix().shear(2, 4)); + line.shear(2, 4); + QCOMPARE(line.matrix(), QMatrix().shear(2, 4).shear(2, 4)); + line.resetMatrix(); + + line.translate(10, 10); + QCOMPARE(line.matrix(), QMatrix().translate(10, 10)); + line.translate(10, 10); + QCOMPARE(line.matrix(), QMatrix().translate(10, 10).translate(10, 10)); + line.resetMatrix(); +} + +void tst_QGraphicsItem::sceneMatrix() +{ + QGraphicsLineItem *parent = new QGraphicsLineItem; + QGraphicsLineItem *child = new QGraphicsLineItem(QLineF(), parent); + + QCOMPARE(parent->sceneMatrix(), QMatrix()); + QCOMPARE(child->sceneMatrix(), QMatrix()); + + parent->translate(10, 10); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(10, 10)); + + child->translate(10, 10); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(20, 20)); + + parent->rotate(90); + QCOMPARE(parent->sceneMatrix(), QMatrix().translate(10, 10).rotate(90)); + QCOMPARE(child->sceneMatrix(), QMatrix().translate(10, 10).rotate(90).translate(10, 10)); + + delete child; + delete parent; +} + +void tst_QGraphicsItem::setMatrix() +{ + QGraphicsScene scene; + qRegisterMetaType >("QList"); + QSignalSpy spy(&scene, SIGNAL(changed(QList))); + QRectF unrotatedRect(-12, -34, 56, 78); + QGraphicsRectItem item(unrotatedRect, 0, &scene); + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 1); + + item.setMatrix(QMatrix().rotate(qreal(12.34))); + QRectF rotatedRect = scene.sceneRect(); + QVERIFY(unrotatedRect != rotatedRect); + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 2); + + item.setMatrix(QMatrix()); + + scene.update(scene.sceneRect()); + QApplication::instance()->processEvents(); + + QCOMPARE(spy.count(), 3); + QList rlist = qVariantValue >(spy.last().at(0)); + + QCOMPARE(rlist.size(), 3); + QCOMPARE(rlist.at(0), rotatedRect); // From item.setMatrix() (clearing rotated rect) + QCOMPARE(rlist.at(1), rotatedRect); // From scene.update() (updating scene rect) + QCOMPARE(rlist.at(2), unrotatedRect); // From post-update (update current state) +} + +static QList _paintedItems; +class PainterItem : public QGraphicsItem +{ +protected: + QRectF boundingRect() const + { return QRectF(-10, -10, 20, 20); } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { _paintedItems << this; painter->fillRect(boundingRect(), Qt::red); } +}; + +void tst_QGraphicsItem::zValue() +{ + Q_CHECK_PAINTEVENTS + + QGraphicsScene scene; + + QGraphicsItem *item1 = new PainterItem; + QGraphicsItem *item2 = new PainterItem; + QGraphicsItem *item3 = new PainterItem; + QGraphicsItem *item4 = new PainterItem; + scene.addItem(item1); + scene.addItem(item2); + scene.addItem(item3); + scene.addItem(item4); + item2->setZValue(-3); + item4->setZValue(-2); + item1->setZValue(-1); + item3->setZValue(0); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::processEvents(); +#ifdef Q_WS_QWS + QApplication::sendPostedEvents(); //glib workaround +#endif + + QTRY_VERIFY(!_paintedItems.isEmpty()); + QVERIFY((_paintedItems.size() % 4) == 0); + for (int i = 0; i < 3; ++i) + QVERIFY(_paintedItems.at(i)->zValue() < _paintedItems.at(i + 1)->zValue()); +} + +void tst_QGraphicsItem::shape() +{ + QGraphicsLineItem line(QLineF(-10, -10, 20, 20)); + + // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0 + // if we pass a value of 0.0 to QPainterPathStroker::setWidth() + const qreal penWidthZero = qreal(0.00000001); + + QPainterPathStroker ps; + ps.setWidth(penWidthZero); + + QPainterPath path(line.line().p1()); + path.lineTo(line.line().p2()); + QPainterPath p = ps.createStroke(path); + p.addPath(path); + QCOMPARE(line.shape(), p); + + QPen linePen; + linePen.setWidthF(5.0); + linePen.setCapStyle(Qt::RoundCap); + line.setPen(linePen); + + ps.setCapStyle(line.pen().capStyle()); + ps.setWidth(line.pen().widthF()); + p = ps.createStroke(path); + p.addPath(path); + QCOMPARE(line.shape(), p); + + linePen.setCapStyle(Qt::FlatCap); + line.setPen(linePen); + ps.setCapStyle(line.pen().capStyle()); + p = ps.createStroke(path); + p.addPath(path); + QCOMPARE(line.shape(), p); + + linePen.setCapStyle(Qt::SquareCap); + line.setPen(linePen); + ps.setCapStyle(line.pen().capStyle()); + p = ps.createStroke(path); + p.addPath(path); + QCOMPARE(line.shape(), p); + + QGraphicsRectItem rect(QRectF(-10, -10, 20, 20)); + QPainterPathStroker ps1; + ps1.setWidth(penWidthZero); + path = QPainterPath(); + path.addRect(rect.rect()); + p = ps1.createStroke(path); + p.addPath(path); + QCOMPARE(rect.shape(), p); + + QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20)); + QPainterPathStroker ps2; + ps2.setWidth(ellipse.pen().widthF() <= 0.0 ? penWidthZero : ellipse.pen().widthF()); + path = QPainterPath(); + path.addEllipse(ellipse.rect()); + p = ps2.createStroke(path); + p.addPath(path); + QCOMPARE(ellipse.shape(), p); + + QPainterPathStroker ps3; + ps3.setWidth(penWidthZero); + p = ps3.createStroke(path); + p.addPath(path); + QGraphicsPathItem pathItem(path); + QCOMPARE(pathItem.shape(), p); + + QRegion region(QRect(0, 0, 300, 200)); + region = region.subtracted(QRect(50, 50, 200, 100)); + + QImage image(300, 200, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter painter(&image); + painter.setClipRegion(region); + painter.fillRect(0, 0, 300, 200, Qt::green); + painter.end(); + QPixmap pixmap = QPixmap::fromImage(image); + + QGraphicsPixmapItem pixmapItem(pixmap); + path = QPainterPath(); + path.addRegion(region); + + { + QBitmap bitmap(300, 200); + bitmap.clear(); + QPainter painter(&bitmap); + painter.setClipRegion(region); + painter.fillRect(0, 0, 300, 200, Qt::color1); + painter.end(); + + QBitmap bitmap2(300, 200); + bitmap2.clear(); + painter.begin(&bitmap2); + painter.setClipPath(pixmapItem.shape()); + painter.fillRect(0, 0, 300, 200, Qt::color1); + painter.end(); + + QCOMPARE(bitmap.toImage(), bitmap2.toImage()); + } + + QPolygonF poly; + poly << QPointF(0, 0) << QPointF(10, 0) << QPointF(0, 10); + QGraphicsPolygonItem polygon(poly); + path = QPainterPath(); + path.addPolygon(poly); + + QPainterPathStroker ps4; + ps4.setWidth(penWidthZero); + p = ps4.createStroke(path); + p.addPath(path); + QCOMPARE(polygon.shape(), p); +} + +void tst_QGraphicsItem::contains() +{ + if (sizeof(qreal) != sizeof(double)) { + QSKIP("Skipped due to rounding errors", SkipAll); + } + + // Rect + QGraphicsRectItem rect(QRectF(-10, -10, 20, 20)); + QVERIFY(!rect.contains(QPointF(-11, -10))); + QVERIFY(rect.contains(QPointF(-10, -10))); + QVERIFY(!rect.contains(QPointF(-11, 0))); + QVERIFY(rect.contains(QPointF(-10, 0))); + QVERIFY(rect.contains(QPointF(0, -10))); + QVERIFY(rect.contains(QPointF(0, 0))); + QVERIFY(rect.contains(QPointF(9, 9))); + + // Ellipse + QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20)); + QVERIFY(!ellipse.contains(QPointF(-10, -10))); + QVERIFY(ellipse.contains(QPointF(-9, 0))); + QVERIFY(ellipse.contains(QPointF(0, -9))); + QVERIFY(ellipse.contains(QPointF(0, 0))); + QVERIFY(!ellipse.contains(QPointF(9, 9))); + + // Line + QGraphicsLineItem line(QLineF(-10, -10, 20, 20)); + QVERIFY(!line.contains(QPointF(-10, 0))); + QVERIFY(!line.contains(QPointF(0, -10))); + QVERIFY(!line.contains(QPointF(10, 0))); + QVERIFY(!line.contains(QPointF(0, 10))); + QVERIFY(line.contains(QPointF(0, 0))); + QVERIFY(line.contains(QPointF(-9, -9))); + QVERIFY(line.contains(QPointF(9, 9))); + + // Polygon + QGraphicsPolygonItem polygon(QPolygonF() + << QPointF(0, 0) + << QPointF(10, 0) + << QPointF(0, 10)); + QVERIFY(polygon.contains(QPointF(1, 1))); + QVERIFY(polygon.contains(QPointF(4, 4))); + QVERIFY(polygon.contains(QPointF(1, 4))); + QVERIFY(polygon.contains(QPointF(4, 1))); + QVERIFY(!polygon.contains(QPointF(8, 8))); + QVERIFY(polygon.contains(QPointF(1, 8))); + QVERIFY(polygon.contains(QPointF(8, 1))); +} + +void tst_QGraphicsItem::collidesWith_item() +{ + // Rectangle + QGraphicsRectItem rect(QRectF(-10, -10, 20, 20)); + QGraphicsRectItem rect2(QRectF(-10, -10, 20, 20)); + QVERIFY(rect.collidesWithItem(&rect2)); + QVERIFY(rect2.collidesWithItem(&rect)); + rect2.setPos(21, 21); + QVERIFY(!rect.collidesWithItem(&rect2)); + QVERIFY(!rect2.collidesWithItem(&rect)); + rect2.setPos(-21, -21); + QVERIFY(!rect.collidesWithItem(&rect2)); + QVERIFY(!rect2.collidesWithItem(&rect)); + rect2.setPos(-17, -17); + QVERIFY(rect.collidesWithItem(&rect2)); + QVERIFY(rect2.collidesWithItem(&rect)); + + QGraphicsEllipseItem ellipse(QRectF(-10, -10, 20, 20)); + QGraphicsEllipseItem ellipse2(QRectF(-10, -10, 20, 20)); + QVERIFY(ellipse.collidesWithItem(&ellipse2)); + QVERIFY(ellipse2.collidesWithItem(&ellipse)); + ellipse2.setPos(21, 21); + QVERIFY(!ellipse.collidesWithItem(&ellipse2)); + QVERIFY(!ellipse2.collidesWithItem(&ellipse)); + ellipse2.setPos(-21, -21); + QVERIFY(!ellipse.collidesWithItem(&ellipse2)); + QVERIFY(!ellipse2.collidesWithItem(&ellipse)); + + ellipse2.setPos(-17, -17); + QVERIFY(!ellipse.collidesWithItem(&ellipse2)); + QVERIFY(!ellipse2.collidesWithItem(&ellipse)); + + { + QGraphicsScene scene; + QGraphicsRectItem rect(20, 20, 100, 100, 0, &scene); + QGraphicsRectItem rect2(40, 40, 50, 50, 0, &scene); + rect2.setZValue(1); + QGraphicsLineItem line(0, 0, 200, 200, 0, &scene); + line.setZValue(2); + + QCOMPARE(scene.items().size(), 3); + + QList col1 = rect.collidingItems(); + QCOMPARE(col1.size(), 2); + QCOMPARE(col1.first(), static_cast(&line)); + QCOMPARE(col1.last(), static_cast(&rect2)); + + QList col2 = rect2.collidingItems(); + QCOMPARE(col2.size(), 2); + QCOMPARE(col2.first(), static_cast(&line)); + QCOMPARE(col2.last(), static_cast(&rect)); + + QList col3 = line.collidingItems(); + QCOMPARE(col3.size(), 2); + QCOMPARE(col3.first(), static_cast(&rect2)); + QCOMPARE(col3.last(), static_cast(&rect)); + } +} + +void tst_QGraphicsItem::collidesWith_path_data() +{ + QTest::addColumn("pos"); + QTest::addColumn("matrix"); + QTest::addColumn("shape"); + QTest::addColumn("rectCollides"); + QTest::addColumn("ellipseCollides"); + + QTest::newRow("nothing") << QPointF(0, 0) << QMatrix() << QPainterPath() << false << false; + + QPainterPath rect; + rect.addRect(0, 0, 20, 20); + + QTest::newRow("rect1") << QPointF(0, 0) << QMatrix() << rect << true << true; + QTest::newRow("rect2") << QPointF(0, 0) << QMatrix().translate(21, 21) << rect << false << false; + QTest::newRow("rect3") << QPointF(21, 21) << QMatrix() << rect << false << false; +} + +void tst_QGraphicsItem::collidesWith_path() +{ + QFETCH(QPointF, pos); + QFETCH(QMatrix, matrix); + QFETCH(QPainterPath, shape); + QFETCH(bool, rectCollides); + QFETCH(bool, ellipseCollides); + + QGraphicsRectItem rect(QRectF(0, 0, 20, 20)); + QGraphicsEllipseItem ellipse(QRectF(0, 0, 20, 20)); + + rect.setPos(pos); + rect.setMatrix(matrix); + + ellipse.setPos(pos); + ellipse.setMatrix(matrix); + + QPainterPath mappedShape = rect.sceneMatrix().inverted().map(shape); + + if (rectCollides) + QVERIFY(rect.collidesWithPath(mappedShape)); + else + QVERIFY(!rect.collidesWithPath(mappedShape)); + + if (ellipseCollides) + QVERIFY(ellipse.collidesWithPath(mappedShape)); + else + QVERIFY(!ellipse.collidesWithPath(mappedShape)); +} + +void tst_QGraphicsItem::collidesWithItemWithClip() +{ + QGraphicsScene scene; + + QGraphicsEllipseItem *ellipse = scene.addEllipse(0, 0, 100, 100); + ellipse->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QGraphicsEllipseItem *ellipse2 = scene.addEllipse(0, 0, 10, 10); + ellipse2->setParentItem(ellipse); + QGraphicsEllipseItem *ellipse3 = scene.addEllipse(0, 0, 10, 10); + ellipse3->setParentItem(ellipse); + QGraphicsEllipseItem *ellipse5 = scene.addEllipse(50, 50, 10, 10); + ellipse5->setParentItem(ellipse); + QGraphicsEllipseItem *ellipse4 = scene.addEllipse(0, 0, 10, 10); + + QVERIFY(ellipse2->collidesWithItem(ellipse3)); + QVERIFY(ellipse3->collidesWithItem(ellipse2)); + QVERIFY(!ellipse2->collidesWithItem(ellipse)); + QVERIFY(!ellipse->collidesWithItem(ellipse2)); + QVERIFY(!ellipse4->collidesWithItem(ellipse)); + QVERIFY(!ellipse4->collidesWithItem(ellipse2)); + QVERIFY(!ellipse4->collidesWithItem(ellipse3)); + QVERIFY(!ellipse->collidesWithItem(ellipse4)); + QVERIFY(!ellipse2->collidesWithItem(ellipse4)); + QVERIFY(!ellipse3->collidesWithItem(ellipse4)); + QVERIFY(ellipse->collidesWithItem(ellipse5)); + QVERIFY(ellipse5->collidesWithItem(ellipse)); +} + +class MyItem : public QGraphicsEllipseItem +{ +public: + bool isObscuredBy(const QGraphicsItem *item) const + { + const MyItem *myItem = qgraphicsitem_cast(item); + if (myItem) { + if (item->zValue() > zValue()) { + QRectF r = rect(); + QPointF topMid = (r.topRight()+r.topLeft())/2; + QPointF botMid = (r.bottomRight()+r.bottomLeft())/2; + QPointF leftMid = (r.topLeft()+r.bottomLeft())/2; + QPointF rightMid = (r.topRight()+r.bottomRight())/2; + + QPainterPath mappedShape = item->mapToItem(this, item->opaqueArea()); + + if (mappedShape.contains(topMid) && + mappedShape.contains(botMid) && + mappedShape.contains(leftMid) && + mappedShape.contains(rightMid)) + return true; + else + return false; + } + else return false; + } + else + return QGraphicsItem::isObscuredBy(item); + } + + QPainterPath opaqueArea() const + { + return shape(); + } + + enum { + Type = UserType+1 + }; + int type() const { return Type; } +}; + +void tst_QGraphicsItem::isObscuredBy() +{ + QGraphicsScene scene; + + MyItem myitem1, myitem2; + + myitem1.setRect(QRectF(50, 50, 40, 200)); + myitem1.rotate(67); + + myitem2.setRect(QRectF(25, 25, 20, 20)); + myitem2.setZValue(-1.0); + scene.addItem(&myitem1); + scene.addItem(&myitem2); + + QVERIFY(!myitem2.isObscuredBy(&myitem1)); + QVERIFY(!myitem1.isObscuredBy(&myitem2)); + + myitem2.setRect(QRectF(-50, 85, 20, 20)); + QVERIFY(myitem2.isObscuredBy(&myitem1)); + QVERIFY(!myitem1.isObscuredBy(&myitem2)); + + myitem2.setRect(QRectF(-30, 70, 20, 20)); + QVERIFY(!myitem2.isObscuredBy(&myitem1)); + QVERIFY(!myitem1.isObscuredBy(&myitem2)); + + QGraphicsRectItem rect1, rect2; + + rect1.setRect(QRectF(-40, -40, 50, 50)); + rect1.setBrush(QBrush(Qt::red)); + rect2.setRect(QRectF(-30, -20, 20, 20)); + rect2.setZValue(-1.0); + rect2.setBrush(QBrush(Qt::blue)); + + QVERIFY(rect2.isObscuredBy(&rect1)); + QVERIFY(!rect1.isObscuredBy(&rect2)); + + rect2.setPos(QPointF(-20, -25)); + + QVERIFY(!rect2.isObscuredBy(&rect1)); + QVERIFY(!rect1.isObscuredBy(&rect2)); + + rect2.setPos(QPointF(-100, -100)); + + QVERIFY(!rect2.isObscuredBy(&rect1)); + QVERIFY(!rect1.isObscuredBy(&rect2)); +} + +class OpaqueItem : public QGraphicsRectItem +{ +protected: + QPainterPath opaqueArea() const + { + return shape(); + } +}; + +void tst_QGraphicsItem::isObscured() +{ + if (sizeof(qreal) != sizeof(double)) { + QSKIP("Skipped due to rounding errors", SkipAll); + } + + OpaqueItem *item1 = new OpaqueItem; + item1->setRect(0, 0, 100, 100); + item1->setZValue(0); + + OpaqueItem *item2 = new OpaqueItem; + item2->setZValue(1); + item2->setRect(0, 0, 100, 100); + + QGraphicsScene scene; + scene.addItem(item1); + scene.addItem(item2); + + QVERIFY(item1->isObscured()); + QVERIFY(item1->isObscuredBy(item2)); + QVERIFY(item1->isObscured(QRectF(0, 0, 50, 50))); + QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50))); + QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50))); + QVERIFY(item1->isObscured(QRectF(0, 50, 50, 50))); + QVERIFY(item1->isObscured(0, 0, 50, 50)); + QVERIFY(item1->isObscured(50, 0, 50, 50)); + QVERIFY(item1->isObscured(50, 50, 50, 50)); + QVERIFY(item1->isObscured(0, 50, 50, 50)); + QVERIFY(!item2->isObscured()); + QVERIFY(!item2->isObscuredBy(item1)); + QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50))); + QVERIFY(!item2->isObscured(0, 0, 50, 50)); + QVERIFY(!item2->isObscured(50, 0, 50, 50)); + QVERIFY(!item2->isObscured(50, 50, 50, 50)); + QVERIFY(!item2->isObscured(0, 50, 50, 50)); + + item2->moveBy(50, 0); + + QVERIFY(!item1->isObscured()); + QVERIFY(!item1->isObscuredBy(item2)); + QVERIFY(!item1->isObscured(QRectF(0, 0, 50, 50))); + QVERIFY(item1->isObscured(QRectF(50, 0, 50, 50))); + QVERIFY(item1->isObscured(QRectF(50, 50, 50, 50))); + QVERIFY(!item1->isObscured(QRectF(0, 50, 50, 50))); + QVERIFY(!item1->isObscured(0, 0, 50, 50)); + QVERIFY(item1->isObscured(50, 0, 50, 50)); + QVERIFY(item1->isObscured(50, 50, 50, 50)); + QVERIFY(!item1->isObscured(0, 50, 50, 50)); + QVERIFY(!item2->isObscured()); + QVERIFY(!item2->isObscuredBy(item1)); + QVERIFY(!item2->isObscured(QRectF(0, 0, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(50, 0, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(50, 50, 50, 50))); + QVERIFY(!item2->isObscured(QRectF(0, 50, 50, 50))); + QVERIFY(!item2->isObscured(0, 0, 50, 50)); + QVERIFY(!item2->isObscured(50, 0, 50, 50)); + QVERIFY(!item2->isObscured(50, 50, 50, 50)); + QVERIFY(!item2->isObscured(0, 50, 50, 50)); +} + +void tst_QGraphicsItem::mapFromToParent() +{ + QPainterPath path1; + path1.addRect(0, 0, 200, 200); + + QPainterPath path2; + path2.addRect(0, 0, 100, 100); + + QPainterPath path3; + path3.addRect(0, 0, 50, 50); + + QPainterPath path4; + path4.addRect(0, 0, 25, 25); + + QGraphicsItem *item1 = new QGraphicsPathItem(path1); + QGraphicsItem *item2 = new QGraphicsPathItem(path2, item1); + QGraphicsItem *item3 = new QGraphicsPathItem(path3, item2); + QGraphicsItem *item4 = new QGraphicsPathItem(path4, item3); + + item1->setPos(10, 10); + item2->setPos(10, 10); + item3->setPos(10, 10); + item4->setPos(10, 10); + + for (int i = 0; i < 4; ++i) { + QMatrix matrix; + matrix.rotate(i * 90); + matrix.translate(i * 100, -i * 100); + matrix.scale(2, 4); + item1->setMatrix(matrix); + + QCOMPARE(item1->mapToParent(QPointF(0, 0)), item1->pos() + matrix.map(QPointF(0, 0))); + QCOMPARE(item2->mapToParent(QPointF(0, 0)), item2->pos()); + QCOMPARE(item3->mapToParent(QPointF(0, 0)), item3->pos()); + QCOMPARE(item4->mapToParent(QPointF(0, 0)), item4->pos()); + QCOMPARE(item1->mapToParent(QPointF(10, -10)), item1->pos() + matrix.map(QPointF(10, -10))); + QCOMPARE(item2->mapToParent(QPointF(10, -10)), item2->pos() + QPointF(10, -10)); + QCOMPARE(item3->mapToParent(QPointF(10, -10)), item3->pos() + QPointF(10, -10)); + QCOMPARE(item4->mapToParent(QPointF(10, -10)), item4->pos() + QPointF(10, -10)); + QCOMPARE(item1->mapToParent(QPointF(-10, 10)), item1->pos() + matrix.map(QPointF(-10, 10))); + QCOMPARE(item2->mapToParent(QPointF(-10, 10)), item2->pos() + QPointF(-10, 10)); + QCOMPARE(item3->mapToParent(QPointF(-10, 10)), item3->pos() + QPointF(-10, 10)); + QCOMPARE(item4->mapToParent(QPointF(-10, 10)), item4->pos() + QPointF(-10, 10)); + QCOMPARE(item1->mapFromParent(item1->pos()), matrix.inverted().map(QPointF(0, 0))); + QCOMPARE(item2->mapFromParent(item2->pos()), QPointF(0, 0)); + QCOMPARE(item3->mapFromParent(item3->pos()), QPointF(0, 0)); + QCOMPARE(item4->mapFromParent(item4->pos()), QPointF(0, 0)); + QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(10, -10)), + matrix.inverted().map(QPointF(10, -10))); + QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(10, -10)), QPointF(10, -10)); + QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(10, -10)), QPointF(10, -10)); + QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(10, -10)), QPointF(10, -10)); + QCOMPARE(item1->mapFromParent(item1->pos() + QPointF(-10, 10)), + matrix.inverted().map(QPointF(-10, 10))); + QCOMPARE(item2->mapFromParent(item2->pos() + QPointF(-10, 10)), QPointF(-10, 10)); + QCOMPARE(item3->mapFromParent(item3->pos() + QPointF(-10, 10)), QPointF(-10, 10)); + QCOMPARE(item4->mapFromParent(item4->pos() + QPointF(-10, 10)), QPointF(-10, 10)); + } + + delete item1; +} + +void tst_QGraphicsItem::mapFromToScene() +{ + QGraphicsItem *item1 = new QGraphicsPathItem(QPainterPath()); + QGraphicsItem *item2 = new QGraphicsPathItem(QPainterPath(), item1); + QGraphicsItem *item3 = new QGraphicsPathItem(QPainterPath(), item2); + QGraphicsItem *item4 = new QGraphicsPathItem(QPainterPath(), item3); + + item1->setPos(100, 100); + item2->setPos(100, 100); + item3->setPos(100, 100); + item4->setPos(100, 100); + QCOMPARE(item1->pos(), QPointF(100, 100)); + QCOMPARE(item2->pos(), QPointF(100, 100)); + QCOMPARE(item3->pos(), QPointF(100, 100)); + QCOMPARE(item4->pos(), QPointF(100, 100)); + QCOMPARE(item1->pos(), item1->mapToParent(0, 0)); + QCOMPARE(item2->pos(), item2->mapToParent(0, 0)); + QCOMPARE(item3->pos(), item3->mapToParent(0, 0)); + QCOMPARE(item4->pos(), item4->mapToParent(0, 0)); + QCOMPARE(item1->mapToParent(10, 10), QPointF(110, 110)); + QCOMPARE(item2->mapToParent(10, 10), QPointF(110, 110)); + QCOMPARE(item3->mapToParent(10, 10), QPointF(110, 110)); + QCOMPARE(item4->mapToParent(10, 10), QPointF(110, 110)); + QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100)); + QCOMPARE(item2->mapToScene(0, 0), QPointF(200, 200)); + QCOMPARE(item3->mapToScene(0, 0), QPointF(300, 300)); + QCOMPARE(item4->mapToScene(0, 0), QPointF(400, 400)); + QCOMPARE(item1->mapToScene(10, 0), QPointF(110, 100)); + QCOMPARE(item2->mapToScene(10, 0), QPointF(210, 200)); + QCOMPARE(item3->mapToScene(10, 0), QPointF(310, 300)); + QCOMPARE(item4->mapToScene(10, 0), QPointF(410, 400)); + QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0)); + QCOMPARE(item2->mapFromScene(200, 200), QPointF(0, 0)); + QCOMPARE(item3->mapFromScene(300, 300), QPointF(0, 0)); + QCOMPARE(item4->mapFromScene(400, 400), QPointF(0, 0)); + QCOMPARE(item1->mapFromScene(110, 100), QPointF(10, 0)); + QCOMPARE(item2->mapFromScene(210, 200), QPointF(10, 0)); + QCOMPARE(item3->mapFromScene(310, 300), QPointF(10, 0)); + QCOMPARE(item4->mapFromScene(410, 400), QPointF(10, 0)); + + // Rotate item1 90 degrees clockwise + QMatrix matrix; matrix.rotate(90); + item1->setMatrix(matrix); + QCOMPARE(item1->pos(), item1->mapToParent(0, 0)); + QCOMPARE(item2->pos(), item2->mapToParent(0, 0)); + QCOMPARE(item3->pos(), item3->mapToParent(0, 0)); + QCOMPARE(item4->pos(), item4->mapToParent(0, 0)); + QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100)); + QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 300)); + QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 400)); + QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToScene(10, 0), QPointF(0, 210)); + QCOMPARE(item3->mapToScene(10, 0), QPointF(-100, 310)); + QCOMPARE(item4->mapToScene(10, 0), QPointF(-200, 410)); + QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0)); + QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0)); + QCOMPARE(item3->mapFromScene(-100, 300), QPointF(0, 0)); + QCOMPARE(item4->mapFromScene(-200, 400), QPointF(0, 0)); + QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0)); + QCOMPARE(item2->mapFromScene(0, 210), QPointF(10, 0)); + QCOMPARE(item3->mapFromScene(-100, 310), QPointF(10, 0)); + QCOMPARE(item4->mapFromScene(-200, 410), QPointF(10, 0)); + + // Rotate item2 90 degrees clockwise + item2->setMatrix(matrix); + QCOMPARE(item1->pos(), item1->mapToParent(0, 0)); + QCOMPARE(item2->pos(), item2->mapToParent(0, 0)); + QCOMPARE(item3->pos(), item3->mapToParent(0, 0)); + QCOMPARE(item4->pos(), item4->mapToParent(0, 0)); + QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110)); + QCOMPARE(item3->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100)); + QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapToScene(0, 0), QPointF(-100, 100)); + QCOMPARE(item4->mapToScene(0, 0), QPointF(-200, 0)); + QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200)); + QCOMPARE(item3->mapToScene(10, 0), QPointF(-110, 100)); + QCOMPARE(item4->mapToScene(10, 0), QPointF(-210, 0)); + QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0)); + QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0)); + QCOMPARE(item3->mapFromScene(-100, 100), QPointF(0, 0)); + QCOMPARE(item4->mapFromScene(-200, 0), QPointF(0, 0)); + QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0)); + QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0)); + QCOMPARE(item3->mapFromScene(-110, 100), QPointF(10, 0)); + QCOMPARE(item4->mapFromScene(-210, 0), QPointF(10, 0)); + + // Translate item3 50 points, then rotate 90 degrees counterclockwise + QMatrix matrix2; + matrix2.translate(50, 0); + matrix2.rotate(-90); + item3->setMatrix(matrix2); + QCOMPARE(item1->pos(), item1->mapToParent(0, 0)); + QCOMPARE(item2->pos(), item2->mapToParent(0, 0)); + QCOMPARE(item3->pos(), item3->mapToParent(0, 0) - QPointF(50, 0)); + QCOMPARE(item4->pos(), item4->mapToParent(0, 0)); + QCOMPARE(item1->mapToParent(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToParent(10, 0), QPointF(100, 110)); + QCOMPARE(item3->mapToParent(10, 0), QPointF(150, 90)); + QCOMPARE(item4->mapToParent(10, 0), QPointF(110, 100)); + QCOMPARE(item1->mapToScene(0, 0), QPointF(100, 100)); + QCOMPARE(item2->mapToScene(0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapToScene(0, 0), QPointF(-150, 100)); + QCOMPARE(item4->mapToScene(0, 0), QPointF(-250, 200)); + QCOMPARE(item1->mapToScene(10, 0), QPointF(100, 110)); + QCOMPARE(item2->mapToScene(10, 0), QPointF(-10, 200)); + QCOMPARE(item3->mapToScene(10, 0), QPointF(-150, 110)); + QCOMPARE(item4->mapToScene(10, 0), QPointF(-250, 210)); + QCOMPARE(item1->mapFromScene(100, 100), QPointF(0, 0)); + QCOMPARE(item2->mapFromScene(0, 200), QPointF(0, 0)); + QCOMPARE(item3->mapFromScene(-150, 100), QPointF(0, 0)); + QCOMPARE(item4->mapFromScene(-250, 200), QPointF(0, 0)); + QCOMPARE(item1->mapFromScene(100, 110), QPointF(10, 0)); + QCOMPARE(item2->mapFromScene(-10, 200), QPointF(10, 0)); + QCOMPARE(item3->mapFromScene(-150, 110), QPointF(10, 0)); + QCOMPARE(item4->mapFromScene(-250, 210), QPointF(10, 0)); + + delete item1; +} + +void tst_QGraphicsItem::mapFromToItem() +{ + QGraphicsItem *item1 = new QGraphicsPathItem; + QGraphicsItem *item2 = new QGraphicsPathItem; + QGraphicsItem *item3 = new QGraphicsPathItem; + QGraphicsItem *item4 = new QGraphicsPathItem; + + item1->setPos(-100, -100); + item2->setPos(100, -100); + item3->setPos(100, 100); + item4->setPos(-100, 100); + + QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(200, 0)); + QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0)); + QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(0, -200)); + QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(0, 200)); + QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-200, 0)); + QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200)); + QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0)); + + QMatrix matrix; + matrix.translate(100, 100); + item1->setMatrix(matrix); + + QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(100, -100)); + QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(-200, 0)); + QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(100, -100)); + QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(-100, 100)); + QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(-100, 100)); + QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(0, -200)); + QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(200, 0)); + + matrix.rotate(90); + item1->setMatrix(matrix); + item2->setMatrix(matrix); + item3->setMatrix(matrix); + item4->setMatrix(matrix); + + QCOMPARE(item1->mapFromItem(item2, 0, 0), QPointF(0, -200)); + QCOMPARE(item2->mapFromItem(item3, 0, 0), QPointF(200, 0)); + QCOMPARE(item3->mapFromItem(item4, 0, 0), QPointF(0, 200)); + QCOMPARE(item4->mapFromItem(item1, 0, 0), QPointF(-200, 0)); + QCOMPARE(item1->mapFromItem(item4, 0, 0), QPointF(200, 0)); + QCOMPARE(item2->mapFromItem(item1, 0, 0), QPointF(0, 200)); + QCOMPARE(item3->mapFromItem(item2, 0, 0), QPointF(-200, 0)); + QCOMPARE(item4->mapFromItem(item3, 0, 0), QPointF(0, -200)); + QCOMPARE(item1->mapFromItem(item2, 10, -5), QPointF(10, -205)); + QCOMPARE(item2->mapFromItem(item3, 10, -5), QPointF(210, -5)); + QCOMPARE(item3->mapFromItem(item4, 10, -5), QPointF(10, 195)); + QCOMPARE(item4->mapFromItem(item1, 10, -5), QPointF(-190, -5)); + QCOMPARE(item1->mapFromItem(item4, 10, -5), QPointF(210, -5)); + QCOMPARE(item2->mapFromItem(item1, 10, -5), QPointF(10, 195)); + QCOMPARE(item3->mapFromItem(item2, 10, -5), QPointF(-190, -5)); + QCOMPARE(item4->mapFromItem(item3, 10, -5), QPointF(10, -205)); + + QCOMPARE(item1->mapFromItem(0, 10, -5), item1->mapFromScene(10, -5)); + QCOMPARE(item2->mapFromItem(0, 10, -5), item2->mapFromScene(10, -5)); + QCOMPARE(item3->mapFromItem(0, 10, -5), item3->mapFromScene(10, -5)); + QCOMPARE(item4->mapFromItem(0, 10, -5), item4->mapFromScene(10, -5)); + QCOMPARE(item1->mapToItem(0, 10, -5), item1->mapToScene(10, -5)); + QCOMPARE(item2->mapToItem(0, 10, -5), item2->mapToScene(10, -5)); + QCOMPARE(item3->mapToItem(0, 10, -5), item3->mapToScene(10, -5)); + QCOMPARE(item4->mapToItem(0, 10, -5), item4->mapToScene(10, -5)); + + delete item1; + delete item2; + delete item3; + delete item4; +} + +void tst_QGraphicsItem::mapRectFromToParent_data() +{ + QTest::addColumn("parent"); + QTest::addColumn("parentPos"); + QTest::addColumn("parentTransform"); + QTest::addColumn("pos"); + QTest::addColumn("transform"); + QTest::addColumn("inputRect"); + QTest::addColumn("outputRect"); + + QTest::newRow("nil") << false << QPointF() << QTransform() << QPointF() << QTransform() << QRectF() << QRectF(); + QTest::newRow("simple") << false << QPointF() << QTransform() << QPointF() << QTransform() + << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10); + QTest::newRow("simple w/parent") << true + << QPointF() << QTransform() + << QPointF() << QTransform() + << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10); + QTest::newRow("simple w/parent parentPos") << true + << QPointF(50, 50) << QTransform() + << QPointF() << QTransform() + << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10); + QTest::newRow("simple w/parent parentPos parentRotation") << true + << QPointF(50, 50) << QTransform().rotate(45) + << QPointF() << QTransform() + << QRectF(0, 0, 10, 10) << QRectF(0, 0, 10, 10); + QTest::newRow("pos w/parent") << true + << QPointF() << QTransform() + << QPointF(50, 50) << QTransform() + << QRectF(0, 0, 10, 10) << QRectF(50, 50, 10, 10); + QTest::newRow("rotation w/parent") << true + << QPointF() << QTransform() + << QPointF() << QTransform().rotate(90) + << QRectF(0, 0, 10, 10) << QRectF(-10, 0, 10, 10); + QTest::newRow("pos rotation w/parent") << true + << QPointF() << QTransform() + << QPointF(50, 50) << QTransform().rotate(90) + << QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10); + QTest::newRow("pos rotation w/parent parentPos parentRotation") << true + << QPointF(-170, -190) << QTransform().rotate(90) + << QPointF(50, 50) << QTransform().rotate(90) + << QRectF(0, 0, 10, 10) << QRectF(40, 50, 10, 10); +} + +void tst_QGraphicsItem::mapRectFromToParent() +{ + QFETCH(bool, parent); + QFETCH(QPointF, parentPos); + QFETCH(QTransform, parentTransform); + QFETCH(QPointF, pos); + QFETCH(QTransform, transform); + QFETCH(QRectF, inputRect); + QFETCH(QRectF, outputRect); + + QGraphicsRectItem *rect = new QGraphicsRectItem; + rect->setPos(pos); + rect->setTransform(transform); + + if (parent) { + QGraphicsRectItem *rectParent = new QGraphicsRectItem; + rect->setParentItem(rectParent); + rectParent->setPos(parentPos); + rectParent->setTransform(parentTransform); + } + + // Make sure we use non-destructive transform operations (e.g., 90 degree + // rotations). + QCOMPARE(rect->mapRectToParent(inputRect), outputRect); + QCOMPARE(rect->mapRectFromParent(outputRect), inputRect); + QCOMPARE(rect->itemTransform(rect->parentItem()).mapRect(inputRect), outputRect); + QCOMPARE(rect->mapToParent(inputRect).boundingRect(), outputRect); + QCOMPARE(rect->mapToParent(QPolygonF(inputRect)).boundingRect(), outputRect); + QCOMPARE(rect->mapFromParent(outputRect).boundingRect(), inputRect); + QCOMPARE(rect->mapFromParent(QPolygonF(outputRect)).boundingRect(), inputRect); + QPainterPath inputPath; + inputPath.addRect(inputRect); + QPainterPath outputPath; + outputPath.addRect(outputRect); + QCOMPARE(rect->mapToParent(inputPath).boundingRect(), outputPath.boundingRect()); + QCOMPARE(rect->mapFromParent(outputPath).boundingRect(), inputPath.boundingRect()); +} + +void tst_QGraphicsItem::isAncestorOf() +{ + QGraphicsItem *grandPa = new QGraphicsRectItem; + QGraphicsItem *parent = new QGraphicsRectItem; + QGraphicsItem *child = new QGraphicsRectItem; + + QVERIFY(!parent->isAncestorOf(0)); + QVERIFY(!child->isAncestorOf(0)); + QVERIFY(!parent->isAncestorOf(child)); + QVERIFY(!child->isAncestorOf(parent)); + QVERIFY(!parent->isAncestorOf(parent)); + + child->setParentItem(parent); + parent->setParentItem(grandPa); + + QVERIFY(parent->isAncestorOf(child)); + QVERIFY(grandPa->isAncestorOf(parent)); + QVERIFY(grandPa->isAncestorOf(child)); + QVERIFY(!child->isAncestorOf(parent)); + QVERIFY(!parent->isAncestorOf(grandPa)); + QVERIFY(!child->isAncestorOf(grandPa)); + QVERIFY(!child->isAncestorOf(child)); + QVERIFY(!parent->isAncestorOf(parent)); + QVERIFY(!grandPa->isAncestorOf(grandPa)); + + parent->setParentItem(0); + + delete child; + delete parent; + delete grandPa; +} + +void tst_QGraphicsItem::commonAncestorItem() +{ + QGraphicsItem *ancestor = new QGraphicsRectItem; + QGraphicsItem *grandMa = new QGraphicsRectItem; + QGraphicsItem *grandPa = new QGraphicsRectItem; + QGraphicsItem *brotherInLaw = new QGraphicsRectItem; + QGraphicsItem *cousin = new QGraphicsRectItem; + QGraphicsItem *husband = new QGraphicsRectItem; + QGraphicsItem *child = new QGraphicsRectItem; + QGraphicsItem *wife = new QGraphicsRectItem; + + child->setParentItem(husband); + husband->setParentItem(grandPa); + brotherInLaw->setParentItem(grandPa); + cousin->setParentItem(brotherInLaw); + wife->setParentItem(grandMa); + grandMa->setParentItem(ancestor); + grandPa->setParentItem(ancestor); + + QCOMPARE(grandMa->commonAncestorItem(grandMa), grandMa); + QCOMPARE(grandMa->commonAncestorItem(0), (QGraphicsItem *)0); + QCOMPARE(grandMa->commonAncestorItem(grandPa), ancestor); + QCOMPARE(grandPa->commonAncestorItem(grandMa), ancestor); + QCOMPARE(grandPa->commonAncestorItem(husband), grandPa); + QCOMPARE(grandPa->commonAncestorItem(wife), ancestor); + QCOMPARE(grandMa->commonAncestorItem(husband), ancestor); + QCOMPARE(grandMa->commonAncestorItem(wife), grandMa); + QCOMPARE(wife->commonAncestorItem(grandMa), grandMa); + QCOMPARE(child->commonAncestorItem(cousin), grandPa); + QCOMPARE(cousin->commonAncestorItem(child), grandPa); + QCOMPARE(wife->commonAncestorItem(child), ancestor); + QCOMPARE(child->commonAncestorItem(wife), ancestor); +} + +void tst_QGraphicsItem::data() +{ + QGraphicsTextItem text; + + QCOMPARE(text.data(0), QVariant()); + text.setData(0, "TextItem"); + QCOMPARE(text.data(0), QVariant(QString("TextItem"))); + text.setData(0, QVariant()); + QCOMPARE(text.data(0), QVariant()); +} + +void tst_QGraphicsItem::type() +{ + QCOMPARE(int(QGraphicsItem::Type), 1); + QCOMPARE(int(QGraphicsPathItem::Type), 2); + QCOMPARE(int(QGraphicsRectItem::Type), 3); + QCOMPARE(int(QGraphicsEllipseItem::Type), 4); + QCOMPARE(int(QGraphicsPolygonItem::Type), 5); + QCOMPARE(int(QGraphicsLineItem::Type), 6); + QCOMPARE(int(QGraphicsPixmapItem::Type), 7); + QCOMPARE(int(QGraphicsTextItem::Type), 8); + + QCOMPARE(QGraphicsPathItem().type(), 2); + QCOMPARE(QGraphicsRectItem().type(), 3); + QCOMPARE(QGraphicsEllipseItem().type(), 4); + QCOMPARE(QGraphicsPolygonItem().type(), 5); + QCOMPARE(QGraphicsLineItem().type(), 6); + QCOMPARE(QGraphicsPixmapItem().type(), 7); + QCOMPARE(QGraphicsTextItem().type(), 8); +} + +void tst_QGraphicsItem::graphicsitem_cast() +{ + QGraphicsPathItem pathItem; + const QGraphicsPathItem *pPathItem = &pathItem; + QGraphicsRectItem rectItem; + const QGraphicsRectItem *pRectItem = &rectItem; + QGraphicsEllipseItem ellipseItem; + const QGraphicsEllipseItem *pEllipseItem = &ellipseItem; + QGraphicsPolygonItem polygonItem; + const QGraphicsPolygonItem *pPolygonItem = &polygonItem; + QGraphicsLineItem lineItem; + const QGraphicsLineItem *pLineItem = &lineItem; + QGraphicsPixmapItem pixmapItem; + const QGraphicsPixmapItem *pPixmapItem = &pixmapItem; + QGraphicsTextItem textItem; + const QGraphicsTextItem *pTextItem = &textItem; + + QVERIFY(qgraphicsitem_cast(&pathItem)); + //QVERIFY(qgraphicsitem_cast(&pathItem)); + QVERIFY(qgraphicsitem_cast(&pathItem)); + QVERIFY(qgraphicsitem_cast(pPathItem)); + QVERIFY(qgraphicsitem_cast(pPathItem)); + + QVERIFY(qgraphicsitem_cast(&rectItem)); + QVERIFY(qgraphicsitem_cast(&rectItem)); + QVERIFY(qgraphicsitem_cast(pRectItem)); + QVERIFY(qgraphicsitem_cast(pRectItem)); + + QVERIFY(qgraphicsitem_cast(&ellipseItem)); + QVERIFY(qgraphicsitem_cast(&ellipseItem)); + QVERIFY(qgraphicsitem_cast(pEllipseItem)); + QVERIFY(qgraphicsitem_cast(pEllipseItem)); + + QVERIFY(qgraphicsitem_cast(&polygonItem)); + //QVERIFY(qgraphicsitem_cast(&polygonItem)); + QVERIFY(qgraphicsitem_cast(&polygonItem)); + QVERIFY(qgraphicsitem_cast(pPolygonItem)); + QVERIFY(qgraphicsitem_cast(pPolygonItem)); + + QVERIFY(qgraphicsitem_cast(&lineItem)); + QVERIFY(qgraphicsitem_cast(&lineItem)); + QVERIFY(qgraphicsitem_cast(pLineItem)); + QVERIFY(qgraphicsitem_cast(pLineItem)); + + QVERIFY(qgraphicsitem_cast(&pixmapItem)); + QVERIFY(qgraphicsitem_cast(&pixmapItem)); + QVERIFY(qgraphicsitem_cast(pPixmapItem)); + QVERIFY(qgraphicsitem_cast(pPixmapItem)); + + QVERIFY(qgraphicsitem_cast(&textItem)); + QVERIFY(qgraphicsitem_cast(&textItem)); + QVERIFY(qgraphicsitem_cast(pTextItem)); + QVERIFY(qgraphicsitem_cast(pTextItem)); + + // and some casts that _should_ fail: + QVERIFY(!qgraphicsitem_cast(&pathItem)); + QVERIFY(!qgraphicsitem_cast(pPolygonItem)); + + // and this shouldn't crash + QGraphicsItem *ptr = 0; + QVERIFY(!qgraphicsitem_cast(ptr)); + QVERIFY(!qgraphicsitem_cast(ptr)); +} + +void tst_QGraphicsItem::hoverEventsGenerateRepaints() +{ + Q_CHECK_PAINTEVENTS + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(150); + + EventTester *tester = new EventTester; + scene.addItem(tester); + tester->setAcceptsHoverEvents(true); + + QTRY_COMPARE(tester->repaints, 1); + + // Send a hover enter event + QGraphicsSceneHoverEvent hoverEnterEvent(QEvent::GraphicsSceneHoverEnter); + hoverEnterEvent.setScenePos(QPointF(0, 0)); + hoverEnterEvent.setPos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &hoverEnterEvent); + + // Check that we get a repaint + int npaints = tester->repaints; + qApp->processEvents(); + qApp->processEvents(); + QCOMPARE(tester->events.size(), 2); // enter + move + QCOMPARE(tester->repaints, npaints + 1); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); + + // Send a hover move event + QGraphicsSceneHoverEvent hoverMoveEvent(QEvent::GraphicsSceneHoverMove); + hoverMoveEvent.setScenePos(QPointF(0, 0)); + hoverMoveEvent.setPos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &hoverMoveEvent); + + // Check that we don't get a repaint + qApp->processEvents(); + qApp->processEvents(); + + QCOMPARE(tester->events.size(), 3); + QCOMPARE(tester->repaints, npaints + 1); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); + + // Send a hover leave event + QGraphicsSceneHoverEvent hoverLeaveEvent(QEvent::GraphicsSceneHoverLeave); + hoverLeaveEvent.setScenePos(QPointF(-100, -100)); + hoverLeaveEvent.setPos(QPointF(0, 0)); + QApplication::sendEvent(&scene, &hoverLeaveEvent); + + // Check that we get a repaint + qApp->processEvents(); + qApp->processEvents(); + + QCOMPARE(tester->events.size(), 4); + QCOMPARE(tester->repaints, npaints + 2); + QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverLeave); +} + +void tst_QGraphicsItem::boundingRects_data() +{ + QTest::addColumn("item"); + QTest::addColumn("boundingRect"); + + QRectF rect(0, 0, 100, 100); + QPainterPath path; + path.addRect(rect); + + QRectF adjustedRect(-0.5, -0.5, 101, 101); + + QTest::newRow("path") << (QGraphicsItem *)new QGraphicsPathItem(path) << adjustedRect; + QTest::newRow("rect") << (QGraphicsItem *)new QGraphicsRectItem(rect) << adjustedRect; + QTest::newRow("ellipse") << (QGraphicsItem *)new QGraphicsEllipseItem(rect) << adjustedRect; + QTest::newRow("polygon") << (QGraphicsItem *)new QGraphicsPolygonItem(rect) << adjustedRect; +} + +void tst_QGraphicsItem::boundingRects() +{ + QFETCH(QGraphicsItem *, item); + QFETCH(QRectF, boundingRect); + + ((QAbstractGraphicsShapeItem *)item)->setPen(QPen(Qt::black, 1)); + QCOMPARE(item->boundingRect(), boundingRect); +} + +void tst_QGraphicsItem::boundingRects2() +{ + QGraphicsPixmapItem pixmap(QPixmap::fromImage(QImage(100, 100, QImage::Format_ARGB32_Premultiplied))); + QCOMPARE(pixmap.boundingRect(), QRectF(0, 0, 100, 100)); + + QGraphicsLineItem line(0, 0, 100, 0); + line.setPen(QPen(Qt::black, 1)); + QCOMPARE(line.boundingRect(), QRectF(-0.5, -0.5, 101, 1)); +} + +void tst_QGraphicsItem::sceneBoundingRect() +{ + QGraphicsScene scene; + QGraphicsRectItem *item = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0)); + item->setPos(100, 100); + + QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100)); + QCOMPARE(item->sceneBoundingRect(), QRectF(100, 100, 100, 100)); + + item->rotate(90); + + QCOMPARE(item->boundingRect(), QRectF(0, 0, 100, 100)); + QCOMPARE(item->sceneBoundingRect(), QRectF(0, 100, 100, 100)); +} + +void tst_QGraphicsItem::childrenBoundingRect() +{ + QGraphicsScene scene; + QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0)); + QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0)); + child->setParentItem(parent); + parent->setPos(100, 100); + child->setPos(100, 100); + + QCOMPARE(parent->boundingRect(), QRectF(0, 0, 100, 100)); + QCOMPARE(child->boundingRect(), QRectF(0, 0, 100, 100)); + QCOMPARE(child->childrenBoundingRect(), QRectF()); + QCOMPARE(parent->childrenBoundingRect(), QRectF(100, 100, 100, 100)); + + QGraphicsRectItem *child2 = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0)); + child2->setParentItem(parent); + child2->setPos(-100, -100); + QCOMPARE(parent->childrenBoundingRect(), QRectF(-100, -100, 300, 300)); + + QGraphicsRectItem *childChild = scene.addRect(QRectF(0, 0, 100, 100), QPen(Qt::black, 0)); + childChild->setParentItem(child); + childChild->setPos(500, 500); + child->rotate(90); + + scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));; + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + QTest::qWait(30); + + QCOMPARE(parent->childrenBoundingRect(), QRectF(-500, -100, 600, 800)); +} + +void tst_QGraphicsItem::childrenBoundingRectTransformed() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); + + rect->rotate(45); + rect2->rotate(-45); + rect3->rotate(45); + rect4->rotate(-45); + rect5->rotate(45); + + subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(rect->childrenBoundingRect(), QRectF(-100, 75, 275, 250)); +} + +void tst_QGraphicsItem::childrenBoundingRect2() +{ + QGraphicsItemGroup box; + QGraphicsLineItem l1(0, 0, 100, 0, &box); + QGraphicsLineItem l2(100, 0, 100, 100, &box); + QGraphicsLineItem l3(0, 0, 0, 100, &box); + // Make sure lines (zero with/height) are included in the childrenBoundingRect. + QCOMPARE(box.childrenBoundingRect(), QRectF(0, 0, 100, 100)); +} + +void tst_QGraphicsItem::childrenBoundingRect3() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect4 = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *rect5 = scene.addRect(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect); + rect3->setParentItem(rect2); + rect4->setParentItem(rect3); + rect5->setParentItem(rect4); + + rect2->setTransform(QTransform().translate(50, 50).rotate(45)); + rect2->setPos(25, 25); + rect3->setTransform(QTransform().translate(50, 50).rotate(45)); + rect3->setPos(25, 25); + rect4->setTransform(QTransform().translate(50, 50).rotate(45)); + rect4->setPos(25, 25); + rect5->setTransform(QTransform().translate(50, 50).rotate(45)); + rect5->setPos(25, 25); + + // Try to mess up the cached bounding rect. + (void)rect2->childrenBoundingRect(); + + QRectF subTreeRect = rect->childrenBoundingRect(); + QCOMPARE(subTreeRect.left(), qreal(-206.0660171779821)); + QCOMPARE(subTreeRect.top(), qreal(75.0)); + QCOMPARE(subTreeRect.width(), qreal(351.7766952966369)); + QCOMPARE(subTreeRect.height(), qreal(251.7766952966369)); +} + +void tst_QGraphicsItem::childrenBoundingRect4() +{ + QGraphicsScene scene; + + QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 10, 10)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(0, 0, 20, 20)); + QGraphicsRectItem *rect3 = scene.addRect(QRectF(0, 0, 30, 30)); + rect2->setParentItem(rect); + rect3->setParentItem(rect); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + + // Try to mess up the cached bounding rect. + rect->childrenBoundingRect(); + rect2->childrenBoundingRect(); + + rect3->setOpacity(0.0); + rect3->setParentItem(rect2); + + QCOMPARE(rect->childrenBoundingRect(), rect3->boundingRect()); + QCOMPARE(rect2->childrenBoundingRect(), rect3->boundingRect()); +} + +void tst_QGraphicsItem::childrenBoundingRect5() +{ + QGraphicsScene scene; + + QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 100, 100)); + child->setParentItem(parent); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + + // Try to mess up the cached bounding rect. + QRectF expectedChildrenBoundingRect = parent->boundingRect(); + QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect); + + // Apply some effects. + QGraphicsDropShadowEffect *dropShadow = new QGraphicsDropShadowEffect; + dropShadow->setOffset(25, 25); + child->setGraphicsEffect(dropShadow); + parent->setGraphicsEffect(new QGraphicsOpacityEffect); + + QVERIFY(parent->childrenBoundingRect() != expectedChildrenBoundingRect); + expectedChildrenBoundingRect |= dropShadow->boundingRect(); + QCOMPARE(parent->childrenBoundingRect(), expectedChildrenBoundingRect); +} + +void tst_QGraphicsItem::group() +{ + QGraphicsScene scene; + QGraphicsRectItem *parent = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::green)); + QGraphicsRectItem *child = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::blue)); + QGraphicsRectItem *parent2 = scene.addRect(QRectF(0, 0, 50, 50), QPen(Qt::black, 0), QBrush(Qt::red)); + parent2->setPos(-50, 50); + child->rotate(45); + child->setParentItem(parent); + parent->setPos(25, 25); + child->setPos(25, 25); + + QCOMPARE(parent->group(), (QGraphicsItemGroup *)0); + QCOMPARE(parent2->group(), (QGraphicsItemGroup *)0); + QCOMPARE(child->group(), (QGraphicsItemGroup *)0); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QGraphicsItemGroup *group = new QGraphicsItemGroup; + group->setSelected(true); + scene.addItem(group); + + QRectF parentSceneBoundingRect = parent->sceneBoundingRect(); + group->addToGroup(parent); + QCOMPARE(parent->group(), group); + QCOMPARE(parent->sceneBoundingRect(), parentSceneBoundingRect); + + QCOMPARE(parent->parentItem(), (QGraphicsItem *)group); + QCOMPARE(group->children().size(), 1); + QCOMPARE(scene.items().size(), 4); + QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 3); + + QTest::qWait(25); + + QRectF parent2SceneBoundingRect = parent2->sceneBoundingRect(); + group->addToGroup(parent2); + QCOMPARE(parent2->group(), group); + QCOMPARE(parent2->sceneBoundingRect(), parent2SceneBoundingRect); + + QCOMPARE(parent2->parentItem(), (QGraphicsItem *)group); + QCOMPARE(group->children().size(), 2); + QCOMPARE(scene.items().size(), 4); + QCOMPARE(scene.items(group->sceneBoundingRect()).size(), 4); + + QTest::qWait(25); + + QList newItems; + for (int i = 0; i < 100; ++i) { + QGraphicsItem *item = scene.addRect(QRectF(-25, -25, 50, 50), QPen(Qt::black, 0), + QBrush(QColor(rand() % 255, rand() % 255, + rand() % 255, rand() % 255))); + newItems << item; + item->setPos(-1000 + rand() % 2000, + -1000 + rand() % 2000); + item->rotate(rand() % 90); + } + + view.fitInView(scene.itemsBoundingRect()); + + int n = 0; + foreach (QGraphicsItem *item, newItems) { + group->addToGroup(item); + QCOMPARE(item->group(), group); + if ((n++ % 100) == 0) + QTest::qWait(10); + } +} + +void tst_QGraphicsItem::setGroup() +{ + QGraphicsItemGroup group1; + QGraphicsItemGroup group2; + + QGraphicsRectItem *rect = new QGraphicsRectItem; + QCOMPARE(rect->group(), (QGraphicsItemGroup *)0); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)0); + rect->setGroup(&group1); + QCOMPARE(rect->group(), &group1); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)&group1); + rect->setGroup(&group2); + QCOMPARE(rect->group(), &group2); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)&group2); + rect->setGroup(0); + QCOMPARE(rect->group(), (QGraphicsItemGroup *)0); + QCOMPARE(rect->parentItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::setGroup2() +{ + QGraphicsScene scene; + QGraphicsItemGroup group; + scene.addItem(&group); + + QGraphicsRectItem *rect = scene.addRect(50,50,50,50,Qt::NoPen,Qt::black); + rect->setTransformOriginPoint(50,50); + rect->setRotation(45); + rect->setScale(1.5); + rect->translate(20,20); + group.translate(-30,-40); + group.setRotation(180); + group.setScale(0.5); + + QTransform oldSceneTransform = rect->sceneTransform(); + rect->setGroup(&group); + QCOMPARE(rect->sceneTransform(), oldSceneTransform); + + group.setRotation(20); + group.setScale(2); + rect->setRotation(90); + rect->setScale(0.8); + + oldSceneTransform = rect->sceneTransform(); + rect->setGroup(0); + QCOMPARE(rect->sceneTransform(), oldSceneTransform); +} + +void tst_QGraphicsItem::nestedGroups() +{ + QGraphicsItemGroup *group1 = new QGraphicsItemGroup; + QGraphicsItemGroup *group2 = new QGraphicsItemGroup; + + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect2->setParentItem(rect); + + group1->addToGroup(rect); + QCOMPARE(rect->group(), group1); + QCOMPARE(rect2->group(), group1); + + group2->addToGroup(group1); + QCOMPARE(rect->group(), group1); + QCOMPARE(rect2->group(), group1); + QCOMPARE(group1->group(), group2); + QCOMPARE(group2->group(), (QGraphicsItemGroup *)0); + + QGraphicsScene scene; + scene.addItem(group1); + + QCOMPARE(rect->group(), group1); + QCOMPARE(rect2->group(), group1); + QCOMPARE(group1->group(), (QGraphicsItemGroup *)0); + QVERIFY(group2->children().isEmpty()); + + delete group2; +} + +void tst_QGraphicsItem::warpChildrenIntoGroup() +{ + QGraphicsScene scene; + QGraphicsRectItem *parentRectItem = scene.addRect(QRectF(0, 0, 100, 100)); + QGraphicsRectItem *childRectItem = scene.addRect(QRectF(0, 0, 100, 100)); + parentRectItem->rotate(90); + childRectItem->setPos(-50, -25); + childRectItem->setParentItem(parentRectItem); + + QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0)); + QCOMPARE(childRectItem->scenePos(), QPointF(25, -50)); + + QGraphicsRectItem *parentOfGroup = scene.addRect(QRectF(0, 0, 100, 100)); + parentOfGroup->setPos(-200, -200); + parentOfGroup->scale(2, 2); + + QGraphicsItemGroup *group = new QGraphicsItemGroup; + group->setPos(50, 50); + group->setParentItem(parentOfGroup); + + QCOMPARE(group->scenePos(), QPointF(-100, -100)); + + group->addToGroup(childRectItem); + + QCOMPARE(childRectItem->mapToScene(50, 0), QPointF(25, 0)); + QCOMPARE(childRectItem->scenePos(), QPointF(25, -50)); +} + +void tst_QGraphicsItem::removeFromGroup() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = scene.addRect(QRectF(-100, -100, 200, 200)); + QGraphicsRectItem *rect2 = scene.addRect(QRectF(100, 100, 200, 200)); + rect1->setFlag(QGraphicsItem::ItemIsSelectable); + rect2->setFlag(QGraphicsItem::ItemIsSelectable); + rect1->setSelected(true); + rect2->setSelected(true); + + QGraphicsView view(&scene); + view.show(); + + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + QGraphicsItemGroup *group = scene.createItemGroup(scene.selectedItems()); + QVERIFY(group); + QCOMPARE(group->children().size(), 2); + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + scene.destroyItemGroup(group); // calls removeFromGroup. + + qApp->processEvents(); // index items + qApp->processEvents(); // emit changed + + QCOMPARE(scene.items().size(), 2); + QVERIFY(!rect1->group()); + QVERIFY(!rect2->group()); +} + +class ChildEventTester : public QGraphicsRectItem +{ +public: + ChildEventTester(const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent), counter(0) + { } + + int counter; + +protected: + void focusInEvent(QFocusEvent *event) + { ++counter; QGraphicsRectItem::focusInEvent(event); } + void mousePressEvent(QGraphicsSceneMouseEvent *) + { ++counter; } + void mouseMoveEvent(QGraphicsSceneMouseEvent *) + { ++counter; } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { ++counter; } +}; + +void tst_QGraphicsItem::handlesChildEvents() +{ + ChildEventTester *blue = new ChildEventTester(QRectF(0, 0, 100, 100)); + ChildEventTester *red = new ChildEventTester(QRectF(0, 0, 50, 50)); + ChildEventTester *green = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *gray = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *yellow = new ChildEventTester(QRectF(0, 0, 50, 50)); + + blue->setBrush(QBrush(Qt::blue)); + red->setBrush(QBrush(Qt::red)); + yellow->setBrush(QBrush(Qt::yellow)); + green->setBrush(QBrush(Qt::green)); + gray->setBrush(QBrush(Qt::gray)); + red->setPos(50, 0); + yellow->setPos(50, 50); + green->setPos(25, 0); + gray->setPos(25, 25); + red->setParentItem(blue); + yellow->setParentItem(blue); + green->setParentItem(red); + gray->setParentItem(red); + + QGraphicsScene scene; + scene.addItem(blue); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + // Pull out the items, closest item first + QList items = scene.items(scene.itemsBoundingRect()); + QCOMPARE(items.at(0), (QGraphicsItem *)yellow); + QCOMPARE(items.at(1), (QGraphicsItem *)gray); + QCOMPARE(items.at(2), (QGraphicsItem *)green); + QCOMPARE(items.at(3), (QGraphicsItem *)red); + QCOMPARE(items.at(4), (QGraphicsItem *)blue); + + QCOMPARE(blue->counter, 0); + + // Send events to the toplevel item + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(blue->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(blue->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 2); + + // Send events to a level1 item + pressEvent.setScenePos(red->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(red->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); + + // Send events to a level2 item + pressEvent.setScenePos(green->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(green->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); + + blue->setHandlesChildEvents(true); + + // Send events to a level1 item + pressEvent.setScenePos(red->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(red->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 4); + QCOMPARE(red->counter, 2); + + // Send events to a level2 item + pressEvent.setScenePos(green->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(green->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); + + blue->setHandlesChildEvents(false); + + // Send events to a level1 item + pressEvent.setScenePos(red->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(red->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); + + // Send events to a level2 item + pressEvent.setScenePos(green->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setScenePos(green->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); + QCOMPARE(green->counter, 4); +} + +void tst_QGraphicsItem::handlesChildEvents2() +{ + ChildEventTester *root = new ChildEventTester(QRectF(0, 0, 10, 10)); + root->setHandlesChildEvents(true); + QVERIFY(root->handlesChildEvents()); + + ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root); + QVERIFY(!child->handlesChildEvents()); + + ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10)); + ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2); + ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3); + child2->setParentItem(root); + QVERIFY(!child2->handlesChildEvents()); + QVERIFY(!child3->handlesChildEvents()); + QVERIFY(!child4->handlesChildEvents()); + + QGraphicsScene scene; + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5), + view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + + QTRY_COMPARE(root->counter, 1); +} + +void tst_QGraphicsItem::handlesChildEvents3() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + ChildEventTester *group2 = new ChildEventTester(QRectF(), 0); + ChildEventTester *group1 = new ChildEventTester(QRectF(), group2); + ChildEventTester *leaf = new ChildEventTester(QRectF(), group1); + scene.addItem(group2); + + leaf->setFlag(QGraphicsItem::ItemIsFocusable); + group1->setFlag(QGraphicsItem::ItemIsFocusable); + group1->setHandlesChildEvents(true); + group2->setFlag(QGraphicsItem::ItemIsFocusable); + group2->setHandlesChildEvents(true); + + leaf->setFocus(); + QVERIFY(leaf->hasFocus()); // group2 stole the event, but leaf still got focus + QVERIFY(!group1->hasFocus()); + QVERIFY(!group2->hasFocus()); + QCOMPARE(leaf->counter, 0); + QCOMPARE(group1->counter, 0); + QCOMPARE(group2->counter, 1); + + group1->setFocus(); + QVERIFY(group1->hasFocus()); // group2 stole the event, but group1 still got focus + QVERIFY(!leaf->hasFocus()); + QVERIFY(!group2->hasFocus()); + QCOMPARE(leaf->counter, 0); + QCOMPARE(group1->counter, 0); + QCOMPARE(group2->counter, 2); + + group2->setFocus(); + QVERIFY(group2->hasFocus()); // group2 stole the event, and now group2 also has focus + QVERIFY(!leaf->hasFocus()); + QVERIFY(!group1->hasFocus()); + QCOMPARE(leaf->counter, 0); + QCOMPARE(group1->counter, 0); + QCOMPARE(group2->counter, 3); +} + + +class ChildEventFilterTester : public ChildEventTester +{ +public: + ChildEventFilterTester(const QRectF &rect, QGraphicsItem *parent = 0) + : ChildEventTester(rect, parent), filter(QEvent::None) + { } + + QEvent::Type filter; + +protected: + bool sceneEventFilter(QGraphicsItem *item, QEvent *event) + { + Q_UNUSED(item); + if (event->type() == filter) { + ++counter; + return true; + } + return false; + } +}; + +void tst_QGraphicsItem::filtersChildEvents() +{ + QGraphicsScene scene; + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + ChildEventFilterTester *filter = new ChildEventFilterTester(QRectF(10, 10, 10, 10), root); + ChildEventTester *child = new ChildEventTester(QRectF(20, 20, 10, 10), filter); + + // setup filter + filter->setFiltersChildEvents(true); + filter->filter = QEvent::GraphicsSceneMousePress; + + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + + // send event to child + pressEvent.setButton(Qt::LeftButton); + pressEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setScenePos(QPointF(25, 25));//child->mapToScene(5, 5)); + releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QTRY_COMPARE(child->counter, 1); // mouse release is not filtered + QCOMPARE(filter->counter, 1); // mouse press is filtered + QCOMPARE(root->counter, 0); + + // add another filter + root->setFiltersChildEvents(true); + root->filter = QEvent::GraphicsSceneMouseRelease; + + // send event to child + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 1); + QCOMPARE(filter->counter, 2); // mouse press is filtered + QCOMPARE(root->counter, 1); // mouse release is filtered + + // reparent to another sub-graph + ChildEventTester *parent = new ChildEventTester(QRectF(10, 10, 10, 10), root); + child->setParentItem(parent); + + // send event to child + QApplication::sendEvent(&scene, &pressEvent); + QApplication::sendEvent(&scene, &releaseEvent); + + QCOMPARE(child->counter, 2); // mouse press is _not_ filtered + QCOMPARE(parent->counter, 0); + QCOMPARE(filter->counter, 2); + QCOMPARE(root->counter, 2); // mouse release is filtered +} + +void tst_QGraphicsItem::filtersChildEvents2() +{ + ChildEventFilterTester *root = new ChildEventFilterTester(QRectF(0, 0, 10, 10)); + root->setFiltersChildEvents(true); + root->filter = QEvent::GraphicsSceneMousePress; + QVERIFY(root->filtersChildEvents()); + + ChildEventTester *child = new ChildEventTester(QRectF(0, 0, 10, 10), root); + QVERIFY(!child->filtersChildEvents()); + + ChildEventTester *child2 = new ChildEventTester(QRectF(0, 0, 10, 10)); + ChildEventTester *child3 = new ChildEventTester(QRectF(0, 0, 10, 10), child2); + ChildEventTester *child4 = new ChildEventTester(QRectF(0, 0, 10, 10), child3); + + child2->setParentItem(root); + QVERIFY(!child2->filtersChildEvents()); + QVERIFY(!child3->filtersChildEvents()); + QVERIFY(!child4->filtersChildEvents()); + + QGraphicsScene scene; + scene.addItem(root); + + QGraphicsView view(&scene); + view.show(); + + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); + + QMouseEvent event(QEvent::MouseButtonPress, view.mapFromScene(5, 5), + view.viewport()->mapToGlobal(view.mapFromScene(5, 5)), Qt::LeftButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + + QTRY_COMPARE(root->counter, 1); + QCOMPARE(child->counter, 0); + QCOMPARE(child2->counter, 0); + QCOMPARE(child3->counter, 0); + QCOMPARE(child4->counter, 0); +} + +class CustomItem : public QGraphicsItem +{ +public: + QRectF boundingRect() const + { return QRectF(-110, -110, 220, 220); } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + for (int x = -100; x <= 100; x += 25) + painter->drawLine(x, -100, x, 100); + for (int y = -100; y <= 100; y += 25) + painter->drawLine(-100, y, 100, y); + + QFont font = painter->font(); + font.setPointSize(4); + painter->setFont(font); + for (int x = -100; x < 100; x += 25) { + for (int y = -100; y < 100; y += 25) { + painter->drawText(QRectF(x, y, 25, 25), Qt::AlignCenter, QString("%1x%2").arg(x).arg(y)); + } + } + } +}; + +void tst_QGraphicsItem::ensureVisible() +{ + QGraphicsScene scene; + scene.setSceneRect(-200, -200, 400, 400); + QGraphicsItem *item = new CustomItem; + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(300, 300); + view.show(); + QTest::qWaitForWindowShown(&view); + + for (int i = 0; i < 25; ++i) { + view.scale(qreal(1.06), qreal(1.06)); + QApplication::processEvents(); + } + + item->ensureVisible(-100, -100, 25, 25); + QTest::qWait(25); + + for (int x = -100; x < 100; x += 25) { + for (int y = -100; y < 100; y += 25) { + int xmargin = rand() % 75; + int ymargin = rand() % 75; + item->ensureVisible(x, y, 25, 25, xmargin, ymargin); + QApplication::processEvents(); + + QPolygonF viewScenePoly; + viewScenePoly << view.mapToScene(view.rect().topLeft()) + << view.mapToScene(view.rect().topRight()) + << view.mapToScene(view.rect().bottomRight()) + << view.mapToScene(view.rect().bottomLeft()); + + QVERIFY(scene.items(viewScenePoly).contains(item)); + + QPainterPath path; + path.addPolygon(viewScenePoly); + QVERIFY(path.contains(item->mapToScene(x + 12, y + 12))); + + QPolygonF viewScenePolyMinusMargins; + viewScenePolyMinusMargins << view.mapToScene(view.rect().topLeft() + QPoint(xmargin, ymargin)) + << view.mapToScene(view.rect().topRight() + QPoint(-xmargin, ymargin)) + << view.mapToScene(view.rect().bottomRight() + QPoint(-xmargin, -ymargin)) + << view.mapToScene(view.rect().bottomLeft() + QPoint(xmargin, -ymargin)); + + QPainterPath path2; + path2.addPolygon(viewScenePolyMinusMargins); + QVERIFY(path2.contains(item->mapToScene(x + 12, y + 12))); + } + } + + item->ensureVisible(100, 100, 25, 25); + QTest::qWait(25); +} + +void tst_QGraphicsItem::cursor() +{ +#ifndef QT_NO_CURSOR + QGraphicsScene scene; + QGraphicsRectItem *item1 = scene.addRect(QRectF(0, 0, 50, 50)); + QGraphicsRectItem *item2 = scene.addRect(QRectF(0, 0, 50, 50)); + item1->setPos(-100, 0); + item2->setPos(50, 0); + + QVERIFY(!item1->hasCursor()); + QVERIFY(!item2->hasCursor()); + + item1->setCursor(Qt::IBeamCursor); + item2->setCursor(Qt::PointingHandCursor); + + QVERIFY(item1->hasCursor()); + QVERIFY(item2->hasCursor()); + + item1->setCursor(QCursor()); + item2->setCursor(QCursor()); + + QVERIFY(item1->hasCursor()); + QVERIFY(item2->hasCursor()); + + item1->unsetCursor(); + item2->unsetCursor(); + + QVERIFY(!item1->hasCursor()); + QVERIFY(!item2->hasCursor()); + + item1->setCursor(Qt::IBeamCursor); + item2->setCursor(Qt::PointingHandCursor); + + QWidget topLevel; + QGraphicsView view(&scene,&topLevel); + view.setFixedSize(200, 100); + topLevel.show(); + QTest::mouseMove(&view, view.rect().center()); + + QTest::qWait(25); + + QCursor cursor = view.viewport()->cursor(); + + { + QMouseEvent event(QEvent::MouseMove, QPoint(100, 50), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), cursor.shape()); + + { + QTest::mouseMove(view.viewport(), view.mapFromScene(item1->sceneBoundingRect().center())); + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item1->sceneBoundingRect().center()), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + if (!PlatformQuirks::haveMouseCursor()) + return; +#if !defined(Q_OS_WINCE) + QTest::qWait(250); +#else + // Test environment does not have any cursor, therefore no shape + return; +#endif + + QCOMPARE(view.viewport()->cursor().shape(), item1->cursor().shape()); + + { + QTest::mouseMove(view.viewport(), view.mapFromScene(item2->sceneBoundingRect().center())); + QMouseEvent event(QEvent::MouseMove, view.mapFromScene(item2->sceneBoundingRect().center()), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), item2->cursor().shape()); + + { + QTest::mouseMove(view.viewport(), view.rect().center()); + QMouseEvent event(QEvent::MouseMove, QPoint(100, 25), Qt::NoButton, 0, 0); + QApplication::sendEvent(view.viewport(), &event); + } + + QTest::qWait(25); + + QCOMPARE(view.viewport()->cursor().shape(), cursor.shape()); +#endif +} +/* +void tst_QGraphicsItem::textControlGetterSetter() +{ + QGraphicsTextItem *item = new QGraphicsTextItem; + QVERIFY(item->textControl()->parent() == item); + QPointer control = item->textControl(); + delete item; + QVERIFY(!control); + + item = new QGraphicsTextItem; + + QPointer oldControl = control; + control = new QTextControl; + + item->setTextControl(control); + QVERIFY(item->textControl() == control); + QVERIFY(!control->parent()); + QVERIFY(!oldControl); + + // set some text to give it a size, to test that + // setTextControl (re)connects signals + const QRectF oldBoundingRect = item->boundingRect(); + QVERIFY(oldBoundingRect.isValid()); + item->setPlainText("Some text"); + item->adjustSize(); + QVERIFY(item->boundingRect().isValid()); + QVERIFY(item->boundingRect() != oldBoundingRect); + + // test that on setting a control the item size + // is adjusted + oldControl = control; + control = new QTextControl; + control->setPlainText("foo!"); + item->setTextControl(control); + QCOMPARE(item->boundingRect().size(), control->document()->documentLayout()->documentSize()); + + QVERIFY(oldControl); + delete oldControl; + + delete item; + QVERIFY(control); + delete control; +} +*/ + +void tst_QGraphicsItem::defaultItemTest_QGraphicsLineItem() +{ + QGraphicsLineItem item; + QCOMPARE(item.line(), QLineF()); + QCOMPARE(item.pen(), QPen()); + QCOMPARE(item.shape(), QPainterPath()); + + item.setPen(QPen(Qt::black, 1)); + QCOMPARE(item.pen(), QPen(Qt::black, 1)); + item.setLine(QLineF(0, 0, 10, 0)); + QCOMPARE(item.line(), QLineF(0, 0, 10, 0)); + QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 11, 1)); + QCOMPARE(item.shape().elementCount(), 11); + + QPainterPath path; + path.moveTo(0, -0.5); + path.lineTo(10, -0.5); + path.lineTo(10.5, -0.5); + path.lineTo(10.5, 0.5); + path.lineTo(10, 0.5); + path.lineTo(0, 0.5); + path.lineTo(-0.5, 0.5); + path.lineTo(-0.5, -0.5); + path.lineTo(0, -0.5); + path.lineTo(0, 0); + path.lineTo(10, 0); + path.closeSubpath(); + + for (int i = 0; i < 11; ++i) + QCOMPARE(QPointF(item.shape().elementAt(i)), QPointF(path.elementAt(i))); +} + +void tst_QGraphicsItem::defaultItemTest_QGraphicsPixmapItem() +{ + QGraphicsPixmapItem item; + QVERIFY(item.pixmap().isNull()); + QCOMPARE(item.offset(), QPointF()); + QCOMPARE(item.transformationMode(), Qt::FastTransformation); + + QPixmap pixmap(300, 200); + pixmap.fill(Qt::red); + item.setPixmap(pixmap); + QCOMPARE(item.pixmap(), pixmap); + + item.setTransformationMode(Qt::FastTransformation); + QCOMPARE(item.transformationMode(), Qt::FastTransformation); + item.setTransformationMode(Qt::SmoothTransformation); + QCOMPARE(item.transformationMode(), Qt::SmoothTransformation); + + item.setOffset(-15, -15); + QCOMPARE(item.offset(), QPointF(-15, -15)); + item.setOffset(QPointF(-10, -10)); + QCOMPARE(item.offset(), QPointF(-10, -10)); + + QCOMPARE(item.boundingRect(), QRectF(-10, -10, 300, 200)); +} + +void tst_QGraphicsItem::defaultItemTest_QGraphicsTextItem() +{ + QGraphicsTextItem *text = new QGraphicsTextItem; + QVERIFY(!text->openExternalLinks()); + QVERIFY(text->textCursor().isNull()); + QCOMPARE(text->defaultTextColor(), QPalette().color(QPalette::Text)); + QVERIFY(text->document() != 0); + QCOMPARE(text->font(), QApplication::font()); + QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::NoTextInteraction)); + QCOMPARE(text->textWidth(), -1.0); + QCOMPARE(text->toPlainText(), QString("")); + + QGraphicsScene scene; + scene.addItem(text); + text->setPlainText("Hello world"); + text->setFlag(QGraphicsItem::ItemIsMovable); + + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(QPointF(1, 1)); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event); + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(11, 11)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event2); + } + + QCOMPARE(text->pos(), QPointF(10, 10)); + + text->setTextInteractionFlags(Qt::NoTextInteraction); + QVERIFY(!(text->flags() & QGraphicsItem::ItemAcceptsInputMethod)); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + QCOMPARE(text->textInteractionFlags(), Qt::TextInteractionFlags(Qt::TextEditorInteraction)); + QVERIFY(text->flags() & QGraphicsItem::ItemAcceptsInputMethod); + + { + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(21, 21)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event2); + } + + QCOMPARE(text->pos(), QPointF(20, 20)); // clicked on edge, item moved +} + +void tst_QGraphicsItem::defaultItemTest_QGraphicsEllipseItem() +{ + QGraphicsEllipseItem item; + QVERIFY(item.rect().isNull()); + QVERIFY(item.boundingRect().isNull()); + QVERIFY(item.shape().isEmpty()); + QCOMPARE(item.spanAngle(), 360 * 16); + QCOMPARE(item.startAngle(), 0); + + item.setRect(0, 0, 100, 100); + QCOMPARE(item.boundingRect(), QRectF(0, 0, 100, 100)); + + item.setSpanAngle(90 * 16); + qFuzzyCompare(item.boundingRect().left(), qreal(50.0)); + qFuzzyCompare(item.boundingRect().top(), qreal(0.0)); + qFuzzyCompare(item.boundingRect().width(), qreal(50.0)); + qFuzzyCompare(item.boundingRect().height(), qreal(50.0)); + + item.setPen(QPen(Qt::black, 1)); + QCOMPARE(item.boundingRect(), QRectF(49.5, -0.5, 51, 51)); + + item.setSpanAngle(180 * 16); + QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 51)); + + item.setSpanAngle(360 * 16); + QCOMPARE(item.boundingRect(), QRectF(-0.5, -0.5, 101, 101)); +} + +class ItemChangeTester : public QGraphicsRectItem +{ +public: + ItemChangeTester() + { setFlag(ItemSendsGeometryChanges); clear(); } + ItemChangeTester(QGraphicsItem *parent) : QGraphicsRectItem(parent) + { setFlag(ItemSendsGeometryChanges); clear(); } + + void clear() + { + itemChangeReturnValue = QVariant(); + itemSceneChangeTargetScene = 0; + changes.clear(); + values.clear(); + oldValues.clear(); + } + + QVariant itemChangeReturnValue; + QGraphicsScene *itemSceneChangeTargetScene; + + QList changes; + QList values; + QList oldValues; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + changes << change; + values << value; + switch (change) { + case QGraphicsItem::ItemPositionChange: + oldValues << pos(); + break; + case QGraphicsItem::ItemPositionHasChanged: + break; + case QGraphicsItem::ItemMatrixChange: { + QVariant variant; + qVariantSetValue(variant, matrix()); + oldValues << variant; + } + break; + case QGraphicsItem::ItemTransformChange: { + QVariant variant; + qVariantSetValue(variant, transform()); + oldValues << variant; + } + break; + case QGraphicsItem::ItemTransformHasChanged: + break; + case QGraphicsItem::ItemVisibleChange: + oldValues << isVisible(); + break; + case QGraphicsItem::ItemVisibleHasChanged: + break; + case QGraphicsItem::ItemEnabledChange: + oldValues << isEnabled(); + break; + case QGraphicsItem::ItemEnabledHasChanged: + break; + case QGraphicsItem::ItemSelectedChange: + oldValues << isSelected(); + break; + case QGraphicsItem::ItemSelectedHasChanged: + break; + case QGraphicsItem::ItemParentChange: + oldValues << qVariantFromValue(parentItem()); + break; + case QGraphicsItem::ItemParentHasChanged: + break; + case QGraphicsItem::ItemChildAddedChange: + oldValues << children().size(); + break; + case QGraphicsItem::ItemChildRemovedChange: + oldValues << children().size(); + break; + case QGraphicsItem::ItemSceneChange: + oldValues << qVariantFromValue(scene()); + if (itemSceneChangeTargetScene + && qVariantValue(value) + && itemSceneChangeTargetScene != qVariantValue(value)) { + return qVariantFromValue(itemSceneChangeTargetScene); + } + return value; + case QGraphicsItem::ItemSceneHasChanged: + break; + case QGraphicsItem::ItemCursorChange: +#ifndef QT_NO_CURSOR + oldValues << cursor(); +#endif + break; + case QGraphicsItem::ItemCursorHasChanged: + break; + case QGraphicsItem::ItemToolTipChange: + oldValues << toolTip(); + break; + case QGraphicsItem::ItemToolTipHasChanged: + break; + case QGraphicsItem::ItemFlagsChange: + oldValues << quint32(flags()); + break; + case QGraphicsItem::ItemFlagsHaveChanged: + break; + case QGraphicsItem::ItemZValueChange: + oldValues << zValue(); + break; + case QGraphicsItem::ItemZValueHasChanged: + break; + case QGraphicsItem::ItemOpacityChange: + oldValues << opacity(); + break; + case QGraphicsItem::ItemOpacityHasChanged: + break; + case QGraphicsItem::ItemScenePositionHasChanged: + break; + case QGraphicsItem::ItemRotationChange: + oldValues << rotation(); + break; + case QGraphicsItem::ItemRotationHasChanged: + break; + case QGraphicsItem::ItemScaleChange: + oldValues << scale(); + break; + case QGraphicsItem::ItemScaleHasChanged: + break; + case QGraphicsItem::ItemTransformOriginPointChange: + oldValues << transformOriginPoint(); + break; + case QGraphicsItem::ItemTransformOriginPointHasChanged: + break; + } + return itemChangeReturnValue.isValid() ? itemChangeReturnValue : value; + } +}; + +void tst_QGraphicsItem::itemChange() +{ + ItemChangeTester tester; + tester.itemSceneChangeTargetScene = 0; + + ItemChangeTester testerHelper; + QVERIFY(tester.changes.isEmpty()); + QVERIFY(tester.values.isEmpty()); + + int changeCount = 0; + { + // ItemEnabledChange + tester.itemChangeReturnValue = true; + tester.setEnabled(false); + ++changeCount; + ++changeCount; // HasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemEnabledChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemEnabledHasChanged); + QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false)); + QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true)); + QCOMPARE(tester.oldValues.last(), QVariant(true)); + QCOMPARE(tester.isEnabled(), true); + } + { + // ItemMatrixChange / ItemTransformHasChanged + qVariantSetValue(tester.itemChangeReturnValue, QMatrix().rotate(90)); + tester.setMatrix(QMatrix().translate(50, 0), true); + ++changeCount; // notification sent too + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(int(tester.changes.at(tester.changes.size() - 2)), int(QGraphicsItem::ItemMatrixChange)); + QCOMPARE(int(tester.changes.last()), int(QGraphicsItem::ItemTransformHasChanged)); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 2)), + QMatrix().translate(50, 0)); + QCOMPARE(tester.values.last(), QVariant(QTransform(QMatrix().rotate(90)))); + QVariant variant; + qVariantSetValue(variant, QMatrix()); + QCOMPARE(tester.oldValues.last(), variant); + QCOMPARE(tester.matrix(), QMatrix().rotate(90)); + } + { + tester.resetTransform(); + ++changeCount; + ++changeCount; // notification sent too + + // ItemTransformChange / ItemTransformHasChanged + qVariantSetValue(tester.itemChangeReturnValue, QTransform().rotate(90)); + tester.translate(50, 0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformHasChanged); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 2)), + QTransform().translate(50, 0)); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 1)), + QTransform().rotate(90)); + QVariant variant; + qVariantSetValue(variant, QTransform()); + QCOMPARE(tester.oldValues.last(), variant); + QCOMPARE(tester.transform(), QTransform().rotate(90)); + } + { + // ItemPositionChange / ItemPositionHasChanged + tester.itemChangeReturnValue = QPointF(42, 0); + tester.setPos(0, 42); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemPositionChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemPositionHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(0, 42))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(42, 0))); + QCOMPARE(tester.oldValues.last(), QVariant(QPointF())); + QCOMPARE(tester.pos(), QPointF(42, 0)); + } + { + // ItemZValueChange / ItemZValueHasChanged + tester.itemChangeReturnValue = qreal(2.0); + tester.setZValue(1.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemZValueChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemZValueHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(0.0))); + QCOMPARE(tester.zValue(), qreal(2.0)); + } + { + // ItemRotationChange / ItemRotationHasChanged + tester.itemChangeReturnValue = qreal(15.0); + tester.setRotation(10.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemRotationChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemRotationHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(10.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(15.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(0.0))); + QCOMPARE(tester.rotation(), qreal(15.0)); + } + { + // ItemScaleChange / ItemScaleHasChanged + tester.itemChangeReturnValue = qreal(2.0); + tester.setScale(1.5); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemScaleChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemScaleHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.5))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(1.0))); + QCOMPARE(tester.scale(), qreal(2.0)); + } + { + // ItemTransformOriginPointChange / ItemTransformOriginPointHasChanged + tester.itemChangeReturnValue = QPointF(2.0, 2.0); + tester.setTransformOriginPoint(1.0, 1.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformOriginPointChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformOriginPointHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(1.0, 1.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(2.0, 2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(QPointF(0.0, 0.0))); + QCOMPARE(tester.transformOriginPoint(), QPointF(2.0, 2.0)); + } + { + // ItemFlagsChange + tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable; + tester.setFlag(QGraphicsItem::ItemIsSelectable, false); + QCOMPARE(tester.changes.size(), changeCount); // No change + tester.setFlag(QGraphicsItem::ItemIsSelectable, true); + ++changeCount; + ++changeCount; // ItemFlagsHasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemFlagsChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemFlagsHaveChanged); + QVariant expectedFlags = qVariantFromValue(QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges)); + QCOMPARE(tester.values.at(tester.values.size() - 2), expectedFlags); + QCOMPARE(tester.values.at(tester.values.size() - 1), qVariantFromValue((quint32)QGraphicsItem::ItemIsSelectable)); + } + { + // ItemSelectedChange + tester.setSelected(false); + QCOMPARE(tester.changes.size(), changeCount); // No change :-) + tester.itemChangeReturnValue = true; + tester.setSelected(true); + ++changeCount; + ++changeCount; // ItemSelectedHasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSelectedHasChanged); + QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(true)); + QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(true)); + QCOMPARE(tester.oldValues.last(), QVariant(false)); + QCOMPARE(tester.isSelected(), true); + + tester.itemChangeReturnValue = false; + tester.setSelected(true); + + // the value hasn't changed to the itemChange return value + // bacause itemChange is never called (true -> true is a noop). + QCOMPARE(tester.isSelected(), true); + } + { + // ItemVisibleChange + tester.itemChangeReturnValue = false; + QVERIFY(tester.isVisible()); + tester.setVisible(false); + ++changeCount; // ItemVisibleChange + ++changeCount; // ItemSelectedChange + ++changeCount; // ItemSelectedHasChanged + ++changeCount; // ItemVisibleHasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemVisibleChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSelectedChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSelectedHasChanged); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemVisibleHasChanged); + QCOMPARE(tester.values.at(tester.values.size() - 4), QVariant(false)); + QCOMPARE(tester.values.at(tester.values.size() - 3), QVariant(false)); + QCOMPARE(tester.values.at(tester.values.size() - 2), QVariant(false)); + QCOMPARE(tester.values.at(tester.values.size() - 1), QVariant(false)); + QCOMPARE(tester.isVisible(), false); + } + { + // ItemParentChange + qVariantSetValue(tester.itemChangeReturnValue, 0); + tester.setParentItem(&testerHelper); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemParentChange); + QCOMPARE(qVariantValue(tester.values.last()), (QGraphicsItem *)&testerHelper); + QCOMPARE(qVariantValue(tester.oldValues.last()), (QGraphicsItem *)0); + QCOMPARE(tester.parentItem(), (QGraphicsItem *)0); + } + { + // ItemOpacityChange + tester.itemChangeReturnValue = 1.0; + tester.setOpacity(0.7); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemOpacityChange); + QVERIFY(qFuzzyCompare(qreal(tester.values.last().toDouble()), qreal(0.7))); + QCOMPARE(tester.oldValues.last().toDouble(), double(1.0)); + QCOMPARE(tester.opacity(), qreal(1.0)); + tester.itemChangeReturnValue = 0.7; + tester.setOpacity(0.7); + ++changeCount; // ItemOpacityChange + ++changeCount; // ItemOpacityHasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemOpacityChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemOpacityHasChanged); + QCOMPARE(tester.opacity(), qreal(0.7)); + } + { + // ItemChildAddedChange + tester.itemChangeReturnValue.clear(); + testerHelper.setParentItem(&tester); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemChildAddedChange); + QCOMPARE(qVariantValue(tester.values.last()), (QGraphicsItem *)&testerHelper); + } + { + // ItemChildRemovedChange 1 + testerHelper.setParentItem(0); + QCOMPARE(tester.changes.size(), ++changeCount); + QCOMPARE(tester.changes.last(), QGraphicsItem::ItemChildRemovedChange); + QCOMPARE(qVariantValue(tester.values.last()), (QGraphicsItem *)&testerHelper); + + // ItemChildRemovedChange 1 + ItemChangeTester *test = new ItemChangeTester; + test->itemSceneChangeTargetScene = 0; + int count = 0; + QGraphicsScene *scene = new QGraphicsScene; + scene->addItem(test); + count = test->changes.size(); + //We test here the fact that when a child is deleted the parent receive only one ItemChildRemovedChange + QGraphicsRectItem *child = new QGraphicsRectItem(test); + //We received ItemChildAddedChange + QCOMPARE(test->changes.size(), ++count); + QCOMPARE(test->changes.last(), QGraphicsItem::ItemChildAddedChange); + delete child; + child = 0; + QCOMPARE(test->changes.size(), ++count); + QCOMPARE(test->changes.last(), QGraphicsItem::ItemChildRemovedChange); + + ItemChangeTester *childTester = new ItemChangeTester(test); + //Changes contains all sceneHasChanged and so on, we don't want to test that + int childCount = childTester->changes.size(); + //We received ItemChildAddedChange + QCOMPARE(test->changes.size(), ++count); + child = new QGraphicsRectItem(childTester); + //We received ItemChildAddedChange + QCOMPARE(childTester->changes.size(), ++childCount); + QCOMPARE(childTester->changes.last(), QGraphicsItem::ItemChildAddedChange); + //Delete the child of the top level with all its children + delete childTester; + //Only one removal + QCOMPARE(test->changes.size(), ++count); + QCOMPARE(test->changes.last(), QGraphicsItem::ItemChildRemovedChange); + delete scene; + } + { + // ItemChildRemovedChange 2 + ItemChangeTester parent; + ItemChangeTester *child = new ItemChangeTester; + child->setParentItem(&parent); + QCOMPARE(parent.changes.last(), QGraphicsItem::ItemChildAddedChange); + QCOMPARE(qVariantValue(parent.values.last()), (QGraphicsItem *)child); + delete child; + QCOMPARE(parent.changes.last(), QGraphicsItem::ItemChildRemovedChange); + QCOMPARE(qVariantValue(parent.values.last()), (QGraphicsItem *)child); + } + { + // !!! Note: If this test crashes because of double-deletion, there's + // a bug somewhere in QGraphicsScene or QGraphicsItem. + + // ItemSceneChange + QGraphicsScene scene; + QGraphicsScene scene2; + scene.addItem(&tester); + ++changeCount; // ItemSceneChange (scene) + ++changeCount; // ItemSceneHasChanged (scene) + QCOMPARE(tester.changes.size(), changeCount); + + QCOMPARE(tester.scene(), &scene); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged); + // Item's old value was 0 + // Item's current value is scene + QCOMPARE(qVariantValue(tester.oldValues.last()), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.last()), (QGraphicsScene *)&scene); + scene2.addItem(&tester); + ++changeCount; // ItemSceneChange (0) was: (scene) + ++changeCount; // ItemSceneHasChanged (0) + ++changeCount; // ItemSceneChange (scene2) was: (0) + ++changeCount; // ItemSceneHasChanged (scene2) + QCOMPARE(tester.changes.size(), changeCount); + + QCOMPARE(tester.scene(), &scene2); + QCOMPARE(tester.changes.at(tester.changes.size() - 4), QGraphicsItem::ItemSceneChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 3), QGraphicsItem::ItemSceneHasChanged); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged); + // Item's last old value was scene + // Item's last current value is 0 + + QCOMPARE(qVariantValue(tester.oldValues.at(tester.oldValues.size() - 2)), (QGraphicsScene *)&scene); + QCOMPARE(qVariantValue(tester.oldValues.at(tester.oldValues.size() - 1)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 4)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 3)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)&scene2); + // Item's last old value was 0 + // Item's last current value is scene2 + QCOMPARE(qVariantValue(tester.oldValues.last()), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.last()), (QGraphicsScene *)&scene2); + + scene2.removeItem(&tester); + ++changeCount; // ItemSceneChange (0) was: (scene2) + ++changeCount; // ItemSceneHasChanged (0) + QCOMPARE(tester.changes.size(), changeCount); + + QCOMPARE(tester.scene(), (QGraphicsScene *)0); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemSceneChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemSceneHasChanged); + // Item's last old value was scene2 + // Item's last current value is 0 + QCOMPARE(qVariantValue(tester.oldValues.last()), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)0); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)0); + + tester.itemSceneChangeTargetScene = &scene; + scene2.addItem(&tester); + ++changeCount; // ItemSceneChange (scene2) was: (0) + ++changeCount; // ItemSceneChange (scene) was: (0) + ++changeCount; // ItemSceneHasChanged (scene) + QCOMPARE(tester.values.size(), changeCount); + + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 3)), (QGraphicsScene *)&scene2); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 2)), (QGraphicsScene *)&scene); + QCOMPARE(qVariantValue(tester.values.at(tester.values.size() - 1)), (QGraphicsScene *)&scene); + + QCOMPARE(tester.scene(), &scene); + tester.itemSceneChangeTargetScene = 0; + tester.itemChangeReturnValue = QVariant(); + scene.removeItem(&tester); + ++changeCount; // ItemSceneChange + ++changeCount; // ItemSceneHasChanged + QCOMPARE(tester.scene(), (QGraphicsScene *)0); + } + { + // ItemToolTipChange/ItemToolTipHasChanged + const QString toolTip(QLatin1String("I'm soo cool")); + const QString overridenToolTip(QLatin1String("No, you are not soo cool")); + tester.itemChangeReturnValue = overridenToolTip; + tester.setToolTip(toolTip); + ++changeCount; // ItemToolTipChange + ++changeCount; // ItemToolTipHasChanged + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(changeCount - 2), QGraphicsItem::ItemToolTipChange); + QCOMPARE(tester.values.at(changeCount - 2).toString(), toolTip); + QCOMPARE(tester.changes.at(changeCount - 1), QGraphicsItem::ItemToolTipHasChanged); + QCOMPARE(tester.values.at(changeCount - 1).toString(), overridenToolTip); + QCOMPARE(tester.toolTip(), overridenToolTip); + tester.itemChangeReturnValue = QVariant(); + } +} + +class EventFilterTesterItem : public QGraphicsLineItem +{ +public: + QList filteredEvents; + QList filteredEventReceivers; + bool handlesSceneEvents; + + QList receivedEvents; + + EventFilterTesterItem() : handlesSceneEvents(false) {} + +protected: + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + filteredEvents << event->type(); + filteredEventReceivers << watched; + return handlesSceneEvents; + } + + bool sceneEvent(QEvent *event) + { + return QGraphicsLineItem::sceneEvent(event); + } +}; + +void tst_QGraphicsItem::sceneEventFilter() +{ + QGraphicsScene scene; + + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + + QGraphicsTextItem *text1 = scene.addText(QLatin1String("Text1")); + QGraphicsTextItem *text2 = scene.addText(QLatin1String("Text2")); + QGraphicsTextItem *text3 = scene.addText(QLatin1String("Text3")); + text1->setFlag(QGraphicsItem::ItemIsFocusable); + text2->setFlag(QGraphicsItem::ItemIsFocusable); + text3->setFlag(QGraphicsItem::ItemIsFocusable); + + EventFilterTesterItem *tester = new EventFilterTesterItem; + scene.addItem(tester); + + QTRY_VERIFY(!text1->hasFocus()); + text1->installSceneEventFilter(tester); + text1->setFocus(); + QTRY_VERIFY(text1->hasFocus()); + + QCOMPARE(tester->filteredEvents.size(), 1); + QCOMPARE(tester->filteredEvents.at(0), QEvent::FocusIn); + QCOMPARE(tester->filteredEventReceivers.at(0), static_cast(text1)); + + text2->installSceneEventFilter(tester); + text3->installSceneEventFilter(tester); + + text2->setFocus(); + text3->setFocus(); + + QCOMPARE(tester->filteredEvents.size(), 5); + QCOMPARE(tester->filteredEvents.at(1), QEvent::FocusOut); + QCOMPARE(tester->filteredEventReceivers.at(1), static_cast(text1)); + QCOMPARE(tester->filteredEvents.at(2), QEvent::FocusIn); + QCOMPARE(tester->filteredEventReceivers.at(2), static_cast(text2)); + QCOMPARE(tester->filteredEvents.at(3), QEvent::FocusOut); + QCOMPARE(tester->filteredEventReceivers.at(3), static_cast(text2)); + QCOMPARE(tester->filteredEvents.at(4), QEvent::FocusIn); + QCOMPARE(tester->filteredEventReceivers.at(4), static_cast(text3)); + + text1->removeSceneEventFilter(tester); + text1->setFocus(); + + QCOMPARE(tester->filteredEvents.size(), 6); + QCOMPARE(tester->filteredEvents.at(5), QEvent::FocusOut); + QCOMPARE(tester->filteredEventReceivers.at(5), static_cast(text3)); + + tester->handlesSceneEvents = true; + text2->setFocus(); + + QCOMPARE(tester->filteredEvents.size(), 7); + QCOMPARE(tester->filteredEvents.at(6), QEvent::FocusIn); + QCOMPARE(tester->filteredEventReceivers.at(6), static_cast(text2)); + + QVERIFY(text2->hasFocus()); + + //Let check if the items are correctly removed from the sceneEventFilters array + //to avoid stale pointers. + QGraphicsView gv; + QGraphicsScene *anotherScene = new QGraphicsScene; + QGraphicsTextItem *ti = anotherScene->addText("This is a test #1"); + ti->moveBy(50, 50); + QGraphicsTextItem *ti2 = anotherScene->addText("This is a test #2"); + QGraphicsTextItem *ti3 = anotherScene->addText("This is a test #3"); + gv.setScene(anotherScene); + gv.show(); + QTest::qWaitForWindowShown(&gv); + QTest::qWait(25); + ti->installSceneEventFilter(ti2); + ti3->installSceneEventFilter(ti); + delete ti2; + //we souldn't crash + QTest::mouseMove(gv.viewport(), gv.mapFromScene(ti->scenePos())); + QTest::qWait(30); + delete ti; +} + +class GeometryChanger : public QGraphicsItem +{ +public: + void changeGeometry() + { prepareGeometryChange(); } +}; + +void tst_QGraphicsItem::prepareGeometryChange() +{ + { + QGraphicsScene scene; + QGraphicsItem *item = scene.addRect(QRectF(0, 0, 100, 100)); + QVERIFY(scene.items(QRectF(0, 0, 100, 100)).contains(item)); + ((GeometryChanger *)item)->changeGeometry(); + QVERIFY(scene.items(QRectF(0, 0, 100, 100)).contains(item)); + } +} + + +class PaintTester : public QGraphicsRectItem +{ +public: + PaintTester() : widget(NULL), painted(0) { setRect(QRectF(10, 10, 20, 20));} + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *w) + { + widget = w; + painted++; + } + + QWidget* widget; + int painted; +}; + +void tst_QGraphicsItem::paint() +{ + QGraphicsScene scene; + + PaintTester paintTester; + scene.addItem(&paintTester); + + QGraphicsView view(&scene); + + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::processEvents(); +#ifdef Q_OS_WIN32 + //we try to switch the desktop: if it fails, we skip the test + if (::SwitchDesktop( ::GetThreadDesktop( ::GetCurrentThreadId() ) ) == 0) { + QSKIP("The Graphics View doesn't get the paint events", SkipSingle); + } +#endif + + QTRY_COMPARE(paintTester.widget, view.viewport()); + + view.hide(); + + QGraphicsScene scene2; + QGraphicsView view2(&scene2); + view2.show(); + QTest::qWaitForWindowShown(&view2); + QTest::qWait(25); + + PaintTester tester2; + scene2.addItem(&tester2); + qApp->processEvents(); + + //First show one paint + QTRY_COMPARE(tester2.painted, 1); + + //nominal case, update call paint + tester2.update(); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 2); + + //we remove the item from the scene, number of updates is still the same + tester2.update(); + scene2.removeItem(&tester2); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 2); + + //We re-add the item, the number of paint should increase + scene2.addItem(&tester2); + tester2.update(); + qApp->processEvents(); + QTRY_VERIFY(tester2.painted == 3); +} + +class HarakiriItem : public QGraphicsRectItem +{ +public: + HarakiriItem(int harakiriPoint) + : QGraphicsRectItem(QRectF(0, 0, 100, 100)), harakiri(harakiriPoint) + { dead = 0; } + + static int dead; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + if (harakiri == 0) { + // delete unsupported since 4.5 + /* + dead = 1; + delete this; + */ + } + } + + void advance(int n) + { + if (harakiri == 1 && n == 0) { + // delete unsupported + /* + dead = 1; + delete this; + */ + } + if (harakiri == 2 && n == 1) { + dead = 1; + delete this; + } + } + +protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *) + { + if (harakiri == 3) { + dead = 1; + delete this; + } + } + + void dragEnterEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragEnterEvent(event); + } + + void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragLeaveEvent(event); + } + + void dragMoveEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dragMoveEvent(event); + } + + void dropEvent(QGraphicsSceneDragDropEvent *event) + { + // ?? + QGraphicsRectItem::dropEvent(event); + } + + void focusInEvent(QFocusEvent *) + { + if (harakiri == 4) { + dead = 1; + delete this; + } + } + + void focusOutEvent(QFocusEvent *) + { + if (harakiri == 5) { + dead = 1; + delete this; + } + } + + void hoverEnterEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 6) { + dead = 1; + delete this; + } + } + + void hoverLeaveEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 7) { + dead = 1; + delete this; + } + } + + void hoverMoveEvent(QGraphicsSceneHoverEvent *) + { + if (harakiri == 8) { + dead = 1; + delete this; + } + } + + void inputMethodEvent(QInputMethodEvent *event) + { + // ?? + QGraphicsRectItem::inputMethodEvent(event); + } + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const + { + // ?? + return QGraphicsRectItem::inputMethodQuery(query); + } + + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + // deletion not supported + return QGraphicsRectItem::itemChange(change, value); + } + + void keyPressEvent(QKeyEvent *) + { + if (harakiri == 9) { + dead = 1; + delete this; + } + } + + void keyReleaseEvent(QKeyEvent *) + { + if (harakiri == 10) { + dead = 1; + delete this; + } + } + + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 11) { + dead = 1; + delete this; + } + } + + void mouseMoveEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 12) { + dead = 1; + delete this; + } + } + + void mousePressEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 13) { + dead = 1; + delete this; + } + } + + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + { + if (harakiri == 14) { + dead = 1; + delete this; + } + } + + bool sceneEvent(QEvent *event) + { + // deletion not supported + return QGraphicsRectItem::sceneEvent(event); + } + + bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) + { + // deletion not supported + return QGraphicsRectItem::sceneEventFilter(watched, event); + } + + void wheelEvent(QGraphicsSceneWheelEvent *) + { + if (harakiri == 16) { + dead = 1; + delete this; + } + } + +private: + int harakiri; +}; + +int HarakiriItem::dead; + +void tst_QGraphicsItem::deleteItemInEventHandlers() +{ + for (int i = 0; i < 17; ++i) { + QGraphicsScene scene; + HarakiriItem *item = new HarakiriItem(i); + item->setAcceptsHoverEvents(true); + item->setFlag(QGraphicsItem::ItemIsFocusable); + + scene.addItem(item); + + item->installSceneEventFilter(item); // <- ehey! + + QGraphicsView view(&scene); + view.show(); + + qApp->processEvents(); + qApp->processEvents(); + + if (!item->dead) + scene.advance(); + + if (!item->dead) { + QContextMenuEvent event(QContextMenuEvent::Other, + view.mapFromScene(item->scenePos())); + QCoreApplication::sendEvent(view.viewport(), &event); + } + if (!item->dead) + QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseClick(view.viewport(), Qt::RightButton, 0, view.mapFromScene(item->scenePos())); + if (!item->dead) + QTest::mouseMove(view.viewport(), view.mapFromScene(item->scenePos() + QPointF(20, -20))); + if (!item->dead) + item->setFocus(); + if (!item->dead) + item->clearFocus(); + if (!item->dead) + item->setFocus(); + if (!item->dead) + QTest::keyPress(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyRelease(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyPress(view.viewport(), Qt::Key_A); + if (!item->dead) + QTest::keyRelease(view.viewport(), Qt::Key_A); + } +} + +class ItemPaintsOutsideShape : public QGraphicsItem +{ +public: + QRectF boundingRect() const + { + return QRectF(0, 0, 100, 100); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->fillRect(-50, -50, 200, 200, Qt::red); + painter->fillRect(0, 0, 100, 100, Qt::blue); + } +}; + +void tst_QGraphicsItem::itemClipsToShape() +{ + QGraphicsItem *clippedItem = new ItemPaintsOutsideShape; + clippedItem->setFlag(QGraphicsItem::ItemClipsToShape); + + QGraphicsItem *unclippedItem = new ItemPaintsOutsideShape; + unclippedItem->setPos(200, 0); + + QGraphicsScene scene(-50, -50, 400, 200); + scene.addItem(clippedItem); + scene.addItem(unclippedItem); + + QImage image(400, 200, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(45, 100), QRgb(0)); + QCOMPARE(image.pixel(100, 45), QRgb(0)); + QCOMPARE(image.pixel(155, 100), QRgb(0)); + QCOMPARE(image.pixel(45, 155), QRgb(0)); + QCOMPARE(image.pixel(55, 100), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(100, 55), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(145, 100), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(55, 145), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(245, 100), QColor(Qt::red).rgba()); + QCOMPARE(image.pixel(300, 45), QColor(Qt::red).rgba()); + QCOMPARE(image.pixel(355, 100), QColor(Qt::red).rgba()); + QCOMPARE(image.pixel(245, 155), QColor(Qt::red).rgba()); + QCOMPARE(image.pixel(255, 100), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(300, 55), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(345, 100), QColor(Qt::blue).rgba()); + QCOMPARE(image.pixel(255, 145), QColor(Qt::blue).rgba()); +} + +void tst_QGraphicsItem::itemClipsChildrenToShape() +{ + QGraphicsScene scene; + QGraphicsItem *rect = scene.addRect(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::yellow)); + + QGraphicsItem *ellipse = scene.addEllipse(0, 0, 100, 100, QPen(Qt::NoPen), QBrush(Qt::green)); + ellipse->setParentItem(rect); + + QGraphicsItem *clippedEllipse = scene.addEllipse(0, 0, 50, 50, QPen(Qt::NoPen), QBrush(Qt::blue)); + clippedEllipse->setParentItem(ellipse); + + QGraphicsItem *clippedEllipse2 = scene.addEllipse(0, 0, 25, 25, QPen(Qt::NoPen), QBrush(Qt::red)); + clippedEllipse2->setParentItem(clippedEllipse); + + QGraphicsItem *clippedEllipse3 = scene.addEllipse(50, 50, 25, 25, QPen(Qt::NoPen), QBrush(Qt::red)); + clippedEllipse3->setParentItem(clippedEllipse); + + QVERIFY(!(ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape)); + ellipse->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QVERIFY((ellipse->flags() & QGraphicsItem::ItemClipsChildrenToShape)); + + QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(16, 16), QColor(255, 0, 0).rgba()); + QCOMPARE(image.pixel(32, 32), QColor(0, 0, 255).rgba()); + QCOMPARE(image.pixel(50, 50), QColor(0, 255, 0).rgba()); + QCOMPARE(image.pixel(12, 12), QColor(255, 255, 0).rgba()); + QCOMPARE(image.pixel(60, 60), QColor(255, 0, 0).rgba()); +} + +void tst_QGraphicsItem::itemClipsChildrenToShape2() +{ + QGraphicsRectItem *parent = new QGraphicsRectItem(QRectF(0, 0, 10, 10)); + QGraphicsEllipseItem *child1 = new QGraphicsEllipseItem(QRectF(50, 50, 100, 100)); + QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(15, 15, 80, 80)); + + child1->setParentItem(parent); + child1->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + child2->setParentItem(child1); + + parent->setBrush(Qt::blue); + child1->setBrush(Qt::green); + child2->setBrush(Qt::red); + + QGraphicsScene scene; + scene.addItem(parent); + + QCOMPARE(scene.itemAt(5, 5), (QGraphicsItem *)parent); + QCOMPARE(scene.itemAt(15, 5), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(5, 15), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(60, 60), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(140, 60), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(60, 140), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(140, 140), (QGraphicsItem *)0); + QCOMPARE(scene.itemAt(75, 75), (QGraphicsItem *)child2); + QCOMPARE(scene.itemAt(75, 100), (QGraphicsItem *)child1); + QCOMPARE(scene.itemAt(100, 75), (QGraphicsItem *)child1); + +#if 1 + QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter painter(&image); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(5, 5), QColor(0, 0, 255).rgba()); + QCOMPARE(image.pixel(5, 10), QRgb(0)); + QCOMPARE(image.pixel(10, 5), QRgb(0)); + QCOMPARE(image.pixel(40, 40), QRgb(0)); + QCOMPARE(image.pixel(90, 40), QRgb(0)); + QCOMPARE(image.pixel(40, 90), QRgb(0)); + QCOMPARE(image.pixel(95, 95), QRgb(0)); + QCOMPARE(image.pixel(50, 70), QColor(0, 255, 0).rgba()); + QCOMPARE(image.pixel(70, 50), QColor(0, 255, 0).rgba()); + QCOMPARE(image.pixel(50, 60), QColor(255, 0, 0).rgba()); + QCOMPARE(image.pixel(60, 50), QColor(255, 0, 0).rgba()); +#else + QGraphicsView view(&scene); + view.show(); + QTest::qWait(5000); +#endif +} + +void tst_QGraphicsItem::itemClipsChildrenToShape3() +{ + // Construct a scene with nested children, each 50 pixels offset from the elder. + // Set a top-level clipping flag + QGraphicsScene scene; + QGraphicsRectItem *parent = scene.addRect( 0, 0, 150, 150 ); + QGraphicsRectItem *child = scene.addRect( 0, 0, 150, 150 ); + QGraphicsRectItem *grandchild = scene.addRect( 0, 0, 150, 150 ); + child->setParentItem(parent); + grandchild->setParentItem(child); + child->setPos( 50, 50 ); + grandchild->setPos( 50, 50 ); + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QCOMPARE(scene.itemAt(25,25), (QGraphicsItem *)parent); + QCOMPARE(scene.itemAt(75,75), (QGraphicsItem *)child); + QCOMPARE(scene.itemAt(125,125), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(175,175), (QGraphicsItem *)0); + + // Move child to fully overlap the parent. The grandchild should + // now occupy two-thirds of the scene + child->prepareGeometryChange(); + child->setPos( 0, 0 ); + + QCOMPARE(scene.itemAt(25,25), (QGraphicsItem *)child); + QCOMPARE(scene.itemAt(75,75), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(125,125), (QGraphicsItem *)grandchild); + QCOMPARE(scene.itemAt(175,175), (QGraphicsItem *)0); +} + +class MyProxyWidget : public QGraphicsProxyWidget +{ +public: + MyProxyWidget(QGraphicsItem *parent) : QGraphicsProxyWidget(parent) + { + painted = false; + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsProxyWidget::paint(painter, option, widget); + painted = true; + } + bool painted; +}; + +void tst_QGraphicsItem::itemClipsChildrenToShape4() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWidget * outerWidget = new QGraphicsWidget(); + outerWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + MyProxyWidget * innerWidget = new MyProxyWidget(outerWidget); + QLabel * label = new QLabel(); + label->setText("Welcome back my friends to the show that never ends..."); + innerWidget->setWidget(label); + view.resize(300, 300); + scene.addItem(outerWidget); + outerWidget->resize( 200, 100 ); + scene.addEllipse( 100, 100, 100, 50 ); // <-- this is important to trigger the right codepath* + //now the label is shown + outerWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false ); + QApplication::setActiveWindow(&view); + view.show(); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + QTRY_COMPARE(innerWidget->painted, true); +} + +//#define DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5 +static inline void renderSceneToImage(QGraphicsScene *scene, QImage *image, const QString &filename) +{ + image->fill(0); + QPainter painter(image); + scene->render(&painter); + painter.end(); +#ifdef DEBUG_ITEM_CLIPS_CHILDREN_TO_SHAPE_5 + image->save(filename); +#else + Q_UNUSED(filename); +#endif +} + +void tst_QGraphicsItem::itemClipsChildrenToShape5() +{ + class ParentItem : public QGraphicsRectItem + { + public: + ParentItem(qreal x, qreal y, qreal width, qreal height) + : QGraphicsRectItem(x, y, width, height) {} + + QPainterPath shape() const + { + QPainterPath path; + path.addRect(50, 50, 200, 200); + return path; + } + }; + + ParentItem *parent = new ParentItem(0, 0, 300, 300); + parent->setBrush(Qt::blue); + parent->setOpacity(0.5); + + const QRegion parentRegion(0, 0, 300, 300); + const QRegion clippedParentRegion = parentRegion & QRect(50, 50, 200, 200); + QRegion childRegion; + QRegion grandChildRegion; + + QGraphicsRectItem *topLeftChild = new QGraphicsRectItem(0, 0, 100, 100); + topLeftChild->setBrush(Qt::red); + topLeftChild->setParentItem(parent); + childRegion += QRect(0, 0, 100, 100); + + QGraphicsRectItem *topRightChild = new QGraphicsRectItem(0, 0, 100, 100); + topRightChild->setBrush(Qt::red); + topRightChild->setParentItem(parent); + topRightChild->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + topRightChild->setPos(200, 0); + childRegion += QRect(200, 0, 100, 100); + + QGraphicsRectItem *topRightGrandChild = new QGraphicsRectItem(0, 0, 100, 100); + topRightGrandChild->setBrush(Qt::green); + topRightGrandChild->setParentItem(topRightChild); + topRightGrandChild->setPos(-40, 40); + grandChildRegion += QRect(200 - 40, 0 + 40, 100, 100) & QRect(200, 0, 100, 100); + + QGraphicsRectItem *bottomLeftChild = new QGraphicsRectItem(0, 0, 100, 100); + bottomLeftChild->setBrush(Qt::red); + bottomLeftChild->setParentItem(parent); + bottomLeftChild->setFlag(QGraphicsItem::ItemClipsToShape); + bottomLeftChild->setPos(0, 200); + childRegion += QRect(0, 200, 100, 100); + + QGraphicsRectItem *bottomLeftGrandChild = new QGraphicsRectItem(0, 0, 160, 160); + bottomLeftGrandChild->setBrush(Qt::green); + bottomLeftGrandChild->setParentItem(bottomLeftChild); + bottomLeftGrandChild->setFlag(QGraphicsItem::ItemClipsToShape); + bottomLeftGrandChild->setPos(0, -60); + grandChildRegion += QRect(0, 200 - 60, 160, 160); + + QGraphicsRectItem *bottomRightChild = new QGraphicsRectItem(0, 0, 100, 100); + bottomRightChild->setBrush(Qt::red); + bottomRightChild->setParentItem(parent); + bottomRightChild->setPos(200, 200); + childRegion += QRect(200, 200, 100, 100); + + QPoint controlPoints[17] = { + QPoint(5, 5) , QPoint(95, 5) , QPoint(205, 5) , QPoint(295, 5) , + QPoint(5, 95) , QPoint(95, 95) , QPoint(205, 95) , QPoint(295, 95) , + QPoint(150, 150), + QPoint(5, 205), QPoint(95, 205), QPoint(205, 205), QPoint(295, 205), + QPoint(5, 295), QPoint(95, 295), QPoint(205, 295), QPoint(295, 295), + }; + + const QRegion clippedChildRegion = childRegion & QRect(50, 50, 200, 200); + const QRegion clippedGrandChildRegion = grandChildRegion & QRect(50, 50, 200, 200); + + QGraphicsScene scene; + scene.addItem(parent); + QImage sceneImage(300, 300, QImage::Format_ARGB32); + +#define VERIFY_CONTROL_POINTS(pRegion, cRegion, gRegion) \ + for (int i = 0; i < 17; ++i) { \ + QPoint controlPoint = controlPoints[i]; \ + QRgb pixel = sceneImage.pixel(controlPoint.x(), controlPoint.y()); \ + if (pRegion.contains(controlPoint)) \ + QVERIFY(qBlue(pixel) != 0); \ + else \ + QVERIFY(qBlue(pixel) == 0); \ + if (cRegion.contains(controlPoint)) \ + QVERIFY(qRed(pixel) != 0); \ + else \ + QVERIFY(qRed(pixel) == 0); \ + if (gRegion.contains(controlPoint)) \ + QVERIFY(qGreen(pixel) != 0); \ + else \ + QVERIFY(qGreen(pixel) == 0); \ + } + + const QList children = parent->childItems(); + const int childrenCount = children.count(); + + for (int i = 0; i < 5; ++i) { + QString clipString; + QString childString; + switch (i) { + case 0: + // All children stacked in front. + childString = QLatin1String("ChildrenInFront.png"); + foreach (QGraphicsItem *child, children) + child->setFlag(QGraphicsItem::ItemStacksBehindParent, false); + break; + case 1: + // All children stacked behind. + childString = QLatin1String("ChildrenBehind.png"); + foreach (QGraphicsItem *child, children) + child->setFlag(QGraphicsItem::ItemStacksBehindParent, true); + break; + case 2: + // First half of the children behind, second half in front. + childString = QLatin1String("FirstHalfBehind_SecondHalfInFront.png"); + for (int j = 0; j < childrenCount; ++j) { + QGraphicsItem *child = children.at(j); + child->setFlag(QGraphicsItem::ItemStacksBehindParent, (j < childrenCount / 2)); + } + break; + case 3: + // First half of the children in front, second half behind. + childString = QLatin1String("FirstHalfInFront_SecondHalfBehind.png"); + for (int j = 0; j < childrenCount; ++j) { + QGraphicsItem *child = children.at(j); + child->setFlag(QGraphicsItem::ItemStacksBehindParent, (j >= childrenCount / 2)); + } + break; + case 4: + // Child2 and child4 behind, rest in front. + childString = QLatin1String("Child2And4Behind_RestInFront.png"); + for (int j = 0; j < childrenCount; ++j) { + QGraphicsItem *child = children.at(j); + if (j == 1 || j == 3) + child->setFlag(QGraphicsItem::ItemStacksBehindParent, true); + else + child->setFlag(QGraphicsItem::ItemStacksBehindParent, false); + } + break; + default: + qFatal("internal error"); + } + + // Nothing is clipped. + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + parent->setFlag(QGraphicsItem::ItemClipsToShape, false); + clipString = QLatin1String("nothingClipped_"); + renderSceneToImage(&scene, &sceneImage, clipString + childString); + VERIFY_CONTROL_POINTS(parentRegion, childRegion, grandChildRegion); + + // Parent clips children to shape. + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + clipString = QLatin1String("parentClipsChildrenToShape_"); + renderSceneToImage(&scene, &sceneImage, clipString + childString); + VERIFY_CONTROL_POINTS(parentRegion, clippedChildRegion, clippedGrandChildRegion); + + // Parent clips itself and children to shape. + parent->setFlag(QGraphicsItem::ItemClipsToShape); + clipString = QLatin1String("parentClipsItselfAndChildrenToShape_"); + renderSceneToImage(&scene, &sceneImage, clipString + childString); + VERIFY_CONTROL_POINTS(clippedParentRegion, clippedChildRegion, clippedGrandChildRegion); + + // Parent clips itself to shape. + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + clipString = QLatin1String("parentClipsItselfToShape_"); + renderSceneToImage(&scene, &sceneImage, clipString + childString); + VERIFY_CONTROL_POINTS(clippedParentRegion, childRegion, grandChildRegion); + } +} + +void tst_QGraphicsItem::itemClipsTextChildToShape() +{ + // Construct a scene with a rect that clips its children, with one text + // child that has text that exceeds the size of the rect. + QGraphicsScene scene; + QGraphicsItem *rect = scene.addRect(0, 0, 50, 50, QPen(Qt::black), Qt::black); + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QGraphicsTextItem *text = new QGraphicsTextItem("This is a long sentence that's wider than 50 pixels."); + text->setParentItem(rect); + + // Render this scene to a transparent image. + QRectF sr = scene.itemsBoundingRect(); + QImage image(sr.size().toSize(), QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter painter(&image); + scene.render(&painter); + + // Erase the area immediately underneath the rect. + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(rect->sceneBoundingRect().translated(-sr.topLeft()).adjusted(-0.5, -0.5, 0.5, 0.5), + Qt::transparent); + painter.end(); + + // Check that you get a truly transparent image back (i.e., the text was + // clipped away, so there should be no trails left after erasing only the + // rect's area). + QImage emptyImage(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied); + emptyImage.fill(0); + QCOMPARE(image, emptyImage); +} + +void tst_QGraphicsItem::itemClippingDiscovery() +{ + // A simple scene with an ellipse parent and two rect children, one a + // child of the other. + QGraphicsScene scene; + QGraphicsEllipseItem *clipItem = scene.addEllipse(0, 0, 100, 100); + QGraphicsRectItem *leftRectItem = scene.addRect(0, 0, 50, 100); + QGraphicsRectItem *rightRectItem = scene.addRect(50, 0, 50, 100); + leftRectItem->setParentItem(clipItem); + rightRectItem->setParentItem(clipItem); + + // The rects item are both visible at these points. + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)leftRectItem); + QCOMPARE(scene.itemAt(90, 90), (QGraphicsItem *)rightRectItem); + + // The ellipse clips the rects now. + clipItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + // The rect items are no longer visible at these points. + QCOMPARE(scene.itemAt(10, 10), (QGraphicsItem *)0); + if (sizeof(qreal) != sizeof(double)) + QSKIP("This fails due to internal rounding errors", SkipSingle); + QCOMPARE(scene.itemAt(90, 90), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::ancestorFlags() +{ + QGraphicsItem *level1 = new QGraphicsRectItem; + QGraphicsItem *level21 = new QGraphicsRectItem; + level21->setParentItem(level1); + QGraphicsItem *level22 = new QGraphicsRectItem; + level22->setParentItem(level1); + QGraphicsItem *level31 = new QGraphicsRectItem; + level31->setParentItem(level21); + QGraphicsItem *level32 = new QGraphicsRectItem; + level32->setParentItem(level21); + + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 0); + + // HandlesChildEvents: 1) Root level sets a flag + level1->setHandlesChildEvents(true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // HandlesChildEvents: 2) Root level set it again + level1->setHandlesChildEvents(true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // HandlesChildEvents: 3) Root level unsets a flag + level1->setHandlesChildEvents(false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 0); + + // HandlesChildEvents: 4) Child item sets a flag + level21->setHandlesChildEvents(true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // HandlesChildEvents: 5) Parent item sets a flag + level1->setHandlesChildEvents(true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // HandlesChildEvents: 6) Child item unsets a flag + level21->setHandlesChildEvents(false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // HandlesChildEvents: 7) Parent item unsets a flag + level21->setHandlesChildEvents(true); + level1->setHandlesChildEvents(false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Reparent the child to root + level21->setParentItem(0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Reparent the child to level1 again. + level1->setHandlesChildEvents(true); + level21->setParentItem(level1); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Reparenting level31 back to level1. + level31->setParentItem(level1); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Reparenting level31 back to level21. + level31->setParentItem(0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + level31->setParentItem(level21); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Level1 doesn't handle child events + level1->setHandlesChildEvents(false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 1); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 1); + + // Nobody handles child events + level21->setHandlesChildEvents(false); + + for (int i = 0; i < 2; ++i) { + QGraphicsItem::GraphicsItemFlag flag = !i ? QGraphicsItem::ItemClipsChildrenToShape + : QGraphicsItem::ItemIgnoresTransformations; + int ancestorFlag = !i ? QGraphicsItemPrivate::AncestorClipsChildren + : QGraphicsItemPrivate::AncestorIgnoresTransformations; + + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 0); + + // HandlesChildEvents: 1) Root level sets a flag + level1->setFlag(flag, true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // HandlesChildEvents: 2) Root level set it again + level1->setFlag(flag, true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // HandlesChildEvents: 3) Root level unsets a flag + level1->setFlag(flag, false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 0); + + // HandlesChildEvents: 4) Child item sets a flag + level21->setFlag(flag, true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // HandlesChildEvents: 5) Parent item sets a flag + level1->setFlag(flag, true); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // HandlesChildEvents: 6) Child item unsets a flag + level21->setFlag(flag, false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // HandlesChildEvents: 7) Parent item unsets a flag + level21->setFlag(flag, true); + level1->setFlag(flag, false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Reparent the child to root + level21->setParentItem(0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Reparent the child to level1 again. + level1->setFlag(flag, true); + level21->setParentItem(level1); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Reparenting level31 back to level1. + level31->setParentItem(level1); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Reparenting level31 back to level21. + level31->setParentItem(0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + level31->setParentItem(level21); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level22->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Level1 doesn't handle child events + level1->setFlag(flag, false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), ancestorFlag); + QCOMPARE(int(level32->d_ptr->ancestorFlags), ancestorFlag); + + // Nobody handles child events + level21->setFlag(flag, false); + QCOMPARE(int(level1->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level21->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level22->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level31->d_ptr->ancestorFlags), 0); + QCOMPARE(int(level32->d_ptr->ancestorFlags), 0); + } + + delete level1; +} + +void tst_QGraphicsItem::untransformable() +{ + QGraphicsItem *item1 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item1->setZValue(1); + item1->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item1->rotate(45); + ((QGraphicsEllipseItem *)item1)->setBrush(Qt::red); + + QGraphicsItem *item2 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item2->setParentItem(item1); + item2->rotate(45); + item2->setPos(100, 0); + ((QGraphicsEllipseItem *)item2)->setBrush(Qt::green); + + QGraphicsItem *item3 = new QGraphicsEllipseItem(QRectF(-50, -50, 100, 100)); + item3->setParentItem(item2); + item3->setPos(100, 0); + ((QGraphicsEllipseItem *)item3)->setBrush(Qt::blue); + + QGraphicsScene scene(-500, -500, 1000, 1000); + scene.addItem(item1); + + QWidget topLevel; + QGraphicsView view(&scene,&topLevel); + view.resize(300, 300); + topLevel.show(); + view.scale(8, 8); + view.centerOn(0, 0); + +// Painting with the DiagCrossPattern is really slow on Mac +// when zoomed out. (The test times out). Task to fix is 155567. +#if !defined(Q_WS_MAC) || 1 + view.setBackgroundBrush(QBrush(Qt::black, Qt::DiagCrossPattern)); +#endif + + QTest::qWaitForWindowShown(&view); + + for (int i = 0; i < 10; ++i) { + QPoint center = view.viewport()->rect().center(); + QCOMPARE(view.itemAt(center), item1); + QCOMPARE(view.itemAt(center - QPoint(40, 0)), item1); + QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item1); + QCOMPARE(view.itemAt(center - QPoint(0, 40)), item1); + QCOMPARE(view.itemAt(center - QPoint(0, -40)), item1); + + center += QPoint(70, 70); + QCOMPARE(view.itemAt(center - QPoint(40, 0)), item2); + QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item2); + QCOMPARE(view.itemAt(center - QPoint(0, 40)), item2); + QCOMPARE(view.itemAt(center - QPoint(0, -40)), item2); + + center += QPoint(0, 100); + QCOMPARE(view.itemAt(center - QPoint(40, 0)), item3); + QCOMPARE(view.itemAt(center - QPoint(-40, 0)), item3); + QCOMPARE(view.itemAt(center - QPoint(0, 40)), item3); + QCOMPARE(view.itemAt(center - QPoint(0, -40)), item3); + + view.scale(0.5, 0.5); + view.rotate(13); + view.shear(qreal(0.01), qreal(0.01)); + view.translate(10, 10); + QTest::qWait(25); + } +} + +class ContextMenuItem : public QGraphicsRectItem +{ +public: + ContextMenuItem() + : ignoreEvent(true), gotEvent(false), eventWasAccepted(false) + { } + bool ignoreEvent; + bool gotEvent; + bool eventWasAccepted; +protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) + { + gotEvent = true; + eventWasAccepted = event->isAccepted(); + if (ignoreEvent) + event->ignore(); + } +}; + +void tst_QGraphicsItem::contextMenuEventPropagation() +{ + ContextMenuItem *bottomItem = new ContextMenuItem; + bottomItem->setRect(0, 0, 100, 100); + ContextMenuItem *topItem = new ContextMenuItem; + topItem->setParentItem(bottomItem); + topItem->setRect(0, 0, 100, 100); + + QGraphicsScene scene; + + QGraphicsView view(&scene); + view.setAlignment(Qt::AlignLeft | Qt::AlignTop); + view.show(); + view.resize(200, 200); + QTest::qWaitForWindowShown(&view); + QTest::qWait(20); + + QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(10, 10), + view.viewport()->mapToGlobal(QPoint(10, 10))); + event.ignore(); + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!event.isAccepted()); + + scene.addItem(bottomItem); + topItem->ignoreEvent = true; + bottomItem->ignoreEvent = true; + + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(!event.isAccepted()); + QCOMPARE(topItem->gotEvent, true); + QCOMPARE(topItem->eventWasAccepted, true); + QCOMPARE(bottomItem->gotEvent, true); + QCOMPARE(bottomItem->eventWasAccepted, true); + + topItem->ignoreEvent = false; + topItem->gotEvent = false; + bottomItem->gotEvent = false; + + QApplication::sendEvent(view.viewport(), &event); + QVERIFY(event.isAccepted()); + QCOMPARE(topItem->gotEvent, true); + QCOMPARE(bottomItem->gotEvent, false); + QCOMPARE(topItem->eventWasAccepted, true); +} + +void tst_QGraphicsItem::itemIsMovable() +{ + QGraphicsRectItem *rect = new QGraphicsRectItem(-50, -50, 100, 100); + rect->setFlag(QGraphicsItem::ItemIsMovable); + + QGraphicsScene scene; + scene.addItem(rect); + + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(0, 0)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::LeftButton); + event.setScenePos(QPointF(10, 10)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(10, 10)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::RightButton); + event.setScenePos(QPointF(20, 20)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(10, 10)); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseMove); + event.setButtons(Qt::LeftButton); + event.setScenePos(QPointF(30, 30)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(rect->pos(), QPointF(30, 30)); +} + +class ItemAddScene : public QGraphicsScene +{ + Q_OBJECT +public: + ItemAddScene() + { + QTimer::singleShot(500, this, SLOT(newTextItem())); + } + +public slots: + void newTextItem() + { + // Add a text item + QGraphicsItem *item = new QGraphicsTextItem("This item will not ensure that it's visible", 0, this); + item->setPos(.0, .0); + item->show(); + } +}; + +void tst_QGraphicsItem::task141694_textItemEnsureVisible() +{ + ItemAddScene scene; + scene.setSceneRect(-1000, -1000, 2000, 2000); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view); + + view.ensureVisible(-1000, -1000, 5, 5); + int hscroll = view.horizontalScrollBar()->value(); + int vscroll = view.verticalScrollBar()->value(); + + QTest::qWait(10); + + // This should not cause the view to scroll + QTRY_COMPARE(view.horizontalScrollBar()->value(), hscroll); + QCOMPARE(view.verticalScrollBar()->value(), vscroll); +} + +void tst_QGraphicsItem::task128696_textItemEnsureMovable() +{ + QGraphicsTextItem *item = new QGraphicsTextItem; + item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); + item->setTextInteractionFlags(Qt::TextEditorInteraction); + item->setPlainText("abc de\nf ghi\n j k l"); + + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + scene.addItem(item); + + QGraphicsView view(&scene); + view.setFixedSize(200, 200); + view.show(); + + QGraphicsSceneMouseEvent event1(QEvent::GraphicsSceneMousePress); + event1.setScenePos(QPointF(0, 0)); + event1.setButton(Qt::LeftButton); + event1.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + + QGraphicsSceneMouseEvent event2(QEvent::GraphicsSceneMouseMove); + event2.setScenePos(QPointF(10, 10)); + event2.setButton(Qt::LeftButton); + event2.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event2); + QCOMPARE(item->pos(), QPointF(10, 10)); +} + +void tst_QGraphicsItem::task177918_lineItemUndetected() +{ + QGraphicsScene scene; + QGraphicsLineItem *line = scene.addLine(10, 10, 10, 10); + QCOMPARE(line->boundingRect(), QRectF(10, 10, 0, 0)); + + QVERIFY(!scene.items(9, 9, 2, 2, Qt::IntersectsItemShape).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::ContainsItemShape).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::IntersectsItemBoundingRect).isEmpty()); + QVERIFY(!scene.items(9, 9, 2, 2, Qt::ContainsItemBoundingRect).isEmpty()); +} + +void tst_QGraphicsItem::task240400_clickOnTextItem_data() +{ + QTest::addColumn("flags"); + QTest::addColumn("textFlags"); + QTest::newRow("editor, noflags") << 0 << int(Qt::TextEditorInteraction); + QTest::newRow("editor, movable") << int(QGraphicsItem::ItemIsMovable) << int(Qt::TextEditorInteraction); + QTest::newRow("editor, selectable") << int(QGraphicsItem::ItemIsSelectable) << int(Qt::TextEditorInteraction); + QTest::newRow("editor, movable | selectable") << int(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable) + << int(Qt::TextEditorInteraction); + QTest::newRow("noninteractive, noflags") << 0 << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, movable") << int(QGraphicsItem::ItemIsMovable) << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, selectable") << int(QGraphicsItem::ItemIsSelectable) << int(Qt::NoTextInteraction); + QTest::newRow("noninteractive, movable | selectable") << int(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable) + << int(Qt::NoTextInteraction); +} + +void tst_QGraphicsItem::task240400_clickOnTextItem() +{ + QFETCH(int, flags); + QFETCH(int, textFlags); + + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsTextItem *item = scene.addText("Hello"); + item->setFlags(QGraphicsItem::GraphicsItemFlags(flags)); + item->setTextInteractionFlags(Qt::TextInteractionFlags(textFlags)); + bool focusable = (item->flags() & QGraphicsItem::ItemIsFocusable); + QVERIFY(textFlags ? focusable : !focusable); + + int column = item->textCursor().columnNumber(); + QCOMPARE(column, 0); + + QVERIFY(!item->hasFocus()); + + // Click in the top-left corner of the item + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1)); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event); + } + if (flags || textFlags) + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + else + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(item->sceneBoundingRect().topLeft() + QPointF(0.1, 0.1)); + event.setButton(Qt::LeftButton); + event.setButtons(0); + QApplication::sendEvent(&scene, &event); + } + if (textFlags) + QVERIFY(item->hasFocus()); + else + QVERIFY(!item->hasFocus()); + QVERIFY(!scene.mouseGrabberItem()); + bool selectable = (flags & QGraphicsItem::ItemIsSelectable); + QVERIFY(selectable ? item->isSelected() : !item->isSelected()); + + // Now click in the middle and check that the cursor moved. + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setScenePos(item->sceneBoundingRect().center()); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + QApplication::sendEvent(&scene, &event); + } + if (flags || textFlags) + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)item); + else + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setScenePos(item->sceneBoundingRect().center()); + event.setButton(Qt::LeftButton); + event.setButtons(0); + QApplication::sendEvent(&scene, &event); + } + if (textFlags) + QVERIFY(item->hasFocus()); + else + QVERIFY(!item->hasFocus()); + QVERIFY(!scene.mouseGrabberItem()); + + QVERIFY(selectable ? item->isSelected() : !item->isSelected()); + + // + if (textFlags & Qt::TextEditorInteraction) + QVERIFY(item->textCursor().columnNumber() > column); + else + QCOMPARE(item->textCursor().columnNumber(), 0); +} + +class TextItem : public QGraphicsSimpleTextItem +{ +public: + TextItem(const QString& text) : QGraphicsSimpleTextItem(text) + { + updates = 0; + } + + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) + { + updates++; + QGraphicsSimpleTextItem::paint(painter, option, widget); + } + + int updates; +}; + +void tst_QGraphicsItem::ensureUpdateOnTextItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + TextItem *text1 = new TextItem(QLatin1String("123")); + scene.addItem(text1); + qApp->processEvents(); + QTRY_COMPARE(text1->updates,1); + + //same bouding rect but we have to update + text1->setText(QLatin1String("321")); + qApp->processEvents(); + QTRY_COMPARE(text1->updates,2); +} + +void tst_QGraphicsItem::task243707_addChildBeforeParent() +{ + // Task reports that adding the child before the parent leads to an + // inconsistent internal state that can cause a crash. This test shows + // one such crash. + QGraphicsScene scene; + QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget *widget2 = new QGraphicsWidget(widget); + scene.addItem(widget2); + QVERIFY(!widget2->parentItem()); + scene.addItem(widget); + QVERIFY(!widget->commonAncestorItem(widget2)); + QVERIFY(!widget2->commonAncestorItem(widget)); +} + +void tst_QGraphicsItem::task197802_childrenVisibility() +{ + QGraphicsScene scene; + QGraphicsRectItem item(QRectF(0,0,20,20)); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(0,0,10,10), &item); + scene.addItem(&item); + + //freshly created: both visible + QVERIFY(item.isVisible()); + QVERIFY(item2->isVisible()); + + //hide child: parent visible, child not + item2->hide(); + QVERIFY(item.isVisible()); + QVERIFY(!item2->isVisible()); + + //hide parent: parent and child invisible + item.hide(); + QVERIFY(!item.isVisible()); + QVERIFY(!item2->isVisible()); + + //ask to show the child: parent and child invisible anyways + item2->show(); + QVERIFY(!item.isVisible()); + QVERIFY(!item2->isVisible()); + + //show the parent: both parent and child visible + item.show(); + QVERIFY(item.isVisible()); + QVERIFY(item2->isVisible()); + + delete item2; +} + +void tst_QGraphicsItem::boundingRegion_data() +{ + QTest::addColumn("line"); + QTest::addColumn("granularity"); + QTest::addColumn("transform"); + QTest::addColumn("expectedRegion"); + + QTest::newRow("(0, 0, 10, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(0.0) << QTransform() + << QRegion(QRect(0, 0, 10, 10)); +#if 0 + { + QRegion r; + r += QRect(0, 0, 6, 2); + r += QRect(0, 2, 8, 2); + r += QRect(0, 4, 10, 2); + r += QRect(2, 6, 8, 2); + r += QRect(4, 8, 6, 2); + QTest::newRow("(0, 0, 10, 10) | 0.5 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(0.5) << QTransform() << r; + } + { + QRegion r; + r += QRect(0, 0, 4, 1); r += QRect(0, 1, 5, 1); r += QRect(0, 2, 6, 1); + r += QRect(0, 3, 7, 1); r += QRect(1, 4, 7, 1); r += QRect(2, 5, 7, 1); + r += QRect(3, 6, 7, 1); r += QRect(4, 7, 6, 1); r += QRect(5, 8, 5, 1); + r += QRect(6, 9, 4, 1); + QTest::newRow("(0, 0, 10, 10) | 1.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 10) << qreal(1.0) << QTransform() << r; + } +#endif + QTest::newRow("(0, 0, 10, 0) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 10, 0) << qreal(0.0) << QTransform() + << QRegion(QRect(0, 0, 10, 1)); + QTest::newRow("(0, 0, 10, 0) | 0.5 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(0.5) << QTransform() + << QRegion(QRect(0, 0, 10, 1)); + QTest::newRow("(0, 0, 10, 0) | 1.0 | identity | {(0, 0, 10, 1)}") << QLineF(0, 0, 10, 0) << qreal(1.0) << QTransform() + << QRegion(QRect(0, 0, 10, 1)); + QTest::newRow("(0, 0, 0, 10) | 0.0 | identity | {(0, 0, 10, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.0) << QTransform() + << QRegion(QRect(0, 0, 1, 10)); + QTest::newRow("(0, 0, 0, 10) | 0.5 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(0.5) << QTransform() + << QRegion(QRect(0, 0, 1, 10)); + QTest::newRow("(0, 0, 0, 10) | 1.0 | identity | {(0, 0, 1, 10)}") << QLineF(0, 0, 0, 10) << qreal(1.0) << QTransform() + << QRegion(QRect(0, 0, 1, 10)); +} + +void tst_QGraphicsItem::boundingRegion() +{ + QFETCH(QLineF, line); + QFETCH(qreal, granularity); + QFETCH(QTransform, transform); + QFETCH(QRegion, expectedRegion); + + QGraphicsLineItem item(line); + QCOMPARE(item.boundingRegionGranularity(), qreal(0.0)); + item.setBoundingRegionGranularity(granularity); + QCOMPARE(item.boundingRegionGranularity(), granularity); + QCOMPARE(item.boundingRegion(transform), expectedRegion); +} + +void tst_QGraphicsItem::itemTransform_parentChild() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(0, 0, 100, 100); + QGraphicsItem *child = scene.addRect(0, 0, 100, 100); + child->setParentItem(parent); + child->setPos(10, 10); + child->scale(2, 2); + child->rotate(90); + + QCOMPARE(child->itemTransform(parent).map(QPointF(10, 10)), QPointF(-10, 30)); + QCOMPARE(parent->itemTransform(child).map(QPointF(-10, 30)), QPointF(10, 10)); +} + +void tst_QGraphicsItem::itemTransform_siblings() +{ + QGraphicsScene scene; + QGraphicsItem *parent = scene.addRect(0, 0, 100, 100); + QGraphicsItem *brother = scene.addRect(0, 0, 100, 100); + QGraphicsItem *sister = scene.addRect(0, 0, 100, 100); + parent->scale(10, 5); + parent->rotate(-180); + parent->shear(2, 3); + + brother->setParentItem(parent); + sister->setParentItem(parent); + + brother->setPos(10, 10); + brother->scale(2, 2); + brother->rotate(90); + sister->setPos(10, 10); + sister->scale(2, 2); + sister->rotate(90); + + QCOMPARE(brother->itemTransform(sister).map(QPointF(10, 10)), QPointF(10, 10)); + QCOMPARE(sister->itemTransform(brother).map(QPointF(10, 10)), QPointF(10, 10)); +} + +void tst_QGraphicsItem::itemTransform_unrelated() +{ + QGraphicsScene scene; + QGraphicsItem *stranger1 = scene.addRect(0, 0, 100, 100); + QGraphicsItem *stranger2 = scene.addRect(0, 0, 100, 100); + stranger1->setPos(10, 10); + stranger1->scale(2, 2); + stranger1->rotate(90); + stranger2->setPos(10, 10); + stranger2->scale(2, 2); + stranger2->rotate(90); + + QCOMPARE(stranger1->itemTransform(stranger2).map(QPointF(10, 10)), QPointF(10, 10)); + QCOMPARE(stranger2->itemTransform(stranger1).map(QPointF(10, 10)), QPointF(10, 10)); +} + +void tst_QGraphicsItem::opacity_data() +{ + QTest::addColumn("p_opacity"); + QTest::addColumn("p_opacityFlags"); + QTest::addColumn("c1_opacity"); + QTest::addColumn("c1_opacityFlags"); + QTest::addColumn("c2_opacity"); + QTest::addColumn("c2_opacityFlags"); + QTest::addColumn("p_effectiveOpacity"); + QTest::addColumn("c1_effectiveOpacity"); + QTest::addColumn("c2_effectiveOpacity"); + QTest::addColumn("c3_effectiveOpacity"); + + // Modify the opacity and see how it propagates + QTest::newRow("A: 1.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("B: 0.5 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5); + QTest::newRow("C: 0.5 0 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.1) << 0 << qreal(1.0) << 0 + << qreal(0.5) << qreal(0.05) << qreal(0.05) << qreal(0.05); + QTest::newRow("D: 0.0 0 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0); + + // Parent doesn't propagate to children - now modify the opacity and see how it propagates + int flags = QGraphicsItem::ItemDoesntPropagateOpacityToChildren; + QTest::newRow("E: 1.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("F: 0.5 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("G: 0.5 2 0.1 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << flags << qreal(0.1) << 0 << qreal(1.0) << 0 + << qreal(0.5) << qreal(0.1) << qreal(0.1) << qreal(0.1); + QTest::newRow("H: 0.0 2 1.0 0 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << flags << qreal(1.0) << 0 << qreal(1.0) << 0 + << qreal(0.0) << qreal(1.0) << qreal(1.0) << qreal(1.0); + + // Child ignores parent - now modify the opacity and see how it propagates + flags = QGraphicsItem::ItemIgnoresParentOpacity; + QTest::newRow("I: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 << qreal(1.0) << flags << qreal(1.0) << 0 + << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("J: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 << qreal(0.5) << flags << qreal(0.5) << 0 + << qreal(0.5) << qreal(0.5) << qreal(0.25) << qreal(0.25); + QTest::newRow("K: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.2) << 0 << qreal(0.2) << flags << qreal(0.2) << 0 + << qreal(0.2) << qreal(0.2) << qreal(0.04) << qreal(0.04); + QTest::newRow("L: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.0) << 0 << qreal(0.0) << flags << qreal(0.0) << 0 + << qreal(0.0) << qreal(0.0) << qreal(0.0) << qreal(0.0); + + // Child ignores parent and doesn't propagate - now modify the opacity and see how it propagates + flags = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren; + QTest::newRow("M: 1.0 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p + << qreal(1.0) << flags // c1 (no prop) + << qreal(1.0) << 0 // c2 + << qreal(1.0) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("M: 0.5 0 1.0 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p + << qreal(1.0) << flags // c1 (no prop) + << qreal(1.0) << 0 // c2 + << qreal(0.5) << qreal(1.0) << qreal(1.0) << qreal(1.0); + QTest::newRow("M: 0.5 0 0.5 1 1.0 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p + << qreal(0.5) << flags // c1 (no prop) + << qreal(1.0) << 0 // c2 + << qreal(0.5) << qreal(0.5) << qreal(1.0) << qreal(1.0); + QTest::newRow("M: 0.5 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(0.5) << 0 // p + << qreal(0.5) << flags // c1 (no prop) + << qreal(0.5) << 0 // c2 + << qreal(0.5) << qreal(0.5) << qreal(0.5) << qreal(0.5); + QTest::newRow("M: 1.0 0 0.5 1 0.5 1.0 1.0 1.0 1.0") << qreal(1.0) << 0 // p + << qreal(0.5) << flags // c1 (no prop) + << qreal(0.5) << 0 // c2 + << qreal(1.0) << qreal(0.5) << qreal(0.5) << qreal(0.5); +} + +void tst_QGraphicsItem::opacity() +{ + QFETCH(qreal, p_opacity); + QFETCH(int, p_opacityFlags); + QFETCH(qreal, p_effectiveOpacity); + QFETCH(qreal, c1_opacity); + QFETCH(int, c1_opacityFlags); + QFETCH(qreal, c1_effectiveOpacity); + QFETCH(qreal, c2_opacity); + QFETCH(int, c2_opacityFlags); + QFETCH(qreal, c2_effectiveOpacity); + QFETCH(qreal, c3_effectiveOpacity); + + QGraphicsRectItem *p = new QGraphicsRectItem; + QGraphicsRectItem *c1 = new QGraphicsRectItem(p); + QGraphicsRectItem *c2 = new QGraphicsRectItem(c1); + QGraphicsRectItem *c3 = new QGraphicsRectItem(c2); + + QCOMPARE(p->opacity(), qreal(1.0)); + QCOMPARE(p->effectiveOpacity(), qreal(1.0)); + int opacityMask = QGraphicsItem::ItemIgnoresParentOpacity | QGraphicsItem::ItemDoesntPropagateOpacityToChildren; + QVERIFY(!(p->flags() & opacityMask)); + + p->setOpacity(p_opacity); + c1->setOpacity(c1_opacity); + c2->setOpacity(c2_opacity); + p->setFlags(QGraphicsItem::GraphicsItemFlags(p->flags() | p_opacityFlags)); + c1->setFlags(QGraphicsItem::GraphicsItemFlags(c1->flags() | c1_opacityFlags)); + c2->setFlags(QGraphicsItem::GraphicsItemFlags(c2->flags() | c2_opacityFlags)); + + QCOMPARE(int(p->flags() & opacityMask), p_opacityFlags); + QCOMPARE(int(c1->flags() & opacityMask), c1_opacityFlags); + QCOMPARE(int(c2->flags() & opacityMask), c2_opacityFlags); + QCOMPARE(p->opacity(), p_opacity); + QCOMPARE(p->effectiveOpacity(), p_effectiveOpacity); + QCOMPARE(c1->effectiveOpacity(), c1_effectiveOpacity); + QCOMPARE(c2->effectiveOpacity(), c2_effectiveOpacity); + QCOMPARE(c3->effectiveOpacity(), c3_effectiveOpacity); +} + +void tst_QGraphicsItem::opacity2() +{ + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + EventTester *grandChild = new EventTester(child); + + QGraphicsScene scene; + scene.addItem(parent); + + MyGraphicsView view(&scene); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints >= 1); + +#define RESET_REPAINT_COUNTERS \ + parent->repaints = 0; \ + child->repaints = 0; \ + grandChild->repaints = 0; \ + view.repaints = 0; + + RESET_REPAINT_COUNTERS + + child->setOpacity(0.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 0); + + RESET_REPAINT_COUNTERS + + child->setOpacity(1.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + + RESET_REPAINT_COUNTERS + + parent->setOpacity(0.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 0); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 0); + + RESET_REPAINT_COUNTERS + + parent->setOpacity(1.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + + grandChild->setFlag(QGraphicsItem::ItemIgnoresParentOpacity); + RESET_REPAINT_COUNTERS + + child->setOpacity(0.0); + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 1); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 1); + + RESET_REPAINT_COUNTERS + + child->setOpacity(0.0); // Already 0.0; no change. + QTest::qWait(10); + QTRY_COMPARE(view.repaints, 0); + QCOMPARE(parent->repaints, 0); + QCOMPARE(child->repaints, 0); + QCOMPARE(grandChild->repaints, 0); +} + +void tst_QGraphicsItem::opacityZeroUpdates() +{ + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + + child->setPos(10, 10); + + QGraphicsScene scene; + scene.addItem(parent); + + MyGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + view.reset(); + parent->setOpacity(0.0); + + QTest::qWait(20); + + // transforming items bounding rect to view coordinates + const QRect childDeviceBoundingRect = child->deviceTransform(view.viewportTransform()) + .mapRect(child->boundingRect()).toRect(); + const QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform()) + .mapRect(parent->boundingRect()).toRect(); + + QRegion expectedRegion = parentDeviceBoundingRect.adjusted(-2, -2, 2, 2); + expectedRegion += childDeviceBoundingRect.adjusted(-2, -2, 2, 2); + + COMPARE_REGIONS(view.paintedRegion, expectedRegion); +} + +class StacksBehindParentHelper : public QGraphicsRectItem +{ +public: + StacksBehindParentHelper(QList *paintedItems, const QRectF &rect, QGraphicsItem *parent = 0) + : QGraphicsRectItem(rect, parent), paintedItems(paintedItems) + { } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + QGraphicsRectItem::paint(painter, option, widget); + paintedItems->append(this); + } + +private: + QList *paintedItems; +}; + +void tst_QGraphicsItem::itemStacksBehindParent() +{ + StacksBehindParentHelper *parent1 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50)); + StacksBehindParentHelper *child11 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent1); + StacksBehindParentHelper *grandChild111 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child11); + StacksBehindParentHelper *child12 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent1); + StacksBehindParentHelper *grandChild121 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child12); + + StacksBehindParentHelper *parent2 = new StacksBehindParentHelper(&paintedItems, QRectF(0, 0, 100, 50)); + StacksBehindParentHelper *child21 = new StacksBehindParentHelper(&paintedItems, QRectF(-10, 10, 50, 50), parent2); + StacksBehindParentHelper *grandChild211 = new StacksBehindParentHelper(&paintedItems, QRectF(-20, 20, 50, 50), child21); + StacksBehindParentHelper *child22 = new StacksBehindParentHelper(&paintedItems, QRectF(60, 10, 50, 50), parent2); + StacksBehindParentHelper *grandChild221 = new StacksBehindParentHelper(&paintedItems, QRectF(70, 20, 50, 50), child22); + + parent1->setData(0, "parent1"); + child11->setData(0, "child11"); + grandChild111->setData(0, "grandChild111"); + child12->setData(0, "child12"); + grandChild121->setData(0, "grandChild121"); + parent2->setData(0, "parent2"); + child21->setData(0, "child21"); + grandChild211->setData(0, "grandChild211"); + child22->setData(0, "child22"); + grandChild221->setData(0, "grandChild221"); + + // Disambiguate siblings + parent1->setZValue(1); + child11->setZValue(1); + child21->setZValue(1); + + QGraphicsScene scene; + scene.addItem(parent1); + scene.addItem(parent2); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!paintedItems.isEmpty()); + QTest::qWait(100); + paintedItems.clear(); + view.viewport()->update(); + QApplication::processEvents(); + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList() + << grandChild111 << child11 + << grandChild121 << child12 << parent1 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QTRY_COMPARE(paintedItems, QList() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << parent1 << child12 << grandChild121 + << child11 << grandChild111); + + child11->setFlag(QGraphicsItem::ItemStacksBehindParent); + scene.update(); + paintedItems.clear(); + QApplication::processEvents(); + + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList() + << grandChild121 << child12 << parent1 + << grandChild111 << child11 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child11 << grandChild111 + << parent1 << child12 << grandChild121); + + child12->setFlag(QGraphicsItem::ItemStacksBehindParent); + paintedItems.clear(); + scene.update(); + QApplication::processEvents(); + + QTRY_COMPARE(scene.items(0, 0, 100, 100), (QList() + << parent1 << grandChild111 << child11 + << grandChild121 << child12 + << grandChild211 << child21 + << grandChild221 << child22 << parent2)); + QCOMPARE(paintedItems, QList() + << parent2 << child22 << grandChild221 + << child21 << grandChild211 + << child12 << grandChild121 + << child11 << grandChild111 << parent1); +} + +class ClippingAndTransformsScene : public QGraphicsScene +{ +public: + QList drawnItems; +protected: + void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[], QWidget *widget = 0) + { + drawnItems.clear(); + for (int i = 0; i < numItems; ++i) + drawnItems << items[i]; + QGraphicsScene::drawItems(painter, numItems, items, options, widget); + } +}; + +void tst_QGraphicsItem::nestedClipping() +{ + ClippingAndTransformsScene scene; + scene.setSceneRect(-50, -50, 200, 200); + + QGraphicsRectItem *root = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + root->setBrush(QColor(0, 0, 255)); + root->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QGraphicsRectItem *l1 = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + l1->setParentItem(root); + l1->setPos(-50, 0); + l1->setBrush(QColor(255, 0, 0)); + l1->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QGraphicsEllipseItem *l2 = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100)); + l2->setParentItem(l1); + l2->setPos(50, 50); + l2->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + l2->setBrush(QColor(255, 255, 0)); + QGraphicsRectItem *l3 = new QGraphicsRectItem(QRectF(0, 0, 25, 25)); + l3->setParentItem(l2); + l3->setBrush(QColor(0, 255, 0)); + l3->setPos(50 - 12, -12); + + scene.addItem(root); + + root->setData(0, "root"); + l1->setData(0, "l1"); + l2->setData(0, "l2"); + l3->setData(0, "l3"); + + QGraphicsView view(&scene); + view.setOptimizationFlag(QGraphicsView::IndirectPainting); + view.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(25); + + QList expected; + expected << root << l1 << l2 << l3; + QTRY_COMPARE(scene.drawnItems, expected); + + QImage image(200, 200, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + + QPainter painter(&image); + scene.render(&painter); + painter.end(); + + // Check transparent areas + QCOMPARE(image.pixel(100, 25), qRgba(0, 0, 0, 0)); + QCOMPARE(image.pixel(100, 175), qRgba(0, 0, 0, 0)); + QCOMPARE(image.pixel(25, 100), qRgba(0, 0, 0, 0)); + QCOMPARE(image.pixel(175, 100), qRgba(0, 0, 0, 0)); + QCOMPARE(image.pixel(70, 80), qRgba(255, 0, 0, 255)); + QCOMPARE(image.pixel(80, 130), qRgba(255, 255, 0, 255)); + QCOMPARE(image.pixel(92, 105), qRgba(0, 255, 0, 255)); + QCOMPARE(image.pixel(105, 105), qRgba(0, 0, 255, 255)); +#if 0 + // Enable this to compare if the test starts failing. + image.save("nestedClipping_reference.png"); +#endif +} + +class TransformDebugItem : public QGraphicsRectItem +{ +public: + TransformDebugItem() + : QGraphicsRectItem(QRectF(-10, -10, 20, 20)) + { + setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256)); + } + + QTransform x; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0) + { + x = painter->worldTransform(); + QGraphicsRectItem::paint(painter, option, widget); + } +}; + +void tst_QGraphicsItem::nestedClippingTransforms() +{ + TransformDebugItem *rootClipper = new TransformDebugItem; + rootClipper->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + TransformDebugItem *child = new TransformDebugItem; + child->setParentItem(rootClipper); + child->setPos(2, 2); + TransformDebugItem *grandChildClipper = new TransformDebugItem; + grandChildClipper->setParentItem(child); + grandChildClipper->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + grandChildClipper->setPos(4, 4); + TransformDebugItem *greatGrandChild = new TransformDebugItem; + greatGrandChild->setPos(2, 2); + greatGrandChild->setParentItem(grandChildClipper); + TransformDebugItem *grandChildClipper2 = new TransformDebugItem; + grandChildClipper2->setParentItem(child); + grandChildClipper2->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + grandChildClipper2->setPos(8, 8); + TransformDebugItem *greatGrandChild2 = new TransformDebugItem; + greatGrandChild2->setPos(2, 2); + greatGrandChild2->setParentItem(grandChildClipper2); + TransformDebugItem *grandChildClipper3 = new TransformDebugItem; + grandChildClipper3->setParentItem(child); + grandChildClipper3->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + grandChildClipper3->setPos(12, 12); + TransformDebugItem *greatGrandChild3 = new TransformDebugItem; + greatGrandChild3->setPos(2, 2); + greatGrandChild3->setParentItem(grandChildClipper3); + + QGraphicsScene scene; + scene.addItem(rootClipper); + + QImage image(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter p(&image); + scene.render(&p); + p.end(); + + QCOMPARE(rootClipper->x, QTransform(1, 0, 0, 0, 1, 0, 10, 10, 1)); + QCOMPARE(child->x, QTransform(1, 0, 0, 0, 1, 0, 12, 12, 1)); + QCOMPARE(grandChildClipper->x, QTransform(1, 0, 0, 0, 1, 0, 16, 16, 1)); + QCOMPARE(greatGrandChild->x, QTransform(1, 0, 0, 0, 1, 0, 18, 18, 1)); + QCOMPARE(grandChildClipper2->x, QTransform(1, 0, 0, 0, 1, 0, 20, 20, 1)); + QCOMPARE(greatGrandChild2->x, QTransform(1, 0, 0, 0, 1, 0, 22, 22, 1)); + QCOMPARE(grandChildClipper3->x, QTransform(1, 0, 0, 0, 1, 0, 24, 24, 1)); + QCOMPARE(greatGrandChild3->x, QTransform(1, 0, 0, 0, 1, 0, 26, 26, 1)); +} + +void tst_QGraphicsItem::sceneTransformCache() +{ + // Test that an item's scene transform is updated correctly when the + // parent is transformed. + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + QGraphicsRectItem *rect2 = scene.addRect(0, 0, 100, 100); + rect2->setParentItem(rect); + rect2->rotate(90); + rect->translate(0, 50); + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + rect->translate(0, 100); + QTransform x; + x.translate(0, 150); + x.rotate(90); + QCOMPARE(rect2->sceneTransform(), x); + + scene.removeItem(rect); + + //Crazy use case : rect4 child of rect3 so the transformation of rect4 will be cached.Good! + //We remove rect4 from the scene, then the validTransform bit flag is set to 0 and the index of the cache + //add to the freeTransformSlots. The problem was that sceneTransformIndex was not set to -1 so if a new item arrive + //with a child (rect6) that will be cached then it will take the freeSlot (ex rect4) and put it his transform. But if rect4 is + //added back to the scene then it will set the transform to his old sceneTransformIndex value that will erase the new + //value of rect6 so rect6 transform will be wrong. + QGraphicsRectItem *rect3 = scene.addRect(0, 0, 100, 100); + QGraphicsRectItem *rect4 = scene.addRect(0, 0, 100, 100); + rect3->setPos(QPointF(10,10)); + + rect4->setParentItem(rect3); + rect4->setPos(QPointF(10,10)); + + QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(20,20)); + + scene.removeItem(rect4); + //rect4 transform is local only + QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10)); + + QGraphicsRectItem *rect5 = scene.addRect(0, 0, 100, 100); + QGraphicsRectItem *rect6 = scene.addRect(0, 0, 100, 100); + rect5->setPos(QPointF(20,20)); + + rect6->setParentItem(rect5); + rect6->setPos(QPointF(10,10)); + //test if rect6 transform is ok + QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30)); + + scene.addItem(rect4); + + QCOMPARE(rect4->mapToScene(rect4->boundingRect().topLeft()), QPointF(10,10)); + //test if rect6 transform is still correct + QCOMPARE(rect6->mapToScene(rect6->boundingRect().topLeft()), QPointF(30,30)); +} + +void tst_QGraphicsItem::tabChangesFocus_data() +{ + QTest::addColumn("tabChangesFocus"); + QTest::newRow("tab changes focus") << true; + QTest::newRow("tab doesn't change focus") << false; +} + +void tst_QGraphicsItem::tabChangesFocus() +{ + QFETCH(bool, tabChangesFocus); + + QGraphicsScene scene; + QGraphicsTextItem *item = scene.addText("Hello"); + item->setTabChangesFocus(tabChangesFocus); + item->setTextInteractionFlags(Qt::TextEditorInteraction); + item->setFocus(); + + QDial *dial1 = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QDial *dial2 = new QDial; + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(dial1); + layout->addWidget(view); + layout->addWidget(dial2); + + QWidget widget; + widget.setLayout(layout); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(2000); + + QTRY_VERIFY(scene.isActive()); + + dial1->setFocus(); + QTest::qWait(15); + QTRY_VERIFY(dial1->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(15); + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(item->hasFocus()); + + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(15); + + if (tabChangesFocus) { + QTRY_VERIFY(!view->hasFocus()); + QTRY_VERIFY(!item->hasFocus()); + QTRY_VERIFY(dial2->hasFocus()); + } else { + QTRY_VERIFY(view->hasFocus()); + QTRY_VERIFY(item->hasFocus()); + QCOMPARE(item->toPlainText(), QString("\tHello")); + } +} + +void tst_QGraphicsItem::cacheMode() +{ + QGraphicsScene scene(0, 0, 100, 100); + QGraphicsView view(&scene); + view.resize(150, 150); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + + // Increase the probability of window activation + // not causing another repaint of test items. + QTest::qWait(50); + + EventTester *tester = new EventTester; + EventTester *testerChild = new EventTester; + testerChild->setParentItem(tester); + EventTester *testerChild2 = new EventTester; + testerChild2->setParentItem(testerChild); + testerChild2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + + scene.addItem(tester); + QTest::qWait(10); + + for (int i = 0; i < 2; ++i) { + // No visual change. + QTRY_COMPARE(tester->repaints, 1); + QCOMPARE(testerChild->repaints, 1); + QCOMPARE(testerChild2->repaints, 1); + tester->setCacheMode(QGraphicsItem::NoCache); + testerChild->setCacheMode(QGraphicsItem::NoCache); + testerChild2->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 1); + QCOMPARE(testerChild->repaints, 1); + QCOMPARE(testerChild2->repaints, 1); + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + testerChild->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + testerChild2->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(25); + } + + // The first move causes a repaint as the item is painted into its pixmap. + // (Only occurs if the item has previously been painted without cache). + tester->setPos(10, 10); + testerChild->setPos(10, 10); + testerChild2->setPos(10, 10); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 2); + QCOMPARE(testerChild->repaints, 2); + QCOMPARE(testerChild2->repaints, 2); + + // Consecutive moves should not repaint. + tester->setPos(20, 20); + testerChild->setPos(20, 20); + testerChild2->setPos(20, 20); + QTest::qWait(250); + QCOMPARE(tester->repaints, 2); + QCOMPARE(testerChild->repaints, 2); + QCOMPARE(testerChild2->repaints, 2); + + // Translating does not result in a repaint. + tester->translate(10, 10); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 2); + QCOMPARE(testerChild->repaints, 2); + QCOMPARE(testerChild2->repaints, 2); + + // Rotating results in a repaint. + tester->rotate(45); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 3); + QCOMPARE(testerChild->repaints, 3); + QCOMPARE(testerChild2->repaints, 2); + + // Change to ItemCoordinateCache (triggers repaint). + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize + testerChild->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize + testerChild2->setCacheMode(QGraphicsItem::ItemCoordinateCache); // autosize + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 4); + QCOMPARE(testerChild->repaints, 4); + QCOMPARE(testerChild2->repaints, 3); + + // Rotating items with ItemCoordinateCache doesn't cause a repaint. + tester->rotate(22); + testerChild->rotate(22); + testerChild2->rotate(22); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 4); + QTRY_COMPARE(testerChild->repaints, 4); + QTRY_COMPARE(testerChild2->repaints, 3); + tester->resetTransform(); + testerChild->resetTransform(); + testerChild2->resetTransform(); + + // Explicit update causes a repaint. + tester->update(0, 0, 5, 5); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 5); + QCOMPARE(testerChild->repaints, 4); + QCOMPARE(testerChild2->repaints, 3); + + // Updating outside the item's bounds does not cause a repaint. + tester->update(10, 10, 5, 5); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 5); + QCOMPARE(testerChild->repaints, 4); + QCOMPARE(testerChild2->repaints, 3); + + // Resizing an item should cause a repaint of that item. (because of + // autosize). + tester->setGeometry(QRectF(-15, -15, 30, 30)); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 6); + QCOMPARE(testerChild->repaints, 4); + QCOMPARE(testerChild2->repaints, 3); + + // Set fixed size. + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30)); + testerChild->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30)); + testerChild2->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(30, 30)); + QTest::qWait(20); + QTRY_COMPARE(tester->repaints, 7); + QCOMPARE(testerChild->repaints, 5); + QCOMPARE(testerChild2->repaints, 4); + + // Resizing the item should cause a repaint. + testerChild->setGeometry(QRectF(-15, -15, 30, 30)); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 7); + QCOMPARE(testerChild->repaints, 6); + QCOMPARE(testerChild2->repaints, 4); + + // Scaling the view does not cause a repaint. + view.scale(0.7, 0.7); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 7); + QCOMPARE(testerChild->repaints, 6); + QCOMPARE(testerChild2->repaints, 4); + + // Switch to device coordinate cache. + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + testerChild->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + testerChild2->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 8); + QCOMPARE(testerChild->repaints, 7); + QCOMPARE(testerChild2->repaints, 5); + + // Scaling the view back should cause repaints for two of the items. + view.setTransform(QTransform()); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 9); + QCOMPARE(testerChild->repaints, 8); + QCOMPARE(testerChild2->repaints, 5); + + // Rotating the base item (perspective) should repaint two items. + tester->setTransform(QTransform().rotate(10, Qt::XAxis)); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 10); + QCOMPARE(testerChild->repaints, 9); + QCOMPARE(testerChild2->repaints, 5); + + // Moving the middle item should case a repaint even if it's a move, + // because the parent is rotated with a perspective. + testerChild->setPos(1, 1); + QTest::qWait(25); + QTRY_COMPARE(tester->repaints, 11); + QTRY_COMPARE(testerChild->repaints, 10); + QTRY_COMPARE(testerChild2->repaints, 5); + tester->resetTransform(); + + // Make a huge item + tester->setGeometry(QRectF(-4000, -4000, 8000, 8000)); + QTRY_COMPARE(tester->repaints, 12); + QTRY_COMPARE(testerChild->repaints, 11); + QTRY_COMPARE(testerChild2->repaints, 5); + + // Move the large item - will cause a repaint as the + // cache is clipped. + tester->setPos(5, 0); + QTRY_COMPARE(tester->repaints, 13); + QTRY_COMPARE(testerChild->repaints, 11); + QTRY_COMPARE(testerChild2->repaints, 5); + + // Hiding and showing should invalidate the cache + tester->hide(); + QTest::qWait(25); + tester->show(); + QTRY_COMPARE(tester->repaints, 14); + QTRY_COMPARE(testerChild->repaints, 12); + QTRY_COMPARE(testerChild2->repaints, 6); +} + +void tst_QGraphicsItem::cacheMode2() +{ + QGraphicsScene scene(0, 0, 100, 100); + QGraphicsView view(&scene); + view.resize(150, 150); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + + // Increase the probability of window activation + // not causing another repaint of test items. + QTest::qWait(50); + + EventTester *tester = new EventTester; + scene.addItem(tester); + QTest::qWait(10); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to NoCache (no repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to DeviceCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from DeviceCoordinateCache to DeviceCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from DeviceCoordinateCache to NoCache (no repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 1); + + // Switching from NoCache to ItemCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 2); + + // Switching from ItemCoordinateCache to ItemCoordinateCache (no repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 2); + + // Switching from ItemCoordinateCache to ItemCoordinateCache with different size (repaint) + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache, QSize(100, 100)); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 3); + + // Switching from ItemCoordinateCache to NoCache (repaint) + tester->setCacheMode(QGraphicsItem::NoCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 4); + + // Switching from DeviceCoordinateCache to ItemCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 4); + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 5); + + // Switching from ItemCoordinateCache to DeviceCoordinateCache (repaint) + tester->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + QTest::qWait(50); + QTRY_COMPARE(tester->repaints, 6); +} + +void tst_QGraphicsItem::updateCachedItemAfterMove() +{ + // A simple item that uses ItemCoordinateCache + EventTester *tester = new EventTester; + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + + // Add to a scene, show in a view, ensure it's painted and reset its + // repaint counter. + QGraphicsScene scene; + scene.addItem(tester); + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QTest::qWait(12); + QTRY_VERIFY(tester->repaints > 0); + tester->repaints = 0; + + // Move the item, should not cause repaints + tester->setPos(10, 0); + QTest::qWait(12); + QCOMPARE(tester->repaints, 0); + + // Move then update, should cause one repaint + tester->setPos(20, 0); + tester->update(); + QTest::qWait(12); + QCOMPARE(tester->repaints, 1); + + // Hiding the item doesn't cause a repaint + tester->hide(); + QTest::qWait(12); + QCOMPARE(tester->repaints, 1); + + // Moving a hidden item doesn't cause a repaint + tester->setPos(30, 0); + tester->update(); + QTest::qWait(12); + QCOMPARE(tester->repaints, 1); +} + +class Track : public QGraphicsRectItem +{ +public: + Track(const QRectF &rect) + : QGraphicsRectItem(rect) + { + setAcceptHoverEvents(true); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + QGraphicsRectItem::paint(painter, option, widget); + painter->drawText(boundingRect(), Qt::AlignCenter, QString("%1x%2\n%3x%4").arg(p.x()).arg(p.y()).arg(sp.x()).arg(sp.y())); + } + +protected: + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) + { + p = event->pos(); + sp = event->widget()->mapFromGlobal(event->screenPos()); + update(); + } +private: + QPointF p; + QPoint sp; +}; + +void tst_QGraphicsItem::deviceTransform_data() +{ + QTest::addColumn("untransformable1"); + QTest::addColumn("untransformable2"); + QTest::addColumn("untransformable3"); + QTest::addColumn("rotation1"); + QTest::addColumn("rotation2"); + QTest::addColumn("rotation3"); + QTest::addColumn("deviceX"); + QTest::addColumn("mapResult1"); + QTest::addColumn("mapResult2"); + QTest::addColumn("mapResult3"); + + QTest::newRow("nil") << false << false << false + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform() + << QPointF(150, 150) << QPointF(250, 250) << QPointF(350, 350); + QTest::newRow("deviceX rot 90") << false << false << false + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-350, 350); + QTest::newRow("deviceX rot 90 100") << true << false << false + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350); + QTest::newRow("deviceX rot 90 010") << false << true << false + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-150, 150) << QPointF(-150, 250) << QPointF(-50, 350); + QTest::newRow("deviceX rot 90 001") << false << false << true + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-150, 150) << QPointF(-250, 250) << QPointF(-250, 350); + QTest::newRow("deviceX rot 90 111") << true << true << true + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350); + QTest::newRow("deviceX rot 90 101") << true << false << true + << qreal(0.0) << qreal(0.0) << qreal(0.0) + << QTransform().rotate(90) + << QPointF(-50, 150) << QPointF(50, 250) << QPointF(150, 350); +} + +void tst_QGraphicsItem::deviceTransform() +{ + QFETCH(bool, untransformable1); + QFETCH(bool, untransformable2); + QFETCH(bool, untransformable3); + QFETCH(qreal, rotation1); + QFETCH(qreal, rotation2); + QFETCH(qreal, rotation3); + QFETCH(QTransform, deviceX); + QFETCH(QPointF, mapResult1); + QFETCH(QPointF, mapResult2); + QFETCH(QPointF, mapResult3); + + QGraphicsScene scene; + Track *rect1 = new Track(QRectF(0, 0, 100, 100)); + Track *rect2 = new Track(QRectF(0, 0, 100, 100)); + Track *rect3 = new Track(QRectF(0, 0, 100, 100)); + rect2->setParentItem(rect1); + rect3->setParentItem(rect2); + rect1->setPos(100, 100); + rect2->setPos(100, 100); + rect3->setPos(100, 100); + rect1->rotate(rotation1); + rect2->rotate(rotation2); + rect3->rotate(rotation3); + rect1->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable1); + rect2->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable2); + rect3->setFlag(QGraphicsItem::ItemIgnoresTransformations, untransformable3); + rect1->setBrush(Qt::red); + rect2->setBrush(Qt::green); + rect3->setBrush(Qt::blue); + scene.addItem(rect1); + + QCOMPARE(rect1->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult1); + QCOMPARE(rect2->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult2); + QCOMPARE(rect3->deviceTransform(deviceX).map(QPointF(50, 50)), mapResult3); +} + +void tst_QGraphicsItem::update() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + QWidget topLevel; + MyGraphicsView view(&scene,&topLevel); + + topLevel.resize(300, 300); + topLevel.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *item = new EventTester; + scene.addItem(item); + QTest::qWait(100); // Make sure all pending updates are processed. + item->repaints = 0; + + item->update(); // Item marked as dirty + scene.update(); // Entire scene marked as dirty + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + + // Make sure the dirty state from the previous update is reset so that + // the item don't think it is already dirty and discards this update. + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 2); + + // Make sure a partial update doesn't cause a full update to be discarded. + view.reset(); + item->repaints = 0; + item->update(QRectF(0, 0, 5, 5)); + item->update(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).toAlignedRect(); + QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + // Make sure update requests outside the bounding rect are discarded. + view.reset(); + item->repaints = 0; + item->update(-15, -15, 5, 5); // Item's brect: (-10, -10, 20, 20) + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + + // Make sure the area occupied by an item is repainted when hiding it. + view.reset(); + item->repaints = 0; + item->update(); // Full update; all sub-sequent update requests are discarded. + item->hide(); // visible set to 0. ignoreVisible must be set to 1; the item won't be processed otherwise. + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + // Make sure item is repainted when shown (after being hidden). + view.reset(); + item->repaints = 0; + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + item->repaints = 0; + item->hide(); + qApp->processEvents(); + view.reset(); + const QPointF originalPos = item->pos(); + item->setPos(5000, 5000); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + qApp->processEvents(); + + item->setPos(originalPos); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 0); + item->show(); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + // The entire item's bounding rect (adjusted for antialiasing) should have been painted. + QCOMPARE(view.paintedRegion, expectedRegion); + + QGraphicsViewPrivate *viewPrivate = static_cast(qt_widget_private(&view)); + item->setPos(originalPos + QPoint(50, 50)); + viewPrivate->updateAll(); + QVERIFY(viewPrivate->fullUpdatePending); + QTest::qWait(50); + item->repaints = 0; + view.reset(); + item->setPos(originalPos); + QTest::qWait(50); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + COMPARE_REGIONS(view.paintedRegion, expectedRegion + expectedRegion.translated(50, 50)); + + // Make sure moving a parent item triggers an update on the children + // (even though the parent itself is outside the viewport). + QGraphicsRectItem *parent = new QGraphicsRectItem(0, 0, 10, 10); + parent->setPos(-400, 0); + item->setParentItem(parent); + item->setPos(400, 0); + scene.addItem(parent); + QTest::qWait(50); + itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(item->boundingRect()).toAlignedRect(); + expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); + view.reset(); + item->repaints = 0; + parent->translate(-400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 0); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + view.reset(); + item->repaints = 0; + parent->translate(400, 0); + qApp->processEvents(); + QCOMPARE(item->repaints, 1); + QCOMPARE(view.repaints, 1); + QCOMPARE(view.paintedRegion, expectedRegion); + QCOMPARE(view.paintedRegion, expectedRegion); +} + +void tst_QGraphicsItem::setTransformProperties_data() +{ + QTest::addColumn("origin"); + QTest::addColumn("rotation"); + QTest::addColumn("scale"); + + QTest::newRow("nothing") << QPointF() << qreal(0.0) << qreal(1.0); + + QTest::newRow("rotation") << QPointF() << qreal(42.2) << qreal(1.0); + + QTest::newRow("rotation dicentred") << QPointF(qreal(22.3), qreal(-56.2)) + << qreal(-2578.2) + << qreal(1.0); + + QTest::newRow("Scale") << QPointF() << qreal(0.0) + << qreal(6); + + QTest::newRow("Everything dicentred") << QPointF(qreal(22.3), qreal(-56.2)) << qreal(-175) << qreal(196); +} + +/** + * the normal QCOMPARE doesn't work because it doesn't use qFuzzyCompare + */ +#define QCOMPARE_TRANSFORM(X1, X2) QVERIFY(((X1)*(X2).inverted()).isIdentity()) + +void tst_QGraphicsItem::setTransformProperties() +{ + QFETCH(QPointF,origin); + QFETCH(qreal,rotation); + QFETCH(qreal,scale); + + QTransform result; + result.translate(origin.x(), origin.y()); + result.rotate(rotation, Qt::ZAxis); + result.scale(scale, scale); + result.translate(-origin.x(), -origin.y()); + + QGraphicsScene scene; + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + scene.addItem(item); + + item->setRotation(rotation); + item->setScale(scale); + item->setTransformOriginPoint(origin); + + QCOMPARE(item->rotation(), rotation); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result, item->sceneTransform()); + + //----------------------------------------------------------------- + //Change the rotation Z + item->setRotation(45); + QTransform result2; + result2.translate(origin.x(), origin.y()); + result2.rotate(45); + result2.scale(scale, scale); + result2.translate(-origin.x(), -origin.y()); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + + QCOMPARE(QTransform(), item->transform()); + QCOMPARE(result2, item->sceneTransform()); + + //----------------------------------------------------------------- + // calling setTransform() and setPos should change the sceneTransform + item->setTransform(result); + item->setPos(100, -150.5); + + QCOMPARE(item->rotation(), 45.); + QCOMPARE(item->scale(), scale); + QCOMPARE(item->transformOriginPoint(), origin); + QCOMPARE(result, item->transform()); + + QTransform result3(result); + + result3.translate(origin.x(), origin.y()); + result3.rotate(45); + result3.scale(scale, scale); + result3.translate(-origin.x(), -origin.y()); + + result3 *= QTransform::fromTranslate(100, -150.5); //the pos; + + QCOMPARE(result3, item->sceneTransform()); + + //----------------------------------------------------- + // setting the propertiees should be the same as setting a transform + {//with center origin on the matrix + QGraphicsRectItem *item1 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item1); + QGraphicsRectItem *item2 = new QGraphicsRectItem(QRectF(50.2, -150, 230.5, 119)); + scene.addItem(item2); + + item1->setPos(12.3, -5); + item2->setPos(12.3, -5); + item1->setRotation(rotation); + item1->setScale(scale); + item1->setTransformOriginPoint(origin); + + item2->setTransform(result); + + QCOMPARE_TRANSFORM(item1->sceneTransform(), item2->sceneTransform()); + + QCOMPARE_TRANSFORM(item1->itemTransform(item2), QTransform()); + QCOMPARE_TRANSFORM(item2->itemTransform(item1), QTransform()); + } +} + +class MyStyleOptionTester : public QGraphicsRectItem +{ +public: + MyStyleOptionTester(const QRectF &rect) + : QGraphicsRectItem(rect), startTrack(false) + {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) + { + if (startTrack) { + //Doesn't use the extended style option so the exposed rect is the boundingRect + if (!(flags() & QGraphicsItem::ItemUsesExtendedStyleOption)) { + QCOMPARE(option->exposedRect, boundingRect()); + QCOMPARE(option->matrix, QMatrix()); + } else { + QVERIFY(option->exposedRect != QRect()); + QVERIFY(option->exposedRect != boundingRect()); + QCOMPARE(option->matrix, sceneTransform().toAffine()); + } + } + QGraphicsRectItem::paint(painter, option, widget); + } + bool startTrack; +}; + +void tst_QGraphicsItem::itemUsesExtendedStyleOption() +{ + QGraphicsScene scene(0, 0, 300, 300); + QGraphicsPixmapItem item; + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QCOMPARE(item.flags(), QGraphicsItem::GraphicsItemFlags(QGraphicsItem::ItemUsesExtendedStyleOption)); + item.setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); + QCOMPARE(item.flags(), 0); + + //We now test the content of the style option + MyStyleOptionTester *rect = new MyStyleOptionTester(QRect(0, 0, 100, 100)); + scene.addItem(rect); + rect->setPos(200, 200); + QWidget topLevel; + QGraphicsView view(&scene, &topLevel); + topLevel.setWindowFlags(Qt::X11BypassWindowManagerHint); + rect->startTrack = false; + topLevel.show(); + QTest::qWaitForWindowShown(&view); + QTest::qWait(60); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(60); + rect->startTrack = false; + rect->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); + QVERIFY((rect->flags() & QGraphicsItem::ItemUsesExtendedStyleOption)); + QTest::qWait(60); + rect->startTrack = true; + rect->update(10, 10, 10, 10); + QTest::qWait(60); +} + +void tst_QGraphicsItem::itemSendsGeometryChanges() +{ + ItemChangeTester item; + item.setFlags(0); + item.clear(); + + QTransform x = QTransform().rotate(45); + QPointF pos(10, 10); + qreal o(0.5); + qreal r(10.0); + qreal s(1.5); + QPointF origin(1.0, 1.0); + item.setTransform(x); + item.setPos(pos); + item.setRotation(r); + item.setScale(s); + item.setTransformOriginPoint(origin); + QCOMPARE(item.transform(), x); + QCOMPARE(item.pos(), pos); + QCOMPARE(item.rotation(), r); + QCOMPARE(item.scale(), s); + QCOMPARE(item.transformOriginPoint(), origin); + QCOMPARE(item.changes.size(), 0); + + item.setOpacity(o); + QCOMPARE(item.changes.size(), 2); // opacity + + item.setFlag(QGraphicsItem::ItemSendsGeometryChanges); + QCOMPARE(item.changes.size(), 4); // flags + item.setTransform(QTransform()); + item.setPos(QPointF()); + QCOMPARE(item.changes.size(), 8); // transform + pos + QCOMPARE(item.transform(), QTransform()); + QCOMPARE(item.pos(), QPointF()); + QCOMPARE(item.opacity(), o); + item.setRotation(0.0); + item.setScale(1.0); + item.setTransformOriginPoint(0.0, 0.0); + QCOMPARE(item.changes.size(), 14); // rotation + scale + origin + QCOMPARE(item.rotation(), qreal(0.0)); + QCOMPARE(item.scale(), qreal(1.0)); + QCOMPARE(item.transformOriginPoint(), QPointF(0.0, 0.0)); + + QCOMPARE(item.changes, QList() + << QGraphicsItem::ItemOpacityChange + << QGraphicsItem::ItemOpacityHasChanged + << QGraphicsItem::ItemFlagsChange + << QGraphicsItem::ItemFlagsHaveChanged + << QGraphicsItem::ItemTransformChange + << QGraphicsItem::ItemTransformHasChanged + << QGraphicsItem::ItemPositionChange + << QGraphicsItem::ItemPositionHasChanged + << QGraphicsItem::ItemRotationChange + << QGraphicsItem::ItemRotationHasChanged + << QGraphicsItem::ItemScaleChange + << QGraphicsItem::ItemScaleHasChanged + << QGraphicsItem::ItemTransformOriginPointChange + << QGraphicsItem::ItemTransformOriginPointHasChanged); +} + +// Make sure we update moved items correctly. +void tst_QGraphicsItem::moveItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-50, -50, 200, 200); + + MyGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + EventTester *grandChild = new EventTester(child); + +#define RESET_COUNTERS \ + parent->repaints = 0; \ + child->repaints = 0; \ + grandChild->repaints = 0; \ + view.reset(); + + scene.addItem(parent); + QTest::qWait(100); + + RESET_COUNTERS + + // Item's boundingRect: (-10, -10, 20, 20). + QRect parentDeviceBoundingRect = parent->deviceTransform(view.viewportTransform()) + .mapRect(parent->boundingRect()).toAlignedRect() + .adjusted(-2, -2, 2, 2); // Adjusted for antialiasing. + + parent->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(view.repaints, 1); + QRegion expectedParentRegion = parentDeviceBoundingRect; // old position + parentDeviceBoundingRect.translate(20, 20); + expectedParentRegion += parentDeviceBoundingRect; // new position + COMPARE_REGIONS(view.paintedRegion, expectedParentRegion); + + RESET_COUNTERS + + child->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedChildRegion = expectedParentRegion.translated(20, 20); + COMPARE_REGIONS(view.paintedRegion, expectedChildRegion); + + RESET_COUNTERS + + grandChild->setPos(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + const QRegion expectedGrandChildRegion = expectedParentRegion.translated(40, 40); + COMPARE_REGIONS(view.paintedRegion, expectedGrandChildRegion); + + RESET_COUNTERS + + parent->translate(20, 20); + qApp->processEvents(); + QCOMPARE(parent->repaints, 1); + QCOMPARE(child->repaints, 1); + QCOMPARE(grandChild->repaints, 1); + QCOMPARE(view.repaints, 1); + expectedParentRegion.translate(20, 20); + expectedParentRegion += expectedChildRegion.translated(20, 20); + expectedParentRegion += expectedGrandChildRegion.translated(20, 20); + COMPARE_REGIONS(view.paintedRegion, expectedParentRegion); +} + +void tst_QGraphicsItem::moveLineItem() +{ + QGraphicsScene scene; + scene.setSceneRect(0, 0, 200, 200); + QGraphicsLineItem *item = new QGraphicsLineItem(0, 0, 100, 0); + item->setPos(50, 50); + scene.addItem(item); + + MyGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(200); + view.reset(); + + QRectF brect = item->boundingRect(); + // Do same adjustments as in qgraphicsscene.cpp + if (!brect.width()) + brect.adjust(qreal(-0.00001), 0, qreal(0.00001), 0); + if (!brect.height()) + brect.adjust(0, qreal(-0.00001), 0, qreal(0.00001)); + const QRect itemDeviceBoundingRect = item->deviceTransform(view.viewportTransform()) + .mapRect(brect).toAlignedRect(); + QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2); // antialiasing + + // Make sure the calculated region is correct. + item->update(); + QTest::qWait(10); + QTRY_COMPARE(view.paintedRegion, expectedRegion); + view.reset(); + + // Old position: (50, 50) + item->setPos(50, 100); + expectedRegion += expectedRegion.translated(0, 50); + QTest::qWait(10); + QCOMPARE(view.paintedRegion, expectedRegion); +} + +void tst_QGraphicsItem::sorting_data() +{ + QTest::addColumn("index"); + + QTest::newRow("NoIndex") << int(QGraphicsScene::NoIndex); + QTest::newRow("BspTreeIndex") << int(QGraphicsScene::BspTreeIndex); +} + +void tst_QGraphicsItem::sorting() +{ + if (PlatformQuirks::isAutoMaximizing()) + QSKIP("Skipped because Platform is auto maximizing", SkipAll); + + _paintedItems.clear(); + + QGraphicsScene scene; + QGraphicsItem *grid[100][100]; + for (int x = 0; x < 100; ++x) { + for (int y = 0; y < 100; ++y) { + PainterItem *item = new PainterItem; + item->setPos(x * 25, y * 25); + item->setData(0, QString("%1x%2").arg(x).arg(y)); + grid[x][y] = item; + scene.addItem(item); + } + } + + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item1->setData(0, "item1"); + item2->setData(0, "item2"); + scene.addItem(item1); + scene.addItem(item2); + + QGraphicsView view(&scene); + view.setResizeAnchor(QGraphicsView::NoAnchor); + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.resize(120, 100); + view.setFrameStyle(0); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#if defined(Q_WS_MAC) + // There's no difference between repaint and update on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(100); +#endif + + QCOMPARE(_paintedItems, QList() + << grid[0][0] << grid[0][1] << grid[0][2] << grid[0][3] + << grid[1][0] << grid[1][1] << grid[1][2] << grid[1][3] + << grid[2][0] << grid[2][1] << grid[2][2] << grid[2][3] + << grid[3][0] << grid[3][1] << grid[3][2] << grid[3][3] + << grid[4][0] << grid[4][1] << grid[4][2] << grid[4][3] + << item1 << item2); +} + +void tst_QGraphicsItem::itemHasNoContents() +{ + PainterItem *item1 = new PainterItem; + PainterItem *item2 = new PainterItem; + item2->setParentItem(item1); + item2->setPos(50, 50); + item1->setFlag(QGraphicsItem::ItemHasNoContents); + item1->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + + QGraphicsScene scene; + scene.addItem(item1); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(!_paintedItems.isEmpty()); + + _paintedItems.clear(); + + view.viewport()->repaint(); +#ifdef Q_WS_MAC + // There's no difference between update() and repaint() on the Mac, + // so we have to process events here to make sure we get the event. + QTest::qWait(10); +#endif + + QTRY_COMPARE(_paintedItems, QList() << item2); +} + +void tst_QGraphicsItem::hitTestUntransformableItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20); + item1->setPos(-200, -200); + + QGraphicsRectItem *item2 = new QGraphicsRectItem(0, 0, 20, 20); + item2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item2->setPos(200, 200); + + QGraphicsRectItem *item3 = new QGraphicsRectItem(0, 0, 20, 20); + item3->setParentItem(item2); + item3->setPos(80, 80); + + scene.addItem(item1); + QTest::qWait(100); + + QList items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast(item3)); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QTest::qWait(100); + + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast(item3)); +} + +void tst_QGraphicsItem::hitTestGraphicsEffectItem() +{ + QGraphicsScene scene; + scene.setSceneRect(-100, -100, 200, 200); + + QWidget toplevel; + + QGraphicsView view(&scene, &toplevel); + toplevel.resize(300, 300); + toplevel.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&toplevel); +#endif + QTest::qWait(100); + + // Confuse the BSP with dummy items. + QGraphicsRectItem *dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100, -100); + scene.addItem(dummy); + for (int i = 0; i < 100; ++i) { + QGraphicsItem *parent = dummy; + dummy = new QGraphicsRectItem(0, 0, 20, 20); + dummy->setPos(-100 + i, -100 + i); + dummy->setParentItem(parent); + } + + const QRectF itemBoundingRect(0, 0, 20, 20); + EventTester *item1 = new EventTester; + item1->br = itemBoundingRect; + item1->setPos(-200, -200); + item1->brush = Qt::red; + + EventTester *item2 = new EventTester; + item2->br = itemBoundingRect; + item2->setFlag(QGraphicsItem::ItemIgnoresTransformations); + item2->setParentItem(item1); + item2->setPos(200, 200); + item2->brush = Qt::green; + + EventTester *item3 = new EventTester; + item3->br = itemBoundingRect; + item3->setParentItem(item2); + item3->setPos(80, 80); + item3->brush = Qt::blue; + + scene.addItem(item1); + QTest::qWait(100); + + item1->repaints = 0; + item2->repaints = 0; + item3->repaints = 0; + + // Apply shadow effect to the entire sub-tree. + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; + shadow->setOffset(-20, -20); + item1->setGraphicsEffect(shadow); + QTest::qWait(50); + + // Make sure all visible items are repainted. + QCOMPARE(item1->repaints, 1); + QCOMPARE(item2->repaints, 1); + QCOMPARE(item3->repaints, 1); + + // Make sure an item doesn't respond to a click on its shadow. + QList items = scene.items(QPointF(75, 75)); + QVERIFY(items.isEmpty()); + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast(item3)); + + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + QTest::qWait(100); + + items = scene.items(QPointF(75, 75)); + QVERIFY(items.isEmpty()); + items = scene.items(QPointF(80, 80)); + QCOMPARE(items.size(), 1); + QCOMPARE(items.at(0), static_cast(item3)); +} + +void tst_QGraphicsItem::focusProxy() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsItem *item = scene.addRect(0, 0, 10, 10); + item->setFlag(QGraphicsItem::ItemIsFocusable); + QVERIFY(!item->focusProxy()); + + QGraphicsItem *item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), item2); + + item->setFocus(); + QVERIFY(item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Try to make a focus chain loop + QString err; + QTextStream stream(&err); + stream << "QGraphicsItem::setFocusProxy: " + << (void*)item << " is already in the focus proxy chain" << flush; + QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData()); + item2->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Try to assign self as focus proxy + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: cannot assign self as focus proxy"); + item->setFocusProxy(item); // fails + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + + // Reset the focus proxy + item->setFocusProxy(0); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QVERIFY(!item->hasFocus()); + QVERIFY(item2->hasFocus()); + + // Test deletion + item->setFocusProxy(item2); + QCOMPARE(item->focusProxy(), (QGraphicsItem *)item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + + // Test event delivery + item2 = scene.addRect(0, 0, 10, 10); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item->setFocusProxy(item2); + item->clearFocus(); + + EventSpy focusInSpy(&scene, item, QEvent::FocusIn); + EventSpy focusOutSpy(&scene, item, QEvent::FocusOut); + EventSpy focusInSpy2(&scene, item2, QEvent::FocusIn); + EventSpy focusOutSpy2(&scene, item2, QEvent::FocusOut); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 0); + QCOMPARE(focusOutSpy2.count(), 0); + + item->setFocus(); + QCOMPARE(focusInSpy.count(), 0); + QCOMPARE(focusInSpy2.count(), 1); + item->clearFocus(); + QCOMPARE(focusOutSpy.count(), 0); + QCOMPARE(focusOutSpy2.count(), 1); + + // Test two items proxying one item. + QGraphicsItem *item3 = scene.addRect(0, 0, 10, 10); + item3->setFlag(QGraphicsItem::ItemIsFocusable); + item3->setFocusProxy(item2); // item and item3 use item2 as proxy + + QCOMPARE(item->focusProxy(), item2); + QCOMPARE(item2->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), item2); + delete item2; + QCOMPARE(item->focusProxy(), (QGraphicsItem *)0); + QCOMPARE(item3->focusProxy(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::subFocus() +{ + // Construct a text item that's not part of a scene (yet) + // and has no parent. Setting focus on it will not make + // the item gain input focus; that requires a scene. But + // it does set subfocus, indicating that the item wishes + // to gain focus later. + QGraphicsTextItem *text = new QGraphicsTextItem("Hello"); + text->setTextInteractionFlags(Qt::TextEditorInteraction); + QVERIFY(!text->hasFocus()); + text->setFocus(); + QVERIFY(!text->hasFocus()); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text); + + // Add a sibling. + QGraphicsTextItem *text2 = new QGraphicsTextItem("Hi"); + text2->setTextInteractionFlags(Qt::TextEditorInteraction); + text2->setPos(30, 30); + + // Add both items to a scene and check that it's text that + // got input focus. + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + scene.addItem(text); + scene.addItem(text2); + QVERIFY(text->hasFocus()); + + text->setData(0, "text"); + text2->setData(0, "text2"); + + // Remove text2 and set subfocus on it. Then readd. Reparent it onto the + // other item and see that it gains input focus. + scene.removeItem(text2); + text2->setFocus(); + scene.addItem(text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + text2->setParentItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + QVERIFY(!text->hasFocus()); + QVERIFY(text2->hasFocus()); + + // Remove both items from the scene, restore subfocus and + // readd them. Now the subfocus should kick in and give + // text2 focus. + scene.removeItem(text); + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + text2->setFocus(); + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2); + scene.addItem(text); + + // Hiding and showing text should pass focus to text2. + QCOMPARE(text->focusItem(), (QGraphicsItem *)text2); + QVERIFY(text2->hasFocus()); + + // Subfocus should repropagate to root when reparenting. + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem(rect); + QGraphicsRectItem *rect3 = new QGraphicsRectItem(rect2); + rect3->setFlag(QGraphicsItem::ItemIsFocusable); + + text->setData(0, "text"); + text2->setData(0, "text2"); + rect->setData(0, "rect"); + rect2->setData(0, "rect2"); + rect3->setData(0, "rect3"); + + rect3->setFocus(); + QVERIFY(!rect3->hasFocus()); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + rect->setParentItem(text2); + QCOMPARE(text->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect2->focusItem(), (QGraphicsItem *)rect3); + QCOMPARE(rect3->focusItem(), (QGraphicsItem *)rect3); + QVERIFY(!rect->hasFocus()); + QVERIFY(!rect2->hasFocus()); + QVERIFY(rect3->hasFocus()); + + delete rect2; + QCOMPARE(text->focusItem(), (QGraphicsItem *)0); + QCOMPARE(text2->focusItem(), (QGraphicsItem *)0); + QCOMPARE(rect->focusItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsItem::focusProxyDeletion() +{ + QGraphicsRectItem *rect = new QGraphicsRectItem; + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QGraphicsScene *scene = new QGraphicsScene; + scene->addItem(rect); + scene->addItem(rect2); + delete rect2; + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + + rect2 = new QGraphicsRectItem; + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::setFocusProxy: focus proxy must be in same scene"); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)0); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + delete rect; // don't crash + + rect = new QGraphicsRectItem; + rect2 = new QGraphicsRectItem; + rect->setFocusProxy(rect2); + QCOMPARE(rect->focusProxy(), (QGraphicsItem *)rect2); + scene->addItem(rect); + scene->addItem(rect2); + rect->setFocusProxy(rect2); + delete scene; // don't crash +} + +void tst_QGraphicsItem::negativeZStacksBehindParent() +{ + QGraphicsRectItem rect; + QCOMPARE(rect.zValue(), qreal(0.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QCOMPARE(rect.zValue(), qreal(-1.0)); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(0); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(rect.flags() & QGraphicsItem::ItemNegativeZStacksBehindParent); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setZValue(-1); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setZValue(0); + QVERIFY(!(rect.flags() & QGraphicsItem::ItemStacksBehindParent)); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + rect.setZValue(-1); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, true); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); + rect.setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, false); + QVERIFY(rect.flags() & QGraphicsItem::ItemStacksBehindParent); +} + +void tst_QGraphicsItem::setGraphicsEffect() +{ + // Check that we don't have any effect by default. + QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10); + QVERIFY(!item->graphicsEffect()); + + // SetGet check. + QPointer blurEffect = new QGraphicsBlurEffect; + item->setGraphicsEffect(blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast(blurEffect)); + + // Ensure the existing effect is deleted when setting a new one. + QPointer shadowEffect = new QGraphicsDropShadowEffect; + item->setGraphicsEffect(shadowEffect); + QVERIFY(!blurEffect); + QCOMPARE(item->graphicsEffect(), static_cast(shadowEffect)); + blurEffect = new QGraphicsBlurEffect; + + // Ensure the effect is uninstalled when setting it on a new target. + QGraphicsItem *anotherItem = new QGraphicsRectItem(0, 0, 10, 10); + anotherItem->setGraphicsEffect(blurEffect); + item->setGraphicsEffect(blurEffect); + QVERIFY(!anotherItem->graphicsEffect()); + QVERIFY(!shadowEffect); + + // Ensure the existing effect is deleted when deleting the item. + delete item; + QVERIFY(!blurEffect); + delete anotherItem; + + // Ensure the effect is uninstalled when deleting it + item = new QGraphicsRectItem(0, 0, 10, 10); + blurEffect = new QGraphicsBlurEffect; + item->setGraphicsEffect(blurEffect); + delete blurEffect; + QVERIFY(!item->graphicsEffect()); + + // Ensure the existing effect is uninstalled and deleted when setting a null effect + blurEffect = new QGraphicsBlurEffect; + item->setGraphicsEffect(blurEffect); + item->setGraphicsEffect(0); + QVERIFY(!item->graphicsEffect()); + QVERIFY(!blurEffect); + + delete item; +} + +void tst_QGraphicsItem::panel() +{ + QGraphicsScene scene; + + QGraphicsRectItem *panel1 = new QGraphicsRectItem; + QGraphicsRectItem *panel2 = new QGraphicsRectItem; + QGraphicsRectItem *panel3 = new QGraphicsRectItem; + QGraphicsRectItem *panel4 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel1 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel2 = new QGraphicsRectItem; + panel1->setFlag(QGraphicsItem::ItemIsPanel); + panel2->setFlag(QGraphicsItem::ItemIsPanel); + panel3->setFlag(QGraphicsItem::ItemIsPanel); + panel4->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(panel1); + scene.addItem(panel2); + scene.addItem(panel3); + scene.addItem(panel4); + scene.addItem(notPanel1); + scene.addItem(notPanel2); + + EventSpy spy_activate_panel1(&scene, panel1, QEvent::WindowActivate); + EventSpy spy_deactivate_panel1(&scene, panel1, QEvent::WindowDeactivate); + EventSpy spy_activate_panel2(&scene, panel2, QEvent::WindowActivate); + EventSpy spy_deactivate_panel2(&scene, panel2, QEvent::WindowDeactivate); + EventSpy spy_activate_panel3(&scene, panel3, QEvent::WindowActivate); + EventSpy spy_deactivate_panel3(&scene, panel3, QEvent::WindowDeactivate); + EventSpy spy_activate_panel4(&scene, panel4, QEvent::WindowActivate); + EventSpy spy_deactivate_panel4(&scene, panel4, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel1(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel1(&scene, notPanel1, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel2(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel2(&scene, notPanel1, QEvent::WindowDeactivate); + + QCOMPARE(spy_activate_panel1.count(), 0); + QCOMPARE(spy_deactivate_panel1.count(), 0); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_deactivate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_deactivate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + QCOMPARE(spy_deactivate_panel4.count(), 0); + QCOMPARE(spy_activate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_activate_notPanel2.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + + QVERIFY(!scene.activePanel()); + QVERIFY(!scene.isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // No previous activation, so the scene is active. + QVERIFY(scene.isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)panel1); + QVERIFY(panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + QCOMPARE(spy_activate_panel1.count(), 1); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + + // Switch back to scene. + scene.setActivePanel(0); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 1); + QCOMPARE(spy_activate_notPanel2.count(), 1); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 1); + QCOMPARE(spy_deactivate_notPanel2.count(), 1); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 2); + QCOMPARE(spy_activate_notPanel2.count(), 2); + + // Switch to panel1 + scene.setActivePanel(panel1); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 2); + QCOMPARE(spy_deactivate_notPanel2.count(), 2); + QCOMPARE(spy_activate_panel1.count(), 2); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QCOMPARE(spy_deactivate_panel1.count(), 2); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_activate_panel1.count(), 3); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QVERIFY(!scene.activePanel()); + scene.setActivePanel(0); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!panel1->isActive()); +} + +void tst_QGraphicsItem::panelWithFocusItem() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsRectItem *parentPanel = new QGraphicsRectItem; + QGraphicsRectItem *parentPanelFocusItem = new QGraphicsRectItem(parentPanel); + parentPanel->setFlag(QGraphicsItem::ItemIsPanel); + parentPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable); + parentPanelFocusItem->setFocus(); + scene.addItem(parentPanel); + + QVERIFY(parentPanel->isActive()); + QVERIFY(parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + + QGraphicsRectItem *childPanel = new QGraphicsRectItem; + QGraphicsRectItem *childPanelFocusItem = new QGraphicsRectItem(childPanel); + childPanel->setFlag(QGraphicsItem::ItemIsPanel); + childPanelFocusItem->setFlag(QGraphicsItem::ItemIsFocusable); + childPanelFocusItem->setFocus(); + + QVERIFY(!childPanelFocusItem->hasFocus()); + QCOMPARE(childPanel->focusItem(), (QGraphicsItem *)childPanelFocusItem); + QCOMPARE(childPanelFocusItem->focusItem(), (QGraphicsItem *)childPanelFocusItem); + + childPanel->setParentItem(parentPanel); + + QVERIFY(!parentPanel->isActive()); + QVERIFY(!parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + + QVERIFY(childPanel->isActive()); + QVERIFY(childPanelFocusItem->hasFocus()); + QCOMPARE(childPanel->focusItem(), (QGraphicsItem *)childPanelFocusItem); + QCOMPARE(childPanelFocusItem->focusItem(), (QGraphicsItem *)childPanelFocusItem); + + childPanel->hide(); + + QVERIFY(parentPanel->isActive()); + QVERIFY(parentPanelFocusItem->hasFocus()); + QCOMPARE(parentPanel->focusItem(), (QGraphicsItem *)parentPanelFocusItem); + QCOMPARE(parentPanelFocusItem->focusItem(), (QGraphicsItem *)parentPanelFocusItem); +} + +void tst_QGraphicsItem::addPanelToActiveScene() +{ + QGraphicsScene scene; + QVERIFY(!scene.isActive()); + + QGraphicsRectItem *rect = new QGraphicsRectItem; + scene.addItem(rect); + QVERIFY(!rect->isActive()); + scene.removeItem(rect); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + scene.addItem(rect); + QVERIFY(rect->isActive()); + scene.removeItem(rect); + + rect->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(rect); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + scene.addItem(rect2); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); +} + +void tst_QGraphicsItem::activate() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = scene.addRect(-10, -10, 20, 20); + QVERIFY(!rect->isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // Non-panel item (active when scene is active). + QVERIFY(rect->isActive()); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect2->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect3 = new QGraphicsRectItem; + rect3->setFlag(QGraphicsItem::ItemIsPanel); + + // Test normal activation. + QVERIFY(!rect2->isActive()); + scene.addItem(rect2); + QVERIFY(rect2->isActive()); // first panel item is activated + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // second panel item is _not_ activated + rect3->setActive(true); + QVERIFY(rect3->isActive()); + scene.removeItem(rect3); + QVERIFY(!rect3->isActive()); // no panel is active anymore + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // second panel item is activated + + // Test pending activation. + scene.removeItem(rect3); + rect2->setActive(true); + QVERIFY(rect2->isActive()); // first panel item is activated + rect3->setActive(true); + QVERIFY(!rect3->isActive()); // not active (yet) + scene.addItem(rect3); + QVERIFY(rect3->isActive()); // now becomes active + + // Test pending deactivation. + scene.removeItem(rect3); + rect3->setActive(false); + scene.addItem(rect3); + QVERIFY(!rect3->isActive()); // doesn't become active + + // Child of panel activation. + rect3->setActive(true); + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setFlag(QGraphicsItem::ItemIsPanel); + QGraphicsRectItem *rect5 = new QGraphicsRectItem(rect4); + QGraphicsRectItem *rect6 = new QGraphicsRectItem(rect5); + scene.addItem(rect4); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect3); + scene.removeItem(rect4); + rect6->setActive(true); + scene.addItem(rect4); + QVERIFY(rect4->isActive()); + QVERIFY(rect5->isActive()); + QVERIFY(rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect4); + scene.removeItem(rect4); // no active panel + rect6->setActive(false); + scene.addItem(rect4); + QVERIFY(!rect4->isActive()); + QVERIFY(!rect5->isActive()); + QVERIFY(!rect6->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + + // Controlling auto-activation when the scene changes activation. + rect4->setActive(true); + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.isActive()); + QVERIFY(!rect4->isActive()); + rect4->setActive(false); + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + QVERIFY(!scene.activePanel()); + QVERIFY(!rect4->isActive()); +} + +void tst_QGraphicsItem::setActivePanelOnInactiveScene() +{ + QGraphicsScene scene; + QGraphicsRectItem *item = scene.addRect(QRectF()); + QGraphicsRectItem *panel = scene.addRect(QRectF()); + panel->setFlag(QGraphicsItem::ItemIsPanel); + + EventSpy itemActivateSpy(&scene, item, QEvent::WindowActivate); + EventSpy itemDeactivateSpy(&scene, item, QEvent::WindowDeactivate); + EventSpy panelActivateSpy(&scene, panel, QEvent::WindowActivate); + EventSpy panelDeactivateSpy(&scene, panel, QEvent::WindowDeactivate); + EventSpy sceneActivationChangeSpy(&scene, QEvent::ActivationChange); + + scene.setActivePanel(panel); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)0); + QCOMPARE(itemActivateSpy.count(), 0); + QCOMPARE(itemDeactivateSpy.count(), 0); + QCOMPARE(panelActivateSpy.count(), 0); + QCOMPARE(panelDeactivateSpy.count(), 0); + QCOMPARE(sceneActivationChangeSpy.count(), 0); +} + +void tst_QGraphicsItem::activationOnShowHide() +{ + QGraphicsScene scene; + QEvent activate(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &activate); + + QGraphicsRectItem *rootPanel = scene.addRect(QRectF()); + rootPanel->setFlag(QGraphicsItem::ItemIsPanel); + rootPanel->setActive(true); + + QGraphicsRectItem *subPanel = new QGraphicsRectItem; + subPanel->setFlag(QGraphicsItem::ItemIsPanel); + + // Reparenting onto an active panel auto-activates the child panel. + subPanel->setParentItem(rootPanel); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Hiding an active child panel will reactivate the parent panel. + subPanel->hide(); + QVERIFY(rootPanel->isActive()); + + // Showing a child panel will auto-activate it. + subPanel->show(); + QVERIFY(subPanel->isActive()); + QVERIFY(!rootPanel->isActive()); + + // Adding an unrelated panel doesn't affect activation. + QGraphicsRectItem *otherPanel = new QGraphicsRectItem; + otherPanel->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(otherPanel); + QVERIFY(subPanel->isActive()); + + // Showing an unrelated panel doesn't affect activation. + otherPanel->hide(); + otherPanel->show(); + QVERIFY(subPanel->isActive()); + + // Add a non-panel item. + QGraphicsRectItem *otherItem = new QGraphicsRectItem; + scene.addItem(otherItem); + otherItem->setActive(true); + QVERIFY(otherItem->isActive()); + + // Reparent a panel onto an active non-panel item. + subPanel->setParentItem(otherItem); + QVERIFY(subPanel->isActive()); + + // Showing a child panel of a non-panel item will activate it. + subPanel->hide(); + QVERIFY(!subPanel->isActive()); + QVERIFY(otherItem->isActive()); + subPanel->show(); + QVERIFY(subPanel->isActive()); + + // Hiding a toplevel active panel will pass activation back + // to the non-panel items. + rootPanel->setActive(true); + rootPanel->hide(); + QVERIFY(!rootPanel->isActive()); + QVERIFY(otherItem->isActive()); +} + +class MoveWhileDying : public QGraphicsRectItem +{ +public: + MoveWhileDying(QGraphicsItem *parent = 0) + : QGraphicsRectItem(parent) + { } + ~MoveWhileDying() + { + foreach (QGraphicsItem *c, childItems()) { + foreach (QGraphicsItem *cc, c->childItems()) { + cc->moveBy(10, 10); + } + c->moveBy(10, 10); + } + if (QGraphicsItem *p = parentItem()) { p->moveBy(10, 10); } + } +}; + +void tst_QGraphicsItem::moveWhileDeleting() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect = new QGraphicsRectItem; + MoveWhileDying *silly = new MoveWhileDying(rect); + QGraphicsRectItem *child = new QGraphicsRectItem(silly); + scene.addItem(rect); + delete rect; // don't crash! + + rect = new QGraphicsRectItem; + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(125); + + delete rect; + + rect = new QGraphicsRectItem; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + silly = new MoveWhileDying(rect); + child = new QGraphicsRectItem(silly); + + QTest::qWait(125); + + delete rect; + + rect = new MoveWhileDying; + rect->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + child = new QGraphicsRectItem(rect); + silly = new MoveWhileDying(child); + + QTest::qWait(125); + + delete rect; +} + +class MyRectItem : public QGraphicsWidget +{ + Q_OBJECT +public: + MyRectItem(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + { + + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(brush); + painter->drawRect(boundingRect()); + } + void move() + { + setPos(-100,-100); + topLevel->collidingItems(Qt::IntersectsItemBoundingRect); + } +public: + QGraphicsItem *topLevel; + QBrush brush; +}; + + +void tst_QGraphicsItem::ensureDirtySceneTransform() +{ + QGraphicsScene scene; + + MyRectItem *topLevel = new MyRectItem; + topLevel->setGeometry(0, 0, 100, 100); + topLevel->setPos(-50, -50); + topLevel->brush = QBrush(QColor(Qt::black)); + scene.addItem(topLevel); + + MyRectItem *parent = new MyRectItem; + parent->topLevel = topLevel; + parent->setGeometry(0, 0, 100, 100); + parent->setPos(0, 0); + parent->brush = QBrush(QColor(Qt::magenta)); + parent->setObjectName("parent"); + scene.addItem(parent); + + MyRectItem *child = new MyRectItem(parent); + child->setGeometry(0, 0, 80, 80); + child->setPos(10, 10); + child->setObjectName("child"); + child->brush = QBrush(QColor(Qt::blue)); + + MyRectItem *child2 = new MyRectItem(parent); + child2->setGeometry(0, 0, 80, 80); + child2->setPos(15, 15); + child2->setObjectName("child2"); + child2->brush = QBrush(QColor(Qt::green)); + + MyRectItem *child3 = new MyRectItem(parent); + child3->setGeometry(0, 0, 80, 80); + child3->setPos(20, 20); + child3->setObjectName("child3"); + child3->brush = QBrush(QColor(Qt::gray)); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&view)); + + //We move the parent + parent->move(); + QApplication::processEvents(); + + //We check if all items moved + QCOMPARE(child->pos(), QPointF(10, 10)); + QCOMPARE(child2->pos(), QPointF(15, 15)); + QCOMPARE(child3->pos(), QPointF(20, 20)); + + QCOMPARE(child->sceneBoundingRect(), QRectF(-90, -90, 80, 80)); + QCOMPARE(child2->sceneBoundingRect(), QRectF(-85, -85, 80, 80)); + QCOMPARE(child3->sceneBoundingRect(), QRectF(-80, -80, 80, 80)); + + QCOMPARE(child->sceneTransform(), QTransform::fromTranslate(-90, -90)); + QCOMPARE(child2->sceneTransform(), QTransform::fromTranslate(-85, -85)); + QCOMPARE(child3->sceneTransform(), QTransform::fromTranslate(-80, -80)); +} + +void tst_QGraphicsItem::focusScope() +{ + // ItemIsFocusScope is an internal feature (for now). + QGraphicsScene scene; + + QGraphicsRectItem *scope3 = new QGraphicsRectItem; + scope3->setData(0, "scope3"); + scope3->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope3->setFocus(); + QVERIFY(!scope3->focusScopeItem()); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope2 = new QGraphicsRectItem; + scope2->setData(0, "scope2"); + scope2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope2->setFocus(); + QVERIFY(!scope2->focusScopeItem()); + scope3->setParentItem(scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope1 = new QGraphicsRectItem; + scope1->setData(0, "scope1"); + scope1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope1->setFocus(); + QVERIFY(!scope1->focusScopeItem()); + scope2->setParentItem(scope1); + + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + scene.addItem(scope1); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + + QVERIFY(scope3->hasFocus()); + + scope3->hide(); + QVERIFY(scope2->hasFocus()); + scope2->hide(); + QVERIFY(scope1->hasFocus()); + scope2->show(); + QVERIFY(scope2->hasFocus()); + scope3->show(); + QVERIFY(scope3->hasFocus()); + scope1->hide(); + QVERIFY(!scope3->hasFocus()); + scope1->show(); + QVERIFY(scope3->hasFocus()); + scope3->clearFocus(); + QVERIFY(scope2->hasFocus()); + scope2->clearFocus(); + QVERIFY(scope1->hasFocus()); + scope2->hide(); + scope2->show(); + QVERIFY(!scope2->hasFocus()); + QVERIFY(scope1->hasFocus()); + scope2->setFocus(); + QVERIFY(scope2->hasFocus()); + scope3->setFocus(); + QVERIFY(scope3->hasFocus()); + + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setData(0, "rect4"); + rect4->setParentItem(scope3); + + QGraphicsRectItem *rect5 = new QGraphicsRectItem; + rect5->setData(0, "rect5"); + rect5->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rect5->setFocus(); + rect5->setParentItem(rect4); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)rect5); + QVERIFY(rect5->hasFocus()); + + rect4->setParentItem(0); + QVERIFY(rect5->hasFocus()); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)0); + QVERIFY(!scope3->hasFocus()); + + QGraphicsRectItem *rectA = new QGraphicsRectItem; + QGraphicsRectItem *scopeA = new QGraphicsRectItem(rectA); + scopeA->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeA->setFocus(); + QGraphicsRectItem *scopeB = new QGraphicsRectItem(scopeA); + scopeB->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scopeB->setFocus(); + + scene.addItem(rectA); + QVERIFY(rect5->hasFocus()); + QVERIFY(!scopeB->hasFocus()); + + scopeA->setFocus(); + QVERIFY(scopeB->hasFocus()); + QCOMPARE(scopeB->focusItem(), (QGraphicsItem *)scopeB); +} + +void tst_QGraphicsItem::focusScope2() +{ + QGraphicsRectItem *child1 = new QGraphicsRectItem; + child1->setFlags(QGraphicsItem::ItemIsFocusable); + child1->setFocus(); + QCOMPARE(child1->focusItem(), (QGraphicsItem *)child1); + + QGraphicsRectItem *child2 = new QGraphicsRectItem; + child2->setFlags(QGraphicsItem::ItemIsFocusable); + + QGraphicsRectItem *rootFocusScope = new QGraphicsRectItem; + rootFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rootFocusScope->setFocus(); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)rootFocusScope); + + child1->setParentItem(rootFocusScope); + child2->setParentItem(rootFocusScope); + + QCOMPARE(rootFocusScope->focusScopeItem(), (QGraphicsItem *)child1); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)child1); + + QGraphicsRectItem *siblingChild1 = new QGraphicsRectItem; + siblingChild1->setFlags(QGraphicsItem::ItemIsFocusable); + siblingChild1->setFocus(); + + QGraphicsRectItem *siblingChild2 = new QGraphicsRectItem; + siblingChild2->setFlags(QGraphicsItem::ItemIsFocusable); + + QGraphicsRectItem *siblingFocusScope = new QGraphicsRectItem; + siblingFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + + siblingChild1->setParentItem(siblingFocusScope); + siblingChild2->setParentItem(siblingFocusScope); + + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild1); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + QGraphicsItem *root = new QGraphicsRectItem; + rootFocusScope->setParentItem(root); + siblingFocusScope->setParentItem(root); + + QCOMPARE(root->focusItem(), (QGraphicsItem *)child1); + + QGraphicsScene scene; + scene.addItem(root); + + QEvent activate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &activate); + scene.setFocus(); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)child1); + + // You cannot set focus on a descendant of a focus scope directly; + // this will only change the scope's focus scope item pointer. If + // you want to give true input focus, you must set it directly on + // the scope itself + siblingChild2->setFocus(); + QVERIFY(!siblingChild2->hasFocus()); + QVERIFY(!siblingChild2->focusItem()); + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + // Set focus on the scope; focus is forwarded to the focus scope item. + siblingFocusScope->setFocus(); + QVERIFY(siblingChild2->hasFocus()); + QVERIFY(siblingChild2->focusItem()); + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)siblingChild2); +} + +void tst_QGraphicsItem::stackBefore() +{ + QGraphicsRectItem parent; + QGraphicsRectItem *child1 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child2 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child3 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QGraphicsRectItem *child4 = new QGraphicsRectItem(QRectF(0, 0, 5, 5), &parent); + QCOMPARE(parent.childItems(), (QList() << child1 << child2 << child3 << child4)); + child1->setData(0, "child1"); + child2->setData(0, "child2"); + child3->setData(0, "child3"); + child4->setData(0, "child4"); + + // Remove and append + child2->setParentItem(0); + child2->setParentItem(&parent); + QCOMPARE(parent.childItems(), (QList() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); // 2134 + QCOMPARE(parent.childItems(), (QList() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); // 2134 + QCOMPARE(parent.childItems(), (QList() << child2 << child1 << child3 << child4)); + child1->setZValue(1); // 2341 + QCOMPARE(parent.childItems(), (QList() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // 2341 + QCOMPARE(parent.childItems(), (QList() << child2 << child3 << child4 << child1)); + child1->setZValue(0); // 1234 + QCOMPARE(parent.childItems(), (QList() << child1 << child2 << child3 << child4)); + child4->stackBefore(child1); // 4123 + QCOMPARE(parent.childItems(), (QList() << child4 << child1 << child2 << child3)); + child4->setZValue(1); // 1234 (4123) + QCOMPARE(parent.childItems(), (QList() << child1 << child2 << child3 << child4)); + child3->stackBefore(child1); // 3124 (4312) + QCOMPARE(parent.childItems(), (QList() << child3 << child1 << child2 << child4)); + child4->setZValue(0); // 4312 + QCOMPARE(parent.childItems(), (QList() << child4 << child3 << child1 << child2)); + + // Make them all toplevels + child1->setParentItem(0); + child2->setParentItem(0); + child3->setParentItem(0); + child4->setParentItem(0); + + QGraphicsScene scene; + scene.addItem(child1); + scene.addItem(child2); + scene.addItem(child3); + scene.addItem(child4); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), + (QList() << child1 << child2 << child3 << child4)); + + // Remove and append + scene.removeItem(child2); + scene.addItem(child2); + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child1 << child3 << child4 << child2)); + + // Move child2 before child1 + child2->stackBefore(child1); // 2134 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child2 << child1 << child3 << child4)); + child2->stackBefore(child2); // 2134 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child2 << child1 << child3 << child4)); + child1->setZValue(1); // 2341 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child2 << child3 << child4 << child1)); + child1->stackBefore(child2); // 2341 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child2 << child3 << child4 << child1)); + child1->setZValue(0); // 1234 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child1 << child2 << child3 << child4)); + child4->stackBefore(child1); // 4123 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child4 << child1 << child2 << child3)); + child4->setZValue(1); // 1234 (4123) + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child1 << child2 << child3 << child4)); + child3->stackBefore(child1); // 3124 (4312) + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child3 << child1 << child2 << child4)); + child4->setZValue(0); // 4312 + QCOMPARE(scene.items(QPointF(2, 2), Qt::IntersectsItemBoundingRect, Qt::AscendingOrder), (QList() << child4 << child3 << child1 << child2)); +} + +void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect() +{ + EventTester *tester = new EventTester; + tester->setCacheMode(QGraphicsItem::ItemCoordinateCache); + + QGraphicsScene scene; + scene.addItem(tester); + scene.setSceneRect(-100, -100, 200, 200); // contains the tester item + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + + QTRY_COMPARE(tester->repaints, 1); + + scene.update(); // triggers "updateAll" optimization + qApp->processEvents(); + qApp->processEvents(); // in 4.6 only one processEvents is necessary + + QCOMPARE(tester->repaints, 1); + + scene.update(); // triggers "updateAll" optimization + tester->update(); + qApp->processEvents(); + qApp->processEvents(); // in 4.6 only one processEvents is necessary + + QCOMPARE(tester->repaints, 2); +} + +void tst_QGraphicsItem::sceneModality() +{ + // 1) Test mouse events (delivery/propagation/redirection) + // 2) Test hover events (incl. leave on block, enter on unblock) + // 3) Test cursor stuff (incl. unset on block, set on unblock) + // 4) Test clickfocus + // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock) + // 6) ### modality for non-panels is unsupported for now + QGraphicsScene scene; + + QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200); + bottomItem->setFlag(QGraphicsItem::ItemIsFocusable); + bottomItem->setBrush(Qt::yellow); + + QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100); + leftParent->setFlag(QGraphicsItem::ItemIsPanel); + leftParent->setBrush(Qt::blue); + + QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50); + leftChild->setFlag(QGraphicsItem::ItemIsPanel); + leftChild->setBrush(Qt::green); + leftChild->setParentItem(leftParent); + + QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100); + rightParent->setFlag(QGraphicsItem::ItemIsPanel); + rightParent->setBrush(Qt::red); + QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50); + rightChild->setFlag(QGraphicsItem::ItemIsPanel); + rightChild->setBrush(Qt::gray); + rightChild->setParentItem(rightParent); + + leftParent->setPos(-75, 0); + rightParent->setPos(75, 0); + + bottomItem->setData(0, "bottomItem"); + leftParent->setData(0, "leftParent"); + leftChild->setData(0, "leftChild"); + rightParent->setData(0, "rightParent"); + rightChild->setData(0, "rightChild"); + + scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50)); + + EventSpy2 leftParentSpy(&scene, leftParent); + EventSpy2 leftChildSpy(&scene, leftChild); + EventSpy2 rightParentSpy(&scene, rightParent); + EventSpy2 rightChildSpy(&scene, rightChild); + EventSpy2 bottomItemSpy(&scene, bottomItem); + + // Scene modality, also test multiple scene modal items + leftChild->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); // not a panel + + // Click inside left child + sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + // Click on left parent, event goes to modal child + sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + // Click on all other items and outside the items + sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3); + sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4); + sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5); + sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + leftChild->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0); + + // Left parent enters scene modality. + leftParent->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); + + // Click inside left child. + sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // panel stops propagation + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + // Click on left parent. + sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); + + // Click on all other items and outside the items + sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 2); + sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 3); + sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 4); + sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 5); + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); + + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + // Now both left parent and child are scene modal. Left parent is blocked. + leftChild->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); + + // Click inside left child + sendMouseClick(&scene, leftChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + // Click on left parent, event goes to modal child + sendMouseClick(&scene, leftParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 2); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + // Click on all other items and outside the items + sendMouseClick(&scene, rightParent->sceneBoundingRect().topLeft() + QPointF(5, 5), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 3); + sendMouseClick(&scene, rightChild->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 4); + sendMouseClick(&scene, bottomItem->scenePos(), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 5); + sendMouseClick(&scene, QPointF(10000, 10000), Qt::LeftButton); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMousePress], 6); + QCOMPARE(leftChildSpy.counts[QEvent::GraphicsSceneMouseRelease], 0); // no grab + QCOMPARE(leftParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightParentSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(rightChildSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + QCOMPARE(bottomItemSpy.counts[QEvent::GraphicsSceneMousePress], 0); // blocked + + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + // Right child enters scene modality, only left child is blocked. + rightChild->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); +} + +void tst_QGraphicsItem::panelModality() +{ + // 1) Test mouse events (delivery/propagation/redirection) + // 2) Test hover events (incl. leave on block, enter on unblock) + // 3) Test cursor stuff (incl. unset on block, set on unblock) + // 4) Test clickfocus + // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock) + // 6) ### modality for non-panels is unsupported for now + QGraphicsScene scene; + + QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200); + bottomItem->setFlag(QGraphicsItem::ItemIsFocusable); + bottomItem->setBrush(Qt::yellow); + + QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100); + leftParent->setFlag(QGraphicsItem::ItemIsPanel); + leftParent->setBrush(Qt::blue); + + QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50); + leftChild->setFlag(QGraphicsItem::ItemIsPanel); + leftChild->setBrush(Qt::green); + leftChild->setParentItem(leftParent); + + QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100); + rightParent->setFlag(QGraphicsItem::ItemIsPanel); + rightParent->setBrush(Qt::red); + QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50); + rightChild->setFlag(QGraphicsItem::ItemIsPanel); + rightChild->setBrush(Qt::gray); + rightChild->setParentItem(rightParent); + + leftParent->setPos(-75, 0); + rightParent->setPos(75, 0); + + bottomItem->setData(0, "bottomItem"); + leftParent->setData(0, "leftParent"); + leftChild->setData(0, "leftChild"); + rightParent->setData(0, "rightParent"); + rightChild->setData(0, "rightChild"); + + scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50)); + + EventSpy2 leftParentSpy(&scene, leftParent); + EventSpy2 leftChildSpy(&scene, leftChild); + EventSpy2 rightParentSpy(&scene, rightParent); + EventSpy2 rightChildSpy(&scene, rightChild); + EventSpy2 bottomItemSpy(&scene, bottomItem); + + // Left Child enters panel modality, only left parent is blocked. + leftChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); + + leftChild->setPanelModality(QGraphicsItem::NonModal); + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + // Left parent enter panel modality, nothing is blocked. + leftParent->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); + + // Left child enters panel modality, left parent is blocked again. + leftChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowBlocked], 0); + + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + leftChild->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1); + leftParent->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(bottomItemSpy.counts[QEvent::WindowUnblocked], 0); + + leftChildSpy.counts.clear(); + rightChildSpy.counts.clear(); + leftParentSpy.counts.clear(); + rightParentSpy.counts.clear(); + bottomItemSpy.counts.clear(); + + // Left and right child enter panel modality, both parents are blocked. + rightChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + leftChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); +} + +void tst_QGraphicsItem::mixedModality() +{ + // 1) Test mouse events (delivery/propagation/redirection) + // 2) Test hover events (incl. leave on block, enter on unblock) + // 3) Test cursor stuff (incl. unset on block, set on unblock) + // 4) Test clickfocus + // 5) Test grab/ungrab events (possibly ungrab on block, regrab on unblock) + // 6) ### modality for non-panels is unsupported for now + QGraphicsScene scene; + + QGraphicsRectItem *bottomItem = scene.addRect(-150, -100, 300, 200); + bottomItem->setFlag(QGraphicsItem::ItemIsFocusable); + bottomItem->setBrush(Qt::yellow); + + QGraphicsRectItem *leftParent = scene.addRect(-50, -50, 100, 100); + leftParent->setFlag(QGraphicsItem::ItemIsPanel); + leftParent->setBrush(Qt::blue); + + QGraphicsRectItem *leftChild = scene.addRect(-25, -25, 50, 50); + leftChild->setFlag(QGraphicsItem::ItemIsPanel); + leftChild->setBrush(Qt::green); + leftChild->setParentItem(leftParent); + + QGraphicsRectItem *rightParent = scene.addRect(-50, -50, 100, 100); + rightParent->setFlag(QGraphicsItem::ItemIsPanel); + rightParent->setBrush(Qt::red); + QGraphicsRectItem *rightChild = scene.addRect(-25, -25, 50, 50); + rightChild->setFlag(QGraphicsItem::ItemIsPanel); + rightChild->setBrush(Qt::gray); + rightChild->setParentItem(rightParent); + + leftParent->setPos(-75, 0); + rightParent->setPos(75, 0); + + bottomItem->setData(0, "bottomItem"); + leftParent->setData(0, "leftParent"); + leftChild->setData(0, "leftChild"); + rightParent->setData(0, "rightParent"); + rightChild->setData(0, "rightChild"); + + scene.setSceneRect(scene.itemsBoundingRect().adjusted(-50, -50, 50, 50)); + + EventSpy2 leftParentSpy(&scene, leftParent); + EventSpy2 leftChildSpy(&scene, leftChild); + EventSpy2 rightParentSpy(&scene, rightParent); + EventSpy2 rightChildSpy(&scene, rightChild); + EventSpy2 bottomItemSpy(&scene, bottomItem); + + // Left Child enters panel modality, only left parent is blocked. + leftChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 0); + + // Left parent enters scene modality, which blocks everything except the child. + leftParent->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); + + // Right child enters panel modality (changes nothing). + rightChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); + + // Left parent leaves modality. Right child is unblocked. + leftParent->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 0); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); + + // Right child "upgrades" its modality to scene modal. Left child is blocked. + // Right parent is unaffected. + rightChild->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); + + // "downgrade" right child back to panel modal, left child is unblocked + rightChild->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(leftChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftChildSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightChildSpy.counts[QEvent::WindowUnblocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(leftParentSpy.counts[QEvent::WindowUnblocked], 0); + QCOMPARE(rightParentSpy.counts[QEvent::WindowBlocked], 1); + QCOMPARE(rightParentSpy.counts[QEvent::WindowUnblocked], 0); +} + +void tst_QGraphicsItem::modality_hover() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100); + rect1->setFlag(QGraphicsItem::ItemIsPanel); + rect1->setAcceptHoverEvents(true); + rect1->setData(0, "rect1"); + + QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100); + rect2->setParentItem(rect1); + rect2->setFlag(QGraphicsItem::ItemIsPanel); + rect2->setAcceptHoverEvents(true); + rect2->setPos(50, 50); + rect2->setPanelModality(QGraphicsItem::SceneModal); + rect2->setData(0, "rect2"); + + EventSpy2 rect1Spy(&scene, rect1); + EventSpy2 rect2Spy(&scene, rect2); + + sendMouseMove(&scene, QPointF(-25, -25)); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0); + + sendMouseMove(&scene, QPointF(75, 75)); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 0); + + sendMouseMove(&scene, QPointF(-25, -25)); + + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 0); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 0); + + rect2->setPanelModality(QGraphicsItem::NonModal); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1); + + sendMouseMove(&scene, QPointF(75, 75)); + + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 2); + + rect2->setPanelModality(QGraphicsItem::SceneModal); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2); + // changing modality causes a spurious GraphicsSceneHoveMove, even though the mouse didn't + // actually move + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3); + + sendMouseMove(&scene, QPointF(-25, -25)); + + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1); + + rect2->setPanelModality(QGraphicsItem::PanelModal); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverLeave], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2); + + rect2->setPanelModality(QGraphicsItem::NonModal); + + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverEnter], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneHoverMove], 2); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverEnter], 2); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverMove], 3); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneHoverLeave], 2); +} + +void tst_QGraphicsItem::modality_mouseGrabber() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100); + rect1->setFlag(QGraphicsItem::ItemIsPanel); + rect1->setFlag(QGraphicsItem::ItemIsMovable); + rect1->setData(0, "rect1"); + + QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100); + rect2->setParentItem(rect1); + rect2->setFlag(QGraphicsItem::ItemIsPanel); + rect2->setFlag(QGraphicsItem::ItemIsMovable); + rect2->setPos(50, 50); + rect2->setData(0, "rect2"); + + EventSpy2 rect1Spy(&scene, rect1); + EventSpy2 rect2Spy(&scene, rect2); + + { + // pressing mouse on rect1 starts implicit grab + sendMousePress(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect1); + + // grab lost when rect1 is modally shadowed + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // releasing goes nowhere + sendMouseRelease(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // pressing mouse on rect1 starts implicit grab on rect2 (since it is modal) + sendMouseClick(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + + rect2->setPanelModality(QGraphicsItem::NonModal); + + // pressing mouse on rect1 starts implicit grab + sendMousePress(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect1); + + // grab lost to rect2 when rect1 is modally shadowed + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // rect1 does *not* re-grab when rect2 is no longer modal + rect2->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // release goes nowhere + sendMouseRelease(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + } + { + // repeat the test using PanelModal + rect2->setPanelModality(QGraphicsItem::NonModal); + rect1Spy.counts.clear(); + rect2Spy.counts.clear(); + + // pressing mouse on rect1 starts implicit grab + sendMousePress(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect1); + + // grab lost when rect1 is modally shadowed + rect2->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // releasing goes nowhere + sendMouseRelease(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // pressing mouse on rect1 starts implicit grab on rect2 (since it is modal) + sendMouseClick(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect2Spy.counts[QEvent::GraphicsSceneMouseRelease], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + + rect2->setPanelModality(QGraphicsItem::NonModal); + + // pressing mouse on rect1 starts implicit grab + sendMousePress(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMousePress], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect1); + + // grab lost to rect2 when rect1 is modally shadowed + rect2->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // rect1 does *not* re-grab when rect2 is no longer modal + rect2->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + // release goes nowhere + sendMouseRelease(&scene, QPoint(-25, -25)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect1Spy.counts[QEvent::GraphicsSceneMouseRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + } + + { + // repeat the PanelModal tests, but this time the mouse events will be on a non-modal item, + // meaning normal grabbing should work + rect2->setPanelModality(QGraphicsItem::NonModal); + rect1Spy.counts.clear(); + rect2Spy.counts.clear(); + + QGraphicsRectItem *rect3 = scene.addRect(-50, -50, 100, 100); + rect3->setFlag(QGraphicsItem::ItemIsPanel); + rect3->setFlag(QGraphicsItem::ItemIsMovable); + rect3->setPos(150, 50); + rect3->setData(0, "rect3"); + + EventSpy2 rect3Spy(&scene, rect3); + + // pressing mouse on rect3 starts implicit grab + sendMousePress(&scene, QPoint(150, 50)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMousePress], 1); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect3); + + // grab is *not* lost when rect1 is modally shadowed by rect2 + rect2->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect3); + + // releasing goes to rect3 + sendMouseRelease(&scene, QPoint(150, 50)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 1); + QCOMPARE(rect3Spy.counts[QEvent::GraphicsSceneMouseRelease], 1); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + + rect2->setPanelModality(QGraphicsItem::NonModal); + + // pressing mouse on rect3 starts implicit grab + sendMousePress(&scene, QPoint(150, 50)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect3); + + // grab is not lost + rect2->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect3); + + // grab stays on rect3 + rect2->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) rect3); + + // release goes to rect3 + sendMouseRelease(&scene, QPoint(150, 50)); + QCOMPARE(rect1Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect1Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::GrabMouse], 0); + QCOMPARE(rect2Spy.counts[QEvent::UngrabMouse], 0); + QCOMPARE(rect3Spy.counts[QEvent::GrabMouse], 2); + QCOMPARE(rect3Spy.counts[QEvent::UngrabMouse], 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *) 0); + } +} + +void tst_QGraphicsItem::modality_clickFocus() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100); + rect1->setFlag(QGraphicsItem::ItemIsPanel); + rect1->setFlag(QGraphicsItem::ItemIsFocusable); + rect1->setData(0, "rect1"); + + QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100); + rect2->setParentItem(rect1); + rect2->setFlag(QGraphicsItem::ItemIsPanel); + rect2->setFlag(QGraphicsItem::ItemIsFocusable); + rect2->setPos(50, 50); + rect2->setData(0, "rect2"); + + QEvent windowActivateEvent(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &windowActivateEvent); + + EventSpy2 rect1Spy(&scene, rect1); + EventSpy2 rect2Spy(&scene, rect2); + + // activate rect1, it should not get focus + rect1->setActive(true); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + + // focus stays unset when rect2 becomes modal + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0); + + // clicking on rect1 should not set it's focus item + sendMouseClick(&scene, QPointF(-25, -25)); + QCOMPARE(rect1->focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0); + + // clicking on rect2 gives it focus + rect2->setActive(true); + sendMouseClick(&scene, QPointF(75, 75)); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) rect2); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 0); + + // clicking on rect1 does *not* give it focus + rect1->setActive(true); + sendMouseClick(&scene, QPointF(-25, -25)); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1); + + // focus doesn't change when leaving modality either + rect2->setPanelModality(QGraphicsItem::NonModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 0); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1); + + // click on rect1, it should get focus now + sendMouseClick(&scene, QPointF(-25, -25)); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) rect1); + QCOMPARE(rect1Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect1Spy.counts[QEvent::FocusOut], 0); + QCOMPARE(rect2Spy.counts[QEvent::FocusIn], 1); + QCOMPARE(rect2Spy.counts[QEvent::FocusOut], 1); +} + +void tst_QGraphicsItem::modality_keyEvents() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = scene.addRect(-50, -50, 100, 100); + rect1->setFlag(QGraphicsItem::ItemIsPanel); + rect1->setFlag(QGraphicsItem::ItemIsFocusable); + rect1->setData(0, "rect1"); + + QGraphicsRectItem *rect1child = scene.addRect(-10, -10, 20, 20); + rect1child->setParentItem(rect1); + rect1child->setFlag(QGraphicsItem::ItemIsFocusable); + rect1child->setData(0, "rect1child1"); + + QGraphicsRectItem *rect2 = scene.addRect(-50, -50, 100, 100); + rect2->setParentItem(rect1); + rect2->setFlag(QGraphicsItem::ItemIsPanel); + rect2->setFlag(QGraphicsItem::ItemIsFocusable); + rect2->setPos(50, 50); + rect2->setData(0, "rect2"); + + QGraphicsRectItem *rect2child = scene.addRect(-10, -10, 20, 20); + rect2child->setParentItem(rect2); + rect2child->setFlag(QGraphicsItem::ItemIsFocusable); + rect2child->setData(0, "rect2child1"); + + QEvent windowActivateEvent(QEvent::WindowActivate); + QApplication::sendEvent(&scene, &windowActivateEvent); + + EventSpy2 rect1Spy(&scene, rect1); + EventSpy2 rect1childSpy(&scene, rect1child); + EventSpy2 rect2Spy(&scene, rect2); + EventSpy2 rect2childSpy(&scene, rect2child); + + // activate rect1 and give it rect1child focus + rect1->setActive(true); + rect1child->setFocus(); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) rect1child); + + // focus stays on rect1child when rect2 becomes modal + rect2->setPanelModality(QGraphicsItem::SceneModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) rect1child); + + // but key events to rect1child should be neither delivered nor propagated + sendKeyClick(&scene, Qt::Key_A); + sendKeyClick(&scene, Qt::Key_S); + sendKeyClick(&scene, Qt::Key_D); + sendKeyClick(&scene, Qt::Key_F); + QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0); + QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0); + QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0); + + // change to panel modality, rect1child1 keeps focus + rect2->setPanelModality(QGraphicsItem::PanelModal); + QCOMPARE(scene.focusItem(), (QGraphicsItem *) rect1child); + + // still no key events + sendKeyClick(&scene, Qt::Key_J); + sendKeyClick(&scene, Qt::Key_K); + sendKeyClick(&scene, Qt::Key_L); + sendKeyClick(&scene, Qt::Key_Semicolon); + QCOMPARE(rect1childSpy.counts[QEvent::KeyPress], 0); + QCOMPARE(rect1childSpy.counts[QEvent::KeyRelease], 0); + QCOMPARE(rect1Spy.counts[QEvent::KeyPress], 0); + QCOMPARE(rect1Spy.counts[QEvent::KeyRelease], 0); +} + +void tst_QGraphicsItem::itemIsInFront() +{ + QGraphicsScene scene; + QGraphicsRectItem *rect1 = new QGraphicsRectItem; + rect1->setData(0, "rect1"); + scene.addItem(rect1); + + QGraphicsRectItem *rect1child1 = new QGraphicsRectItem(rect1); + rect1child1->setZValue(1); + rect1child1->setData(0, "rect1child1"); + + QGraphicsRectItem *rect1child2 = new QGraphicsRectItem(rect1); + rect1child2->setParentItem(rect1); + rect1child2->setData(0, "rect1child2"); + + QGraphicsRectItem *rect1child1_1 = new QGraphicsRectItem(rect1child1); + rect1child1_1->setData(0, "rect1child1_1"); + + QGraphicsRectItem *rect1child1_2 = new QGraphicsRectItem(rect1child1); + rect1child1_2->setFlag(QGraphicsItem::ItemStacksBehindParent); + rect1child1_2->setData(0, "rect1child1_2"); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + rect2->setData(0, "rect2"); + scene.addItem(rect2); + + QGraphicsRectItem *rect2child1 = new QGraphicsRectItem(rect2); + rect2child1->setData(0, "rect2child1"); + + QCOMPARE(qt_closestItemFirst(rect1, rect1), false); + QCOMPARE(qt_closestItemFirst(rect1, rect2), false); + QCOMPARE(qt_closestItemFirst(rect1child1, rect2child1), false); + QCOMPARE(qt_closestItemFirst(rect1child1, rect1child2), true); + QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child2), true); + QCOMPARE(qt_closestItemFirst(rect1child1_1, rect1child1), true); + QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child2), true); + QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1child1), false); + QCOMPARE(qt_closestItemFirst(rect1child1_2, rect1), true); + QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2), false); + QCOMPARE(qt_closestItemFirst(rect1child1_2, rect2child1), false); +} + +class ScenePosChangeTester : public ItemChangeTester +{ +public: + ScenePosChangeTester() + { } + ScenePosChangeTester(QGraphicsItem *parent) : ItemChangeTester(parent) + { } +}; + +void tst_QGraphicsItem::scenePosChange() +{ + ScenePosChangeTester* root = new ScenePosChangeTester; + ScenePosChangeTester* child1 = new ScenePosChangeTester(root); + ScenePosChangeTester* grandChild1 = new ScenePosChangeTester(child1); + ScenePosChangeTester* child2 = new ScenePosChangeTester(root); + ScenePosChangeTester* grandChild2 = new ScenePosChangeTester(child2); + + child1->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + grandChild2->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + + QVERIFY(child1->flags() & QGraphicsItem::ItemSendsScenePositionChanges); + QVERIFY(grandChild2->flags() & QGraphicsItem::ItemSendsScenePositionChanges); + + QGraphicsScene scene; + scene.addItem(root); + + // ignore uninteresting changes + child1->clear(); + child2->clear(); + grandChild1->clear(); + grandChild2->clear(); + + // move whole tree + root->moveBy(1.0, 1.0); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + + // move subtree + child2->moveBy(1.0, 1.0); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2); + + // reparent + grandChild2->setParentItem(child1); + child1->moveBy(1.0, 1.0); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3); + + // change flags + grandChild1->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + grandChild2->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, false); + QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants() + child1->moveBy(1.0, 1.0); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + QCOMPARE(grandChild2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 3); + + // remove + scene.removeItem(grandChild1); + delete grandChild2; grandChild2 = 0; + QCoreApplication::processEvents(); // QGraphicsScenePrivate::_q_updateScenePosDescendants() + root->moveBy(1.0, 1.0); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 4); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + + root->setX(1); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 5); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); + + root->setY(1); + QCOMPARE(child1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 6); + QCOMPARE(grandChild1->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); +} + +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : nbUpdates(0) {} + ~MyInputContext() {} + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() {} + + bool isComposing() const { return false; } + + void update() { nbUpdates++; } + + bool nbUpdates; +}; + +class MyInputWidget : public QGraphicsWidget +{ +public: + MyInputWidget() + { + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + } + void mousePressEvent(QGraphicsSceneMouseEvent *event) + { + event->accept(); + } + + void doUpdateMicroFocus() + { + if (QWidget *fw = QApplication::focusWidget()) { + if (scene()) { + for (int i = 0 ; i < scene()->views().count() ; ++i) { + if (scene()->views().at(i) == fw) { + if (QInputContext *inputContext = fw->inputContext()) { + inputContext->update(); + } + } + } + } + } + } +}; + +void tst_QGraphicsItem::updateMicroFocus() +{ +#if defined Q_OS_WIN || defined Q_OS_MAC + QSKIP("QTBUG-9578", SkipAll); +#endif + QGraphicsScene scene; + QWidget parent; + QGridLayout layout; + parent.setLayout(&layout); + QGraphicsView view(&scene); + QGraphicsView view2(&scene); + layout.addWidget(&view, 0, 0); + layout.addWidget(&view2, 0, 1); + MyInputContext *ic = new MyInputContext; + qApp->setInputContext(ic); + MyInputWidget input; + input.setPos(0, 0); + input.resize(150, 150); + scene.addItem(&input); + input.setFocus(); + parent.show(); + view.setFocus(); + qApp->setAutoSipEnabled(true); + QApplication::setActiveWindow(&parent); + QTest::qWaitForWindowShown(&parent); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&parent)); + //We reset the number of updates that happened previously (initialisation) + ic->nbUpdates = 0; + input.doUpdateMicroFocus(); + QApplication::processEvents(); + QTRY_COMPARE(ic->nbUpdates, 1); +} + +void tst_QGraphicsItem::textItem_shortcuts() +{ + QWidget w; + QVBoxLayout l; + w.setLayout(&l); + QGraphicsScene scene; + QGraphicsView view(&scene); + l.addWidget(&view); + QPushButton b("Push Me"); + l.addWidget(&b); + + QGraphicsTextItem *item = scene.addText("Troll Text"); + item->setFlag(QGraphicsItem::ItemIsFocusable); + item->setTextInteractionFlags(Qt::TextEditorInteraction); + w.show(); + QTest::qWaitForWindowShown(&w); + + item->setFocus(); + QTRY_VERIFY(item->hasFocus()); + QVERIFY(item->textCursor().selectedText().isEmpty()); + + // Shortcut should work (select all) + QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier); + QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText()); + QTextCursor tc = item->textCursor(); + tc.clearSelection(); + item->setTextCursor(tc); + QVERIFY(item->textCursor().selectedText().isEmpty()); + + // Shortcut should also work if the text item has the focus and another widget + // has the same shortcut. + b.setShortcut(QKeySequence("CTRL+A")); + QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier); + QTRY_COMPARE(item->textCursor().selectedText(), item->toPlainText()); +} + +void tst_QGraphicsItem::scroll() +{ + // Create two overlapping rectangles in the scene: + // +-------+ + // | | <- item1 + // | +-------+ + // | | | + // +---| | <- item2 + // | | + // +-------+ + + EventTester *item1 = new EventTester; + item1->br = QRectF(0, 0, 200, 200); + item1->brush = Qt::red; + item1->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); + + EventTester *item2 = new EventTester; + item2->br = QRectF(0, 0, 200, 200); + item2->brush = Qt::blue; + item2->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); + item2->setPos(100, 100); + + QGraphicsScene scene(0, 0, 300, 300); + scene.addItem(item1); + scene.addItem(item2); + + MyGraphicsView view(&scene); + view.setFrameStyle(0); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + view.reset(); + item1->reset(); + item2->reset(); + + const QRectF item1BoundingRect = item1->boundingRect(); + const QRectF item2BoundingRect = item2->boundingRect(); + + // Scroll item1: + // Item1 should get full exposure + // Item2 should get exposure for the part that overlaps item1. + item1->scroll(0, -10); + QTRY_VERIFY(view.repaints > 0); + QCOMPARE(item1->lastExposedRect, item1BoundingRect); + + QRectF expectedItem2Expose = item2BoundingRect; + // NB! Adjusted by 2 pixels for antialiasing + expectedItem2Expose &= item1->mapRectToItem(item2, item1BoundingRect.adjusted(-2, -2, 2, 2)); + QCOMPARE(item2->lastExposedRect, expectedItem2Expose); + + // Enable ItemCoordinateCache on item1. + view.reset(); + item1->setCacheMode(QGraphicsItem::ItemCoordinateCache); + QTRY_VERIFY(view.repaints > 0); + view.reset(); + item1->reset(); + item2->reset(); + + // Scroll item1: + // Item1 should only get expose for the newly exposed area (accelerated scroll). + // Item2 should get exposure for the part that overlaps item1. + item1->scroll(0, -10, QRectF(50, 50, 100, 100)); + QTRY_VERIFY(view.repaints > 0); + QCOMPARE(item1->lastExposedRect, QRectF(50, 140, 100, 10)); + + expectedItem2Expose = item2BoundingRect; + // NB! Adjusted by 2 pixels for antialiasing + expectedItem2Expose &= item1->mapRectToItem(item2, QRectF(50, 50, 100, 100).adjusted(-2, -2, 2, 2)); + QCOMPARE(item2->lastExposedRect, expectedItem2Expose); +} + +Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlag); + +void tst_QGraphicsItem::focusHandling_data() +{ + QTest::addColumn("focusFlag"); + QTest::addColumn("useStickyFocus"); + QTest::addColumn("expectedFocusItem"); // 0: none, 1: focusableUnder, 2: itemWithFocus + + QTest::newRow("Focus goes through.") + << static_cast(0x0) << false << 1; + + QTest::newRow("Focus goes through, even with sticky scene.") + << static_cast(0x0) << true << 1; + + QTest::newRow("With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (but can still focus-out).") + << QGraphicsItem::ItemStopsClickFocusPropagation << false << 0; + + QTest::newRow("With ItemStopsClickFocusPropagation, we cannot focus the item beneath the flagged one (and cannot focus-out if scene is sticky).") + << QGraphicsItem::ItemStopsClickFocusPropagation << true << 2; + + QTest::newRow("With ItemStopsFocusHandling, focus cannot be changed by presses.") + << QGraphicsItem::ItemStopsFocusHandling << false << 2; + + QTest::newRow("With ItemStopsFocusHandling, focus cannot be changed by presses (even if scene is sticky).") + << QGraphicsItem::ItemStopsFocusHandling << true << 2; +} + +void tst_QGraphicsItem::focusHandling() +{ + QFETCH(QGraphicsItem::GraphicsItemFlag, focusFlag); + QFETCH(bool, useStickyFocus); + QFETCH(int, expectedFocusItem); + + class MyItem : public QGraphicsRectItem + { + public: + MyItem() : QGraphicsRectItem(0, 0, 100, 100) {} + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->fillRect(boundingRect(), hasFocus() ? QBrush(Qt::red) : brush()); + } + }; + + QGraphicsRectItem *noFocusOnTop = new MyItem; + noFocusOnTop->setFlag(QGraphicsItem::ItemIsFocusable, false); + noFocusOnTop->setBrush(Qt::yellow); + + QGraphicsRectItem *focusableUnder = new MyItem; + focusableUnder->setBrush(Qt::blue); + focusableUnder->setFlag(QGraphicsItem::ItemIsFocusable); + focusableUnder->setPos(50, 50); + + QGraphicsRectItem *itemWithFocus = new MyItem; + itemWithFocus->setBrush(Qt::black); + itemWithFocus->setFlag(QGraphicsItem::ItemIsFocusable); + itemWithFocus->setPos(250, 10); + + QGraphicsScene scene(-50, -50, 400, 400); + scene.addItem(noFocusOnTop); + scene.addItem(focusableUnder); + scene.addItem(itemWithFocus); + scene.setStickyFocus(useStickyFocus); + + noFocusOnTop->setFlag(focusFlag); + focusableUnder->stackBefore(noFocusOnTop); + itemWithFocus->setFocus(); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QApplication::setActiveWindow(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&view)); + QVERIFY(itemWithFocus->hasFocus()); + + const QPointF mousePressPoint = noFocusOnTop->mapToScene(noFocusOnTop->boundingRect().center()); + const QList itemsAtMousePressPosition = scene.items(mousePressPoint); + QVERIFY(itemsAtMousePressPosition.contains(noFocusOnTop)); + + sendMousePress(&scene, mousePressPoint); + + switch (expectedFocusItem) { + case 0: + QCOMPARE(scene.focusItem(), static_cast(0)); + break; + case 1: + QCOMPARE(scene.focusItem(), focusableUnder); + break; + case 2: + QCOMPARE(scene.focusItem(), itemWithFocus); + break; + } + + // Sanity check - manually setting the focus must work regardless of our + // focus handling flags: + focusableUnder->setFocus(); + QCOMPARE(scene.focusItem(), focusableUnder); +} + +void tst_QGraphicsItem::touchEventPropagation_data() +{ + QTest::addColumn("flag"); + QTest::addColumn("expectedCount"); + + QTest::newRow("ItemIsPanel") + << QGraphicsItem::ItemIsPanel << 0; + QTest::newRow("ItemStopsClickFocusPropagation") + << QGraphicsItem::ItemStopsClickFocusPropagation << 1; + QTest::newRow("ItemStopsFocusHandling") + << QGraphicsItem::ItemStopsFocusHandling << 1; +} + +void tst_QGraphicsItem::touchEventPropagation() +{ + QFETCH(QGraphicsItem::GraphicsItemFlag, flag); + QFETCH(int, expectedCount); + + class Testee : public QGraphicsRectItem + { + public: + int touchBeginEventCount; + + Testee() + : QGraphicsRectItem(0, 0, 100, 100) + , touchBeginEventCount(0) + { + setAcceptTouchEvents(true); + setFlag(QGraphicsItem::ItemIsFocusable, false); + } + + bool sceneEvent(QEvent *ev) + { + if (ev->type() == QEvent::TouchBegin) + ++touchBeginEventCount; + + return QGraphicsRectItem::sceneEvent(ev); + } + }; + + Testee *touchEventReceiver = new Testee; + QGraphicsItem *topMost = new QGraphicsRectItem(touchEventReceiver->boundingRect()); + + QGraphicsScene scene; + scene.addItem(topMost); + scene.addItem(touchEventReceiver); + + topMost->setAcceptTouchEvents(true); + topMost->setZValue(FLT_MAX); + topMost->setFlag(QGraphicsItem::ItemIsFocusable, false); + topMost->setFlag(flag, true); + + QGraphicsView view(&scene); + view.setSceneRect(touchEventReceiver->boundingRect()); + view.show(); + QTest::qWaitForWindowShown(&view); + + QCOMPARE(touchEventReceiver->touchBeginEventCount, 0); + + QTouchEvent::TouchPoint tp(0); + tp.setState(Qt::TouchPointPressed); + tp.setScenePos(view.sceneRect().center()); + tp.setLastScenePos(view.sceneRect().center()); + + QList touchPoints; + touchPoints << tp; + + sendMousePress(&scene, tp.scenePos()); + QTouchEvent touchBegin(QEvent::TouchBegin, QTouchEvent::TouchScreen, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); + + qApp->sendEvent(&scene, &touchBegin); + QCOMPARE(touchEventReceiver->touchBeginEventCount, expectedCount); +} + +void tst_QGraphicsItem::deviceCoordinateCache_simpleRotations() +{ + // Make sure we don't invalidate the cache when applying simple + // (90, 180, 270, 360) rotation transforms to the item. + QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 300, 200); + item->setBrush(Qt::red); + item->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + QGraphicsScene scene; + scene.setSceneRect(0, 0, 300, 200); + scene.addItem(item); + + MyGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + QGraphicsItemCache *itemCache = QGraphicsItemPrivate::get(item)->extraItemCache(); + QVERIFY(itemCache); + QPixmapCache::Key currentKey = itemCache->deviceData.value(view.viewport()).key; + + // Trigger an update and verify that the cache is unchanged. + QPixmapCache::Key oldKey = currentKey; + view.reset(); + view.viewport()->update(); + QTRY_VERIFY(view.repaints > 0); + currentKey = itemCache->deviceData.value(view.viewport()).key; + QCOMPARE(currentKey, oldKey); + + // Check 90, 180, 270 and 360 degree rotations. + for (int angle = 90; angle <= 360; angle += 90) { + // Rotate item and verify that the cache was invalidated. + oldKey = currentKey; + view.reset(); + QTransform transform; + transform.translate(150, 100); + transform.rotate(angle); + transform.translate(-150, -100); + item->setTransform(transform); + QTRY_VERIFY(view.repaints > 0); + currentKey = itemCache->deviceData.value(view.viewport()).key; + QVERIFY(currentKey != oldKey); + + // IMPORTANT PART: + // Trigger an update and verify that the cache is unchanged. + oldKey = currentKey; + view.reset(); + view.viewport()->update(); + QTRY_VERIFY(view.repaints > 0); + currentKey = itemCache->deviceData.value(view.viewport()).key; + QCOMPARE(currentKey, oldKey); + } + + // 45 degree rotation. + oldKey = currentKey; + view.reset(); + QTransform transform; + transform.translate(150, 100); + transform.rotate(45); + transform.translate(-150, -100); + item->setTransform(transform); + QTRY_VERIFY(view.repaints > 0); + currentKey = itemCache->deviceData.value(view.viewport()).key; + QVERIFY(currentKey != oldKey); + + // Trigger an update and verify that the cache was invalidated. + // We should always invalidate the cache for non-trivial transforms. + oldKey = currentKey; + view.reset(); + view.viewport()->update(); + QTRY_VERIFY(view.repaints > 0); + currentKey = itemCache->deviceData.value(view.viewport()).key; + QVERIFY(currentKey != oldKey); +} + +void tst_QGraphicsItem::QTBUG_5418_textItemSetDefaultColor() +{ + struct Item : public QGraphicsTextItem + { + int painted; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) + { + painted++; + QGraphicsTextItem::paint(painter, opt, wid); + } + }; + + Item *i = new Item; + i->painted = 0; + i->setPlainText("I AM A TROLL"); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + scene.addItem(i); + QApplication::processEvents(); + QTRY_VERIFY(i->painted); + QApplication::processEvents(); + + i->painted = 0; + QColor col(Qt::red); + i->setDefaultTextColor(col); + QApplication::processEvents(); + QTRY_COMPARE(i->painted, 1); //check that changing the color force an update + + i->painted = false; + QImage image(400, 200, QImage::Format_RGB32); + image.fill(0); + QPainter painter(&image); + scene.render(&painter); + painter.end(); + QCOMPARE(i->painted, 1); + + int numRedPixel = 0; + QRgb rgb = col.rgb(); + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + // Because of antialiasing we allow a certain range of errors here. + QRgb pixel = image.pixel(x, y); + if (qAbs((int)(pixel & 0xff) - (int)(rgb & 0xff)) + + qAbs((int)((pixel & 0xff00) >> 8) - (int)((rgb & 0xff00) >> 8)) + + qAbs((int)((pixel & 0xff0000) >> 16) - (int)((rgb & 0xff0000) >> 16)) <= 50) { + if (++numRedPixel >= 10) { + return; + } + } + } + } + QCOMPARE(numRedPixel, -1); //color not found, FAIL! + + i->painted = 0; + i->setDefaultTextColor(col); + QApplication::processEvents(); + QCOMPARE(i->painted, 0); //same color as before should not trigger an update (QTBUG-6242) +} + +void tst_QGraphicsItem::QTBUG_6738_missingUpdateWithSetParent() +{ + // In all 3 test cases below the reparented item should disappear + EventTester *parent = new EventTester; + EventTester *child = new EventTester(parent); + EventTester *child2 = new EventTester(parent); + EventTester *child3 = new EventTester(parent); + EventTester *child4 = new EventTester(parent); + + child->setPos(10, 10); + child2->setPos(20, 20); + child3->setPos(30, 30); + child4->setPos(40, 40); + + QGraphicsScene scene; + scene.addItem(parent); + + MyGraphicsView view(&scene); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.repaints > 0); + + // test case #1 + view.reset(); + child2->setVisible(false); + child2->setParentItem(child); + + QTRY_VERIFY(view.repaints == 1); + + // test case #2 + view.reset(); + child3->setOpacity(0.0); + child3->setParentItem(child); + + QTRY_VERIFY(view.repaints == 1); + + // test case #3 + view.reset(); + child4->setParentItem(child); + child4->setVisible(false); + + QTRY_VERIFY(view.repaints == 1); +} + +void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate() +{ + QGraphicsScene scene(0, 0, 200, 200); + MyGraphicsView view(&scene); + + EventTester *parentGreen = new EventTester(); + parentGreen->setGeometry(QRectF(20, 20, 100, 100)); + parentGreen->brush = Qt::green; + + EventTester *childYellow = new EventTester(parentGreen); + childYellow->setGeometry(QRectF(10, 10, 50, 50)); + childYellow->brush = Qt::yellow; + + scene.addItem(parentGreen); + + childYellow->setOpacity(0.0); + parentGreen->setOpacity(0.0); + + // set any of the flags below to trigger a fullUpdate to reproduce the bug: + // ItemIgnoresTransformations, ItemClipsChildrenToShape, ItemIsSelectable + parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations); + + if (PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + view.reset(); + + parentGreen->setOpacity(1.0); + + QTRY_COMPARE(view.repaints, 1); + + view.reset(); + childYellow->repaints = 0; + + childYellow->setOpacity(1.0); + + QTRY_COMPARE(view.repaints, 1); + QTRY_COMPARE(childYellow->repaints, 1); +} + +void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2() +{ + QGraphicsScene scene(0, 0, 200, 200); + MyGraphicsView view(&scene); + MyGraphicsView origView(&scene); + + EventTester *parentGreen = new EventTester(); + parentGreen->setGeometry(QRectF(20, 20, 100, 100)); + parentGreen->brush = Qt::green; + + EventTester *childYellow = new EventTester(parentGreen); + childYellow->setGeometry(QRectF(10, 10, 50, 50)); + childYellow->brush = Qt::yellow; + + scene.addItem(parentGreen); + + origView.show(); + QTest::qWaitForWindowShown(&origView); + origView.setGeometry(origView.width() + 20, 20, + origView.width(), origView.height()); + + parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations); + + origView.reset(); + childYellow->setOpacity(0.0); + + QTRY_COMPARE(origView.repaints, 1); + + view.show(); + + QTest::qWaitForWindowShown(&view); + view.reset(); + origView.reset(); + + childYellow->setOpacity(1.0); + + QTRY_COMPARE(origView.repaints, 1); + QTRY_COMPARE(view.repaints, 1); +} + +void tst_QGraphicsItem::QT_2649_focusScope() +{ + QGraphicsScene *scene = new QGraphicsScene; + + QGraphicsRectItem *subFocusItem = new QGraphicsRectItem; + subFocusItem->setFlags(QGraphicsItem::ItemIsFocusable); + subFocusItem->setFocus(); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + + QGraphicsRectItem *scope = new QGraphicsRectItem; + scope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope->setFocus(); + subFocusItem->setParentItem(scope); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + + QGraphicsRectItem *rootItem = new QGraphicsRectItem; + rootItem->setFlags(QGraphicsItem::ItemIsFocusable); + scope->setParentItem(rootItem); + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + + scene->addItem(rootItem); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(scene, &windowActivate); + scene->setFocus(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(subFocusItem->hasFocus()); + + scope->hide(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)0); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)0); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(!subFocusItem->hasFocus()); + + scope->show(); + + QCOMPARE(rootItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(scope->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(rootItem->focusScopeItem(), (QGraphicsItem *)0); + QCOMPARE(scope->focusScopeItem(), (QGraphicsItem *)subFocusItem); + QCOMPARE(subFocusItem->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(subFocusItem->hasFocus()); + + // This should not crash + scope->hide(); + delete scene; +} + +class MyGraphicsItemWithItemChange : public QGraphicsWidget +{ +public: + MyGraphicsItemWithItemChange(QGraphicsItem *parent = 0) : QGraphicsWidget(parent) + {} + + QVariant itemChange(GraphicsItemChange change, const QVariant &value) + { + if (change == QGraphicsItem::ItemSceneHasChanged) { + foreach (QGraphicsView *view, scene()->views()) { + //We trigger a sort of unindexed items in the BSP + view->sceneRect(); + } + } + return QGraphicsWidget::itemChange(change, value); + } +}; + +void tst_QGraphicsItem::sortItemsWhileAdding() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsWidget grandGrandParent; + grandGrandParent.resize(200, 200); + scene.addItem(&grandGrandParent); + QGraphicsWidget grandParent; + grandParent.resize(200, 200); + QGraphicsWidget parent(&grandParent); + parent.resize(200, 200); + MyGraphicsItemWithItemChange item(&parent); + grandParent.setParentItem(&grandGrandParent); +} + +void tst_QGraphicsItem::doNotMarkFullUpdateIfNotInScene() +{ + struct Item : public QGraphicsTextItem + { + int painted; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *wid) + { + painted++; + QGraphicsTextItem::paint(painter, opt, wid); + } + }; + QGraphicsScene scene; + MyGraphicsView view(&scene); + Item *item = new Item; + item->painted = 0; + item->setPlainText("Grandparent"); + Item *item2 = new Item; + item2->setPlainText("parent"); + item2->painted = 0; + Item *item3 = new Item; + item3->setPlainText("child"); + item3->painted = 0; + QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect; + effect->setOpacity(0.5); + item2->setGraphicsEffect(effect); + item3->setParentItem(item2); + item2->setParentItem(item); + scene.addItem(item); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(view.repaints, 1); + QTRY_COMPARE(item->painted, 1); + QTRY_COMPARE(item2->painted, 1); + QTRY_COMPARE(item3->painted, 1); + item2->update(); + QApplication::processEvents(); + QTRY_COMPARE(item->painted, 2); + QTRY_COMPARE(item2->painted, 2); + QTRY_COMPARE(item3->painted, 2); + item2->update(); + QApplication::processEvents(); + QTRY_COMPARE(item->painted, 3); + QTRY_COMPARE(item2->painted, 3); + QTRY_COMPARE(item3->painted, 3); +} + +void tst_QGraphicsItem::itemDiesDuringDraggingOperation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100)); + item->setFlag(QGraphicsItem::ItemIsMovable); + item->setAcceptDrops(true); + scene.addItem(item); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter); + dragEnter.setScenePos(item->boundingRect().center()); + QApplication::sendEvent(&scene, &dragEnter); + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + event.setScenePos(item->boundingRect().center()); + QApplication::sendEvent(&scene, &event); + QVERIFY(QGraphicsScenePrivate::get(&scene)->dragDropItem == item); + delete item; + QVERIFY(QGraphicsScenePrivate::get(&scene)->dragDropItem == 0); +} + +void tst_QGraphicsItem::QTBUG_12112_focusItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsRectItem *item1 = new QGraphicsRectItem(0, 0, 20, 20); + item1->setFlag(QGraphicsItem::ItemIsFocusable); + QGraphicsRectItem *item2 = new QGraphicsRectItem(20, 20, 20, 20); + item2->setFlag(QGraphicsItem::ItemIsFocusable); + item1->setFocus(); + scene.addItem(item2); + scene.addItem(item1); + + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), (QWidget *)&view); + + QVERIFY(item1->focusItem()); + QVERIFY(!item2->focusItem()); + + item2->setFocus(); + QVERIFY(!item1->focusItem()); + QVERIFY(item2->focusItem()); +} + +void tst_QGraphicsItem::QTBUG_13473_sceneposchange() +{ + ScenePosChangeTester* parent = new ScenePosChangeTester; + ScenePosChangeTester* child = new ScenePosChangeTester(parent); + + // parent's disabled ItemSendsGeometryChanges flag must not affect + // child's scene pos change notifications + parent->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); + child->setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + + QGraphicsScene scene; + scene.addItem(parent); + + // ignore uninteresting changes + parent->clear(); + child->clear(); + + // move + parent->moveBy(1.0, 1.0); + QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 1); + + // transform + parent->setTransform(QTransform::fromScale(0.5, 0.5)); + QCOMPARE(child->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 2); +} + +class MyGraphicsWidget : public QGraphicsWidget { +Q_OBJECT +public: + MyGraphicsWidget() + : QGraphicsWidget(0) + { + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical); + QLatin1String wiseWords("AZ BUKI VEDI"); + QString sentence(wiseWords); + QStringList words = sentence.split(QLatin1Char(' '), QString::SkipEmptyParts); + for (int i = 0; i < words.count(); ++i) { + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this); + QLabel *label = new QLabel(words.at(i)); + proxy->setWidget(label); + proxy->setFocusPolicy(Qt::StrongFocus); + proxy->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + if (i%2 == 0) + proxy->setVisible(false); + proxy->setFocus(); + lay->addItem(proxy); + } + setLayout(lay); + } + +}; + +class MyWidgetWindow : public QGraphicsWidget +{ +public: + MyWidgetWindow() + : QGraphicsWidget(0, Qt::Window) + { + QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical); + MyGraphicsWidget *widget = new MyGraphicsWidget(); + lay->addItem(widget); + setLayout(lay); + } +}; + +void tst_QGraphicsItem::QTBUG_16374_crashInDestructor() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + MyWidgetWindow win; + scene.addItem(&win); + + view.show(); + QTest::qWaitForWindowShown(&view); +} + +void tst_QGraphicsItem::QTBUG_20699_focusScopeCrash() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsPixmapItem fs; + fs.setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable); + scene.addItem(&fs); + QGraphicsPixmapItem* fs2 = new QGraphicsPixmapItem(&fs); + fs2->setFlags(QGraphicsItem::ItemIsFocusScope | QGraphicsItem::ItemIsFocusable); + QGraphicsPixmapItem* fi2 = new QGraphicsPixmapItem(&fs); + fi2->setFlags(QGraphicsItem::ItemIsFocusable); + QGraphicsPixmapItem* fi = new QGraphicsPixmapItem(fs2); + fi->setFlags(QGraphicsItem::ItemIsFocusable); + fs.setFocus(); + fi->setFocus(); + + view.show(); + QTest::qWaitForWindowShown(&view); + + fi->setParentItem(fi2); + fi->setFocus(); + fs.setFocus(); + fi->setParentItem(fs2); + fi->setFocus(); + fs2->setFocus(); + fs.setFocus(); + fi->setParentItem(fi2); + fi->setFocus(); + fs.setFocus(); +} + +QTEST_MAIN(tst_QGraphicsItem) +#include "tst_qgraphicsitem.moc" -- cgit v1.2.3