/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtTest/QtTest>
#include <QtGui>
#include <QtWidgets>
#include <private/qgraphicsproxywidget_p.h>
#include <private/qlayoutengine_p.h>    // qSmartMin functions...

#include "../../../qtest-config.h"

static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton)
{
    QMouseEvent event(QEvent::MouseMove, point, widget->mapToGlobal(point), button, button, 0);
    QApplication::sendEvent(widget, &event);
}

/*
    Notes:

    1) The proxy and the widget geometries are linked.
       proxy resize => widget resize => stop (no livelock)
       widget resize => proxy resize => stop (no livelock)

    2) As far as possible, the properties are linked.
       proxy enable => widget enable => stop
       widget disabled => proxy disabled => stop

    3) Windowed state is linked
       Windowed proxy state => windowed widget state => stop
       Windowed widget state => windowed proxy state => stop
*/

class EventSpy : public QObject
{
public:
    EventSpy(QObject *receiver)
    {
        receiver->installEventFilter(this);
    }

    QMap<QEvent::Type, int> counts;

protected:
    bool eventFilter(QObject *, QEvent *event)
    {
        ++counts[event->type()];
        return false;
    }
};

class tst_QGraphicsProxyWidget : public QObject
{
    Q_OBJECT

public slots:
    void initTestCase();
    void cleanupTestCase();
    void init();
    void cleanup();

private slots:
    void qgraphicsproxywidget_data();
    void qgraphicsproxywidget();
    void paint();
    void paint_2();
    void setWidget_data();
    void setWidget();
    void eventFilter_data();
    void eventFilter();
    void focusInEvent_data();
    void focusInEvent();
    void focusInEventNoWidget();
    void focusNextPrevChild_data();
    void focusNextPrevChild();
    void focusOutEvent_data();
    void focusOutEvent();
#ifndef QTEST_NO_CURSOR
    void hoverEnterLeaveEvent_data();
    void hoverEnterLeaveEvent();
#endif
    void hoverMoveEvent_data();
    void hoverMoveEvent();
    void keyPressEvent_data();
    void keyPressEvent();
    void keyReleaseEvent_data();
    void keyReleaseEvent();
    void mouseDoubleClickEvent_data();
    void mouseDoubleClickEvent();
    void mousePressReleaseEvent_data();
    void mousePressReleaseEvent();
    void resizeEvent_data();
    void resizeEvent();
    void paintEvent();
    void wheelEvent();
    void sizeHint_data();
    void sizeHint();
    void sizePolicy();
    void minimumSize();
    void maximumSize();
    void scrollUpdate();
    void setWidget_simple();
    void setWidget_ownership();
    void resize_simple_data();
    void resize_simple();
    void symmetricMove();
    void symmetricResize();
    void symmetricEnabled();
    void symmetricVisible();
    void tabFocus_simpleWidget();
    void tabFocus_simpleTwoWidgets();
    void tabFocus_complexWidget();
    void tabFocus_complexTwoWidgets();
    void setFocus_simpleWidget();
    void setFocus_simpleTwoWidgets();
    void setFocus_complexTwoWidgets();
    void popup_basic();
    void popup_subwidget();
#ifndef QTEST_NO_CURSOR
    void changingCursor_basic();
#endif
    void tooltip_basic();
    void childPos_data();
    void childPos();
    void autoShow();
    void windowOpacity();
    void stylePropagation();
    void palettePropagation();
    void fontPropagation();
    void dontCrashWhenDie();
    void createProxyForChildWidget();
    void actionsContextMenu();
    void actionsContextMenu_data();
    void deleteProxyForChildWidget();
    void bypassGraphicsProxyWidget_data();
    void bypassGraphicsProxyWidget();
    void dragDrop();
    void windowFlags_data();
    void windowFlags();
    void comboboxWindowFlags();
    void updateAndDelete();
    void inputMethod();
    void clickFocus();
    void windowFrameMargins();
    void QTBUG_6986_sendMouseEventToAlienWidget();
    void mapToGlobal();
    void mapToGlobalWithoutScene();
    void QTBUG_43780_visibility();
};

// Subclass that exposes the protected functions.
class SubQGraphicsProxyWidget : public QGraphicsProxyWidget
{

public:
    SubQGraphicsProxyWidget(QGraphicsItem *parent = 0) : QGraphicsProxyWidget(parent),
    paintCount(0), keyPress(0), focusOut(0)
        {}

    bool call_eventFilter(QObject* object, QEvent* event)
        { return SubQGraphicsProxyWidget::eventFilter(object, event); }

    void call_focusInEvent(QFocusEvent* event)
        { return SubQGraphicsProxyWidget::focusInEvent(event); }

    bool call_focusNextPrevChild(bool next)
        { return SubQGraphicsProxyWidget::focusNextPrevChild(next); }

    void call_focusOutEvent(QFocusEvent* event)
        { return SubQGraphicsProxyWidget::focusOutEvent(event); }

    void call_hideEvent(QHideEvent* event)
        { return SubQGraphicsProxyWidget::hideEvent(event); }

    void call_hoverEnterEvent(QGraphicsSceneHoverEvent* event)
        { return SubQGraphicsProxyWidget::hoverEnterEvent(event); }

    void call_hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
        { return SubQGraphicsProxyWidget::hoverLeaveEvent(event); }

    void call_hoverMoveEvent(QGraphicsSceneHoverEvent* event)
        { return SubQGraphicsProxyWidget::hoverMoveEvent(event); }

    void call_keyPressEvent(QKeyEvent* event)
        { return SubQGraphicsProxyWidget::keyPressEvent(event); }

    void call_keyReleaseEvent(QKeyEvent* event)
        { return SubQGraphicsProxyWidget::keyReleaseEvent(event); }

    void call_mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
        { return SubQGraphicsProxyWidget::mouseDoubleClickEvent(event); }

    void call_mouseMoveEvent(QGraphicsSceneMouseEvent* event)
        { return SubQGraphicsProxyWidget::mouseMoveEvent(event); }

    void call_mousePressEvent(QGraphicsSceneMouseEvent* event)
        { return SubQGraphicsProxyWidget::mousePressEvent(event); }

    void call_mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
        { return SubQGraphicsProxyWidget::mouseReleaseEvent(event); }

    void call_resizeEvent(QGraphicsSceneResizeEvent* event)
        { return SubQGraphicsProxyWidget::resizeEvent(event); }

    QSizeF call_sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const
        { return SubQGraphicsProxyWidget::sizeHint(which, constraint); }

    void call_showEvent(QShowEvent* event)
        { return SubQGraphicsProxyWidget::showEvent(event); }

    void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0)    {
        paintCount++;
        QGraphicsProxyWidget::paint(painter, option, widget);
    }

    void focusOutEvent(QFocusEvent *event)
    {
        focusOut++;
        QGraphicsProxyWidget::focusOutEvent(event);
    }

    bool eventFilter(QObject *object, QEvent *event) {
        if (event->type() == QEvent::KeyPress && object == widget())
            keyPress++;
        return QGraphicsProxyWidget::eventFilter(object, event);
    }
    int paintCount;
    int keyPress;
    int focusOut;
};

class WheelWidget : public QWidget
{
public:
    WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); }

    virtual void wheelEvent(QWheelEvent *event) { event->accept(); wheelEventCalled = true; }

    bool wheelEventCalled;
};

// This will be called before the first test function is executed.
// It is only called once.
void tst_QGraphicsProxyWidget::initTestCase()
{
#ifdef Q_OS_WINCE //disable magic for WindowsCE
    qApp->setAutoMaximizeThreshold(-1);
#endif
    // Disable menu animations to prevent the alpha widget from getting in the way
    // in actionsContextMenu().
    QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false);
    // Disable combo for QTBUG_43780_visibility()/Windows Vista.
    QApplication::setEffectEnabled(Qt::UI_AnimateCombo, false);
}

// This will be called after the last test function is executed.
// It is only called once.
void tst_QGraphicsProxyWidget::cleanupTestCase()
{
}

// This will be called before each test function is executed.
void tst_QGraphicsProxyWidget::init()
{
}

// This will be called after every test function.
void tst_QGraphicsProxyWidget::cleanup()
{
    QVERIFY(QApplication::topLevelWidgets().isEmpty());
}

void tst_QGraphicsProxyWidget::qgraphicsproxywidget_data()
{
}

void tst_QGraphicsProxyWidget::qgraphicsproxywidget()
{
    SubQGraphicsProxyWidget proxy;
    proxy.paint(0, 0, 0);
    proxy.setWidget(0);
    QVERIFY(proxy.type() == QGraphicsProxyWidget::Type);
    QVERIFY(!proxy.widget());
    QEvent event(QEvent::None);
    proxy.call_eventFilter(0, &event);
    QFocusEvent focusEvent(QEvent::FocusIn);
    focusEvent.ignore();
    proxy.call_focusInEvent(&focusEvent);
    QCOMPARE(focusEvent.isAccepted(), false);
    QCOMPARE(proxy.call_focusNextPrevChild(false), false);
    QCOMPARE(proxy.call_focusNextPrevChild(true), false);
    proxy.call_focusOutEvent(&focusEvent);
    QHideEvent hideEvent;
    proxy.call_hideEvent(&hideEvent);
    QGraphicsSceneHoverEvent hoverEvent;
    proxy.call_hoverEnterEvent(&hoverEvent);
    proxy.call_hoverLeaveEvent(&hoverEvent);
    proxy.call_hoverMoveEvent(&hoverEvent);
    QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier);
    proxy.call_keyPressEvent(&keyEvent);
    proxy.call_keyReleaseEvent(&keyEvent);
    QGraphicsSceneMouseEvent mouseEvent;
    proxy.call_mouseDoubleClickEvent(&mouseEvent);
    proxy.call_mouseMoveEvent(&mouseEvent);
    proxy.call_mousePressEvent(&mouseEvent);
    proxy.call_mouseReleaseEvent(&mouseEvent);
    QGraphicsSceneResizeEvent resizeEvent;
    proxy.call_resizeEvent(&resizeEvent);
    QShowEvent showEvent;
    proxy.call_showEvent(&showEvent);
    proxy.call_sizeHint(Qt::PreferredSize, QSizeF());
}

// public void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget)
void tst_QGraphicsProxyWidget::paint()
{
    SubQGraphicsProxyWidget proxy;
    proxy.paint(0, 0, 0);
}

class MyProxyWidget : public QGraphicsProxyWidget
{
public:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter.
        painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform
                                | QPainter::NonCosmeticDefaultPen | QPainter::TextAntialiasing);
        const QPainter::RenderHints oldRenderHints = painter->renderHints();
        QGraphicsProxyWidget::paint(painter, option, widget);
        QCOMPARE(painter->renderHints(), oldRenderHints);
    }
};

void tst_QGraphicsProxyWidget::paint_2()
{
    MyProxyWidget *proxyWidget = new MyProxyWidget;
    proxyWidget->setWidget(new QLineEdit);

    QGraphicsScene scene;
    scene.addItem(proxyWidget);
    scene.setSceneRect(scene.itemsBoundingRect());

    // Trigger repaint.
    QPixmap pixmap(scene.sceneRect().toRect().size());
    QPainter painter(&pixmap);
    scene.render(&painter);
}

void tst_QGraphicsProxyWidget::setWidget_data()
{
    QTest::addColumn<bool>("widgetExists");
    QTest::addColumn<bool>("insertWidget");
    QTest::addColumn<bool>("hasParent");
    QTest::addColumn<bool>("proxyHasParent");

    QTest::newRow("setWidget(0)") << false << false << false << false;
    QTest::newRow("setWidget(widget)") << false << true << false << false;
    QTest::newRow("setWidget(widgetWParent)") << false << true << true << false;
    QTest::newRow("setWidget(1), setWidget(0)") << true << false << false << false;
    QTest::newRow("setWidget(1), setWidget(widget)") << true << true << false << false;
    QTest::newRow("setWidget(1), setWidget(widgetWParent)") << true << true << true << false;
    QTest::newRow("p setWidget(0)") << false << false << false << true;
    QTest::newRow("p setWidget(widget)") << false << true << false << true;
    QTest::newRow("p setWidget(widgetWParent)") << false << true << true << true;
    QTest::newRow("p setWidget(1), setWidget(0)") << true << false << false << true;
    QTest::newRow("p setWidget(1), setWidget(widget)") << true << true << false << true;
    QTest::newRow("p setWidget(1), setWidget(widgetWParent)") << true << true << true << true;
}

// public void setWidget(QWidget* widget)
void tst_QGraphicsProxyWidget::setWidget()
{
    QFETCH(bool, widgetExists);
    QFETCH(bool, insertWidget);
    QFETCH(bool, hasParent);
    QFETCH(bool, proxyHasParent);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    QPointer<SubQGraphicsProxyWidget> proxy = new SubQGraphicsProxyWidget;
    SubQGraphicsProxyWidget parentProxy;
    scene.addItem(proxy);
    scene.addItem(&parentProxy);
    if (proxyHasParent)
        proxy->setParent(&parentProxy);
    QPointer<QWidget> existingSubWidget = new QWidget;
    proxy->setVisible(false);
    proxy->setEnabled(false);

    if (widgetExists) {
        existingSubWidget->setAttribute(Qt::WA_QuitOnClose, true);
        proxy->setWidget(existingSubWidget);
    }

    QWidget *widget = new QWidget;
#ifndef QTEST_NO_CURSOR
    widget->setCursor(Qt::IBeamCursor);
#endif
    widget->setPalette(QPalette(Qt::magenta));
    widget->setLayoutDirection(Qt::RightToLeft);
    widget->setStyle(QStyleFactory::create(QLatin1String("Fusion")));
    widget->setFont(QFont("Times"));
    widget->setVisible(true);
    QApplication::setActiveWindow(widget);
    widget->activateWindow();
    widget->setEnabled(true);
    widget->resize(325, 241);
    widget->setMinimumSize(100, 200);
    widget->setMaximumSize(1000, 2000);
    widget->setFocusPolicy(Qt::TabFocus);
    widget->setContentsMargins(10, 29, 19, 81);
    widget->setFocus(Qt::TabFocusReason);
    widget->setAcceptDrops(true);
    QTRY_VERIFY(widget->hasFocus());
    QVERIFY(widget->isActiveWindow());

    QWidget parentWidget;
    if (hasParent)
        widget->setParent(&parentWidget);

    QWidget *subWidget = insertWidget ? widget : 0;
    bool shouldBeInsertable = !hasParent && subWidget;
    if (shouldBeInsertable)
        subWidget->setAttribute(Qt::WA_QuitOnClose, true);

    proxy->setWidget(subWidget);

    if (shouldBeInsertable) {
        QCOMPARE(proxy->widget(), subWidget);
        QVERIFY(subWidget->testAttribute(Qt::WA_DontShowOnScreen));
        QVERIFY(!subWidget->testAttribute(Qt::WA_QuitOnClose));
        QCOMPARE(proxy->acceptHoverEvents(), true);
#ifndef QTEST_NO_CURSOR
        QVERIFY(proxy->hasCursor());

        // These should match
        QCOMPARE(proxy->cursor().shape(), widget->cursor().shape());
#endif
        //###QCOMPARE(proxy->palette(), widget->palette());
        QCOMPARE(proxy->layoutDirection(), widget->layoutDirection());
        QCOMPARE(proxy->style(), widget->style());
        QCOMPARE(proxy->isVisible(), widget->isVisible());
        QCOMPARE(proxy->isEnabled(), widget->isEnabled());
        QVERIFY(proxy->flags() & QGraphicsItem::ItemIsFocusable);
        QCOMPARE(proxy->effectiveSizeHint(Qt::MinimumSize).toSize(),
            qSmartMinSize(widget));
        QCOMPARE(proxy->minimumSize().toSize(),
                 qSmartMinSize(widget) );
        QCOMPARE(proxy->maximumSize().toSize(), QSize(1000, 2000));
        QCOMPARE(proxy->effectiveSizeHint(Qt::PreferredSize).toSize(),
                qSmartMinSize(widget));
        QCOMPARE(proxy->size().toSize(), widget->size());
        QCOMPARE(proxy->rect().toRect(), widget->rect());
        QCOMPARE(proxy->focusPolicy(), Qt::WheelFocus);
        QVERIFY(proxy->acceptDrops());
        QCOMPARE(proxy->acceptsHoverEvents(), true); // to get widget enter events
        int left, top, right, bottom;
        widget->getContentsMargins(&left, &top, &right, &bottom);
        qreal rleft, rtop, rright, rbottom;
        proxy->getContentsMargins(&rleft, &rtop, &rright, &rbottom);
        QCOMPARE((qreal)left, rleft);
        QCOMPARE((qreal)top, rtop);
        QCOMPARE((qreal)right, rright);
        QCOMPARE((qreal)bottom, rbottom);
    } else {
        // proxy shouldn't mess with the widget if it can't insert it.
        QCOMPARE(proxy->widget(), (QWidget*)0);
        QCOMPARE(proxy->acceptHoverEvents(), false);
        if (subWidget) {
            QVERIFY(!subWidget->testAttribute(Qt::WA_DontShowOnScreen));
            QVERIFY(subWidget->testAttribute(Qt::WA_QuitOnClose));
            // reset
            subWidget->setAttribute(Qt::WA_QuitOnClose, false);
        }
    }

    if (widgetExists) {
        QCOMPARE(existingSubWidget->parent(), static_cast<QObject*>(0));
        QVERIFY(!existingSubWidget->testAttribute(Qt::WA_DontShowOnScreen));
        QVERIFY(!existingSubWidget->testAttribute(Qt::WA_QuitOnClose));
    }

    if (hasParent)
        widget->setParent(0);

    delete widget;
    if (shouldBeInsertable)
        QVERIFY(!proxy);
    delete existingSubWidget;
    if (!shouldBeInsertable) {
        QVERIFY(proxy);
        delete proxy;
    }
    QVERIFY(!proxy);
}

Q_DECLARE_METATYPE(QEvent::Type)
void tst_QGraphicsProxyWidget::eventFilter_data()
{
    QTest::addColumn<QEvent::Type>("eventType");
    QTest::addColumn<bool>("fromObject"); // big grin evil
    QTest::newRow("none") << QEvent::None << false;
    for (int i = 0; i < 2; ++i) {
        bool fromObject = (i == 0);
        QTest::newRow(QString("resize %1").arg(fromObject).toLatin1()) << QEvent::Resize << fromObject;
        QTest::newRow(QString("move %1").arg(fromObject).toLatin1()) << QEvent::Move << fromObject;
        QTest::newRow(QString("hide %1").arg(fromObject).toLatin1()) << QEvent::Hide << fromObject;
        QTest::newRow(QString("show %1").arg(fromObject).toLatin1()) << QEvent::Show << fromObject;
        QTest::newRow(QString("enabled %1").arg(fromObject).toLatin1()) << QEvent::EnabledChange << fromObject;
        QTest::newRow(QString("focusIn %1").arg(fromObject).toLatin1()) << QEvent::FocusIn << fromObject;
        QTest::newRow(QString("focusOut %1").arg(fromObject).toLatin1()) << QEvent::FocusOut << fromObject;
        QTest::newRow(QString("keyPress %1").arg(fromObject).toLatin1()) << QEvent::KeyPress << fromObject;
    }
}

// protected bool eventFilter(QObject* object, QEvent* event)
void tst_QGraphicsProxyWidget::eventFilter()
{
    QFETCH(QEvent::Type, eventType);
    QFETCH(bool, fromObject);

    QGraphicsScene scene;
    QEvent windowActivate(QEvent::WindowActivate);
    qApp->sendEvent(&scene, &windowActivate);

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    scene.addItem(proxy);

    QWidget *widget = new QWidget(0, Qt::FramelessWindowHint);
    widget->setFocusPolicy(Qt::TabFocus);
    widget->resize(10, 10);
    widget->show();
    proxy->setWidget(widget);
    proxy->show();

    // mirror whatever is happening to the widget
    // don't get in a loop
    switch (eventType) {
    case QEvent::None: {
        QEvent event(QEvent::None);
        proxy->call_eventFilter(widget, &event);
        break;
                       }
    case QEvent::Resize: {
        QSize newSize = QSize(100, 100);
        if (fromObject) {
            widget->resize(newSize);
        } else {
            proxy->resize(newSize);
        }
        QCOMPARE(proxy->size().toSize(), newSize);
        QCOMPARE(widget->size(), newSize);
        break;
                         }
    case QEvent::Move: {
        QPoint newPoint = QPoint(100, 100);
        if (fromObject) {
            widget->move(newPoint);
        } else {
            proxy->setPos(newPoint);
        }
        QCOMPARE(proxy->pos().toPoint(), newPoint);
        QCOMPARE(widget->pos(), newPoint);
        break;
                         }
    case QEvent::Hide: {
        // A hide event can only come from a widget
        if (fromObject) {
            widget->setFocus(Qt::TabFocusReason);
            widget->hide();
        } else {
            QHideEvent event;
            proxy->call_eventFilter(widget, &event);
        }
        QCOMPARE(proxy->isVisible(), false);
        break;
                         }
    case QEvent::Show: {
        // A show event can either come from a widget or somewhere else
        widget->hide();
        if (fromObject) {
            widget->show();
        } else {
            QShowEvent event;
            proxy->call_eventFilter(widget, &event);
        }
        QCOMPARE(proxy->isVisible(), true);
        break;
                         }
    case QEvent::EnabledChange: {
        widget->setEnabled(false);
        proxy->setEnabled(false);
        if (fromObject) {
            widget->setEnabled(true);
            QCOMPARE(proxy->isEnabled(), true);
            widget->setEnabled(false);
            QCOMPARE(proxy->isEnabled(), false);
        } else {
            QEvent event(QEvent::EnabledChange);
            proxy->call_eventFilter(widget, &event);
            // match the widget not the event
            QCOMPARE(proxy->isEnabled(), false);
        }
        break;
                         }
    case QEvent::FocusIn: {
        if (fromObject) {
            widget->setFocus(Qt::TabFocusReason);
            QVERIFY(proxy->hasFocus());
        }
        break;
                         }
    case QEvent::FocusOut: {
        widget->setFocus(Qt::TabFocusReason);
        QVERIFY(proxy->hasFocus());
        QVERIFY(widget->hasFocus());
        if (fromObject) {
            widget->clearFocus();
            QVERIFY(!proxy->hasFocus());
        }
        break;
                         }
    case QEvent::KeyPress: {
        if (fromObject) {
            QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier);
        } else {
            QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
            proxy->call_eventFilter(widget, &event);
        }
        QCOMPARE(proxy->keyPress, 1);
        break;
                         }
    default:
        break;
    }
}

void tst_QGraphicsProxyWidget::focusInEvent_data()
{
    QTest::addColumn<bool>("widgetHasFocus");
    QTest::addColumn<bool>("widgetCanHaveFocus");
    QTest::newRow("no focus, can't get") << false << false;
    QTest::newRow("no focus, can get") << false << true;
    QTest::newRow("has focus, can't get") << true << false;
    QTest::newRow("has focus, can get") << true << true;
    // ### add test for widget having a focusNextPrevChild
}

// protected void focusInEvent(QFocusEvent* event)
void tst_QGraphicsProxyWidget::focusInEvent()
{
#ifdef Q_OS_WIN
    // Fails on Windows due QPlatformWindow::isActive() check required for embedded native widgets.
    // Since the test is apparently broken anyway, just skip it.
    QSKIP("Broken test.");
#endif

    // ### This test is just plain old broken
    QFETCH(bool, widgetHasFocus);
    QFETCH(bool, widgetCanHaveFocus);

    QGraphicsScene scene;
    QEvent windowActivate(QEvent::WindowActivate);
    qApp->sendEvent(&scene, &windowActivate);

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setEnabled(true);
    scene.addItem(proxy);
    proxy->setVisible(true);

    QWidget *widget = new QWidget;
    widget->resize(100, 100);
    if (widgetCanHaveFocus)
        widget->setFocusPolicy(Qt::WheelFocus);
    widget->show();

    if (widgetHasFocus)
        widget->setFocus(Qt::TabFocusReason);

    proxy->setWidget(widget);
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this

    // ### This test is just plain old broken - sending a focus in event
    // does not cause items to gain input focus. The widget has focus
    // because the proxy has focus, not because it got this event.

    QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
    event.ignore();
    proxy->call_focusInEvent(&event);
    QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus);
}

void tst_QGraphicsProxyWidget::focusInEventNoWidget()
{
    QGraphicsView view;
    QGraphicsScene scene(&view);
    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setEnabled(true);
    scene.addItem(proxy);
    proxy->setVisible(true);
    view.show();

    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this
    QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
    event.ignore();
    //should not crash
    proxy->call_focusInEvent(&event);
}

void tst_QGraphicsProxyWidget::focusNextPrevChild_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::addColumn<bool>("hasScene");
    QTest::addColumn<bool>("next");
    QTest::addColumn<bool>("focusNextPrevChild");

    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 2; ++j) {
            for (int k = 0; k < 2; ++k) {
                bool next = (i == 0);
                bool hasWidget = (j == 0);
                bool hasScene = (k == 0);
                bool result = hasScene && hasWidget;
                QString name = QString("Forward: %1, hasWidget: %2, hasScene: %3, result: %4").arg(next).arg(hasWidget).arg(hasScene).arg(result);
                QTest::newRow(name.toLatin1()) << hasWidget << hasScene << next << result;
            }
        }
    }
}

// protected bool focusNextPrevChild(bool next)
void tst_QGraphicsProxyWidget::focusNextPrevChild()
{
    QFETCH(bool, next);
    QFETCH(bool, focusNextPrevChild);
    QFETCH(bool, hasWidget);
    QFETCH(bool, hasScene);

    // If a widget has its own focusNextPrevChild we need to respect it
    // otherwise respect the scene
    // Respect the widget over the scene!

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;

    QLabel *widget = new QLabel;
    // I can't believe designer adds this much junk!
    widget->setText("<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\"> p, li { white-space: pre-wrap; } </style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\"> <p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.slashdot.org\"><span style=\" text-decoration: underline; color:#0000ff;\">old</span></a> foo <a href=\"http://www.reddit.org\"><span style=\" text-decoration: underline; color:#0000ff;\">new</span></a></p></body></html>");
    widget->setTextInteractionFlags(Qt::TextBrowserInteraction);

    if (hasWidget)
        proxy->setWidget(widget);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    if (hasScene) {
        scene.addItem(proxy);
        proxy->show();

        // widget should take precedence over scene so make scene.focusNextPrevChild return false
        // so we know that a true can only come from the widget
        if (!(hasWidget && hasScene)) {
            QGraphicsTextItem *item = new QGraphicsTextItem("Foo");
            item->setTextInteractionFlags(Qt::TextBrowserInteraction);
            scene.addItem(item);
            item->setPos(50, 40);
        }
        scene.setFocusItem(proxy);
        QVERIFY(proxy->hasFocus());
    }

    QCOMPARE(proxy->call_focusNextPrevChild(next), focusNextPrevChild);

    if (!hasScene)
        delete proxy;
    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::focusOutEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::addColumn<bool>("call");
    QTest::newRow("no widget, focus to other widget") << false << false;
    QTest::newRow("no widget, focusOutCalled") << false << true;
    QTest::newRow("widget, focus to other widget") << true << false;
    QTest::newRow("widget, focusOutCalled") << true << true;
}

// protected void focusOutEvent(QFocusEvent* event)
void tst_QGraphicsProxyWidget::focusOutEvent()
{
    QFETCH(bool, hasWidget);
    QFETCH(bool, call);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    scene.addItem(proxy);
    view.show();
    QApplication::setActiveWindow(&view);
    view.activateWindow();
    view.setFocus();
    QTest::qWaitForWindowActive(&view);
    QTRY_VERIFY(view.isVisible());
    QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view);

    QScopedPointer<QWidget> widgetGuard(new QWidget);
    QWidget *widget = widgetGuard.data();
    widget->setFocusPolicy(Qt::WheelFocus);
    if (hasWidget)
        proxy->setWidget(widgetGuard.take());
    proxy->show();
    proxy->setFocus();
    QVERIFY(proxy->hasFocus());
    QEXPECT_FAIL("widget, focus to other widget", "Widget should have focus but doesn't", Continue);
    QEXPECT_FAIL("widget, focusOutCalled", "Widget should have focus but doesn't", Continue);
    QCOMPARE(widget->hasFocus(), hasWidget);

    if (!call) {
        QWidget *other = new QLineEdit(&view);
        other->show();
        QApplication::processEvents();
        QTRY_VERIFY(other->isVisible());
        other->setFocus();
        QTRY_VERIFY(other->hasFocus());
        qApp->processEvents();
        QTRY_COMPARE(proxy->hasFocus(), false);
        QVERIFY(proxy->focusOut);
        QCOMPARE(widget->hasFocus(), false);
    } else {
        {
            /*
            ### Test doesn't make sense
            QFocusEvent focusEvent(QEvent::FocusOut);
            proxy->call_focusOutEvent(&focusEvent);
            QCOMPARE(focusEvent.isAccepted(), hasWidget);
            qApp->processEvents();
            QCOMPARE(proxy->paintCount, hasWidget ? 1 : 0);
            */
        }
        {
            /*
            ### Test doesn't make sense
            proxy->setFlag(QGraphicsItem::ItemIsFocusable, false);
            QFocusEvent focusEvent(QEvent::FocusOut);
            proxy->call_focusOutEvent(&focusEvent);
            QCOMPARE(focusEvent.isAccepted(), hasWidget);
            qApp->processEvents();
            QCOMPARE(proxy->paintCount, 0);
            */
        }
    }
}

class EventLogger : public QWidget
{
public:
    EventLogger() : QWidget(), enterCount(0), leaveCount(0), moveCount(0),
        hoverEnter(0), hoverLeave(0), hoverMove(0)
    {
        installEventFilter(this);
    }

    void enterEvent(QEvent *event)
    {
        enterCount++;
        QWidget::enterEvent(event);
    }

    void leaveEvent(QEvent *event )
    {
        leaveCount++;
        QWidget::leaveEvent(event);
    }

    void mouseMoveEvent(QMouseEvent *event)
    {
        event->setAccepted(true);
        moveCount++;
        QWidget::mouseMoveEvent(event);
    }

    int enterCount;
    int leaveCount;
    int moveCount;

    int hoverEnter;
    int hoverLeave;
    int hoverMove;
protected:
    bool eventFilter(QObject *object, QEvent *event)
    {
        switch (event->type()) {
        case QEvent::HoverEnter:
             hoverEnter++;
             break;
        case QEvent::HoverLeave:
             hoverLeave++;
             break;
        case QEvent::HoverMove:
             hoverMove++;
             break;
        default:
             break;
        }
        return QWidget::eventFilter(object, event);
    }
};

#ifndef QTEST_NO_CURSOR
void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::addColumn<bool>("hoverEnabled");
    QTest::newRow("widget, no hover") << true << false;
    QTest::newRow("no widget, no hover") << false << false;
    QTest::newRow("widget, hover") << true << true;
    QTest::newRow("no widget, hover") << false << true;
}

// protected void hoverEnterEvent(QGraphicsSceneHoverEvent* event)
void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent()
{
    QFETCH(bool, hasWidget);
    QFETCH(bool, hoverEnabled);

    // proxy should translate this into events that the widget would expect

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    //do not let the window manager move the window while we are moving the mouse on it
    view.setWindowFlags(Qt::X11BypassWindowManagerHint);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    QScopedPointer<EventLogger> widgetGuard(new EventLogger);
    EventLogger *widget = widgetGuard.data();
    widget->resize(50, 50);
    widget->setAttribute(Qt::WA_Hover, hoverEnabled);
    widget->setMouseTracking(true);
    view.resize(100, 100);
    if (hasWidget)
        proxy->setWidget(widgetGuard.take());
    proxy->setPos(50, 0);
    scene.addItem(proxy);
    QTest::qWait(30);
    QTest::mouseMove(&view, QPoint(10, 10));
    QTest::qWait(30);

    // in
    QTest::mouseMove(&view, QPoint(50, 50));
    QSKIP("QTBUG-25294");
    QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), hasWidget);
    // ### this attribute isn't supported
    QCOMPARE(widget->enterCount, hasWidget ? 1 : 0);
    QCOMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0);
    // does not work on all platforms
    //QCOMPARE(widget->moveCount, 0);

    // out
    QTest::mouseMove(&view, QPoint(10, 10));
    // QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), false);
    // ### this attribute isn't supported
    QTRY_COMPARE(widget->leaveCount, hasWidget ? 1 : 0);
    QTRY_COMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0);
    // does not work on all platforms
    //QCOMPARE(widget->moveCount, 0);
}
#endif

void tst_QGraphicsProxyWidget::hoverMoveEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::addColumn<bool>("hoverEnabled");
    QTest::addColumn<bool>("mouseTracking");
    QTest::addColumn<bool>("mouseDown");
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 2; ++j) {
            for (int k = 0; k < 2; ++k) {
                for (int l = 0; l < 2; ++l) {
                    bool hasWidget = (i == 0);
                    bool hoverEnabled = (j == 0);
                    bool mouseTracking = (k == 0);
                    bool mouseDown = (l == 0);
                    QString name = QString("hasWidget:%1, hover:%2, mouseTracking:%3, mouseDown: %4").arg(hasWidget).arg(hoverEnabled).arg(mouseTracking).arg(mouseDown);
                    QTest::newRow(name.toLatin1()) << hasWidget << hoverEnabled << mouseTracking << mouseDown;
                }
            }
        }
    }
}

// protected void hoverMoveEvent(QGraphicsSceneHoverEvent* event)
void tst_QGraphicsProxyWidget::hoverMoveEvent()
{
    QFETCH(bool, hasWidget);
    QFETCH(bool, hoverEnabled);
    QFETCH(bool, mouseTracking);
    QFETCH(bool, mouseDown);

    QSKIP("Ambiguous test...");

    // proxy should translate the move events to what the widget would expect

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
    EventLogger *widget = new EventLogger;
    widget->resize(50, 50);
    widget->setAttribute(Qt::WA_Hover, hoverEnabled);
    if (mouseTracking)
        widget->setMouseTracking(true);
    view.resize(100, 100);
    if (hasWidget)
        proxy->setWidget(widget);
    proxy->setPos(50, 0);
    scene.addItem(proxy);

    // in
    QTest::mouseMove(&view, QPoint(50, 50));
    QTest::qWait(12);

    if (mouseDown)
        QTest::mousePress(view.viewport(), Qt::LeftButton);

    // move a little bit
    QTest::mouseMove(&view, QPoint(60, 60));
    QTRY_COMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0);
    QCOMPARE(widget->moveCount, (hasWidget && mouseTracking) || (hasWidget && mouseDown) ? 1 : 0);

    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::keyPressEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::newRow("widget") << true;
    QTest::newRow("no widget") << false;
}

// protected void keyPressEvent(QKeyEvent* event)
void tst_QGraphicsProxyWidget::keyPressEvent()
{
    QFETCH(bool, hasWidget);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    view.viewport()->setFocus();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(QApplication::activeWindow(), (QWidget*)&view);

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!

    QLineEdit *widget = new QLineEdit;
    widget->resize(50, 50);
    view.resize(100, 100);
    if (hasWidget) {
        proxy->setWidget(widget);
        proxy->show();
    }
    proxy->setPos(50, 0);
    scene.addItem(proxy);
    proxy->setFocus();

    QTest::keyPress(view.viewport(), 'x');

    QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString());

    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::keyReleaseEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::newRow("widget") << true;
    QTest::newRow("no widget") << false;
}

// protected void keyReleaseEvent(QKeyEvent* event)
void tst_QGraphicsProxyWidget::keyReleaseEvent()
{
    QFETCH(bool, hasWidget);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(QApplication::activeWindow(), (QWidget*)&view);


    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
    QPushButton *widget = new QPushButton;
    QSignalSpy spy(widget, SIGNAL(clicked()));
    widget->resize(50, 50);
    view.resize(100, 100);
    if (hasWidget) {
        proxy->setWidget(widget);
        proxy->show();
    }
    proxy->setPos(50, 0);
    scene.addItem(proxy);
    proxy->setFocus();

    QTest::keyPress(view.viewport(), Qt::Key_Space);
    QTRY_COMPARE(spy.count(), 0);
    QTest::keyRelease(view.viewport(), Qt::Key_Space);
    QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0);

    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::mouseDoubleClickEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::newRow("widget") << true;
    QTest::newRow("no widget") << false;
}

// protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
void tst_QGraphicsProxyWidget::mouseDoubleClickEvent()
{
    QFETCH(bool, hasWidget);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();

    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(QApplication::activeWindow(), (QWidget*)&view);

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
    QLineEdit *widget = new QLineEdit;
    widget->setText("foo");
    widget->resize(50, 50);
    view.resize(100, 100);
    if (hasWidget) {
        proxy->setWidget(widget);
        proxy->show();
    }
    proxy->setPos(50, 0);
    scene.addItem(proxy);
    proxy->setFocus();

    QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())));
    QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())));
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())));
    QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())));
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())));

    QTRY_COMPARE(widget->selectedText(), hasWidget ? QString("foo") : QString());

    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::newRow("widget") << true;
    QTest::newRow("no widget") << false;
}

// protected void mousePressEvent(QGraphicsSceneMouseEvent* event)
void tst_QGraphicsProxyWidget::mousePressReleaseEvent()
{
    QFETCH(bool, hasWidget);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
    QPushButton *widget = new QPushButton;
    QSignalSpy spy(widget, SIGNAL(clicked()));
    widget->resize(50, 50);
    view.resize(100, 100);
    if (hasWidget) {
        proxy->setWidget(widget);
        proxy->show();
    }
    proxy->setPos(50, 0);
    scene.addItem(proxy);
    proxy->setFocus();

    QTest::mousePress(view.viewport(), Qt::LeftButton, 0,
                      view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
    QTRY_COMPARE(spy.count(), 0);
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0,
                        view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
    QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0);

    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::resizeEvent_data()
{
    QTest::addColumn<bool>("hasWidget");
    QTest::newRow("widget") << true;
    QTest::newRow("no widget") << false;
}

// protected void resizeEvent(QGraphicsSceneResizeEvent* event)
void tst_QGraphicsProxyWidget::resizeEvent()
{
    QFETCH(bool, hasWidget);

    SubQGraphicsProxyWidget proxy;
    QWidget *widget = new QWidget;
    if (hasWidget)
        proxy.setWidget(widget);

    QSize newSize(100, 100);
    QGraphicsSceneResizeEvent event;
    event.setOldSize(QSize(10, 10));
    event.setNewSize(newSize);
    proxy.call_resizeEvent(&event);
    if (hasWidget)
        QCOMPARE(widget->size(), newSize);
    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::paintEvent()
{
    //we test that calling update on a widget inside a QGraphicsView is triggering a repaint
    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QVERIFY(view.isActiveWindow());

    SubQGraphicsProxyWidget proxy;

    QWidget *w = new QWidget;
    //showing the widget here seems to create a bug in Graphics View
    //this bug prevents the widget from being updated

    w->show();
    QVERIFY(QTest::qWaitForWindowExposed(w));
    QApplication::processEvents();
    QTest::qWait(30);
    proxy.setWidget(w);
    scene.addItem(&proxy);

    //make sure we flush all the paint events
    QTRY_VERIFY(proxy.paintCount > 1);
    QTest::qWait(30);
    proxy.paintCount = 0;

    w->update();
    QTRY_COMPARE(proxy.paintCount, 1); //the widget should have been painted now
}


void tst_QGraphicsProxyWidget::wheelEvent()
{
    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    WheelWidget *wheelWidget = new WheelWidget();
    wheelWidget->setFixedSize(400, 400);

    QGraphicsProxyWidget *proxy = scene.addWidget(wheelWidget);
    proxy->setVisible(true);

    QGraphicsSceneWheelEvent event(QEvent::GraphicsSceneWheel);
    event.setScenePos(QPoint(50, 50));
    event.setAccepted(false);
    wheelWidget->wheelEventCalled = false;

    QApplication::sendEvent(&scene, &event);

    QVERIFY(event.isAccepted());
    QVERIFY(wheelWidget->wheelEventCalled);
}

Q_DECLARE_METATYPE(Qt::SizeHint)
void tst_QGraphicsProxyWidget::sizeHint_data()
{
    QTest::addColumn<Qt::SizeHint>("which");
    QTest::addColumn<QSizeF>("constraint");
    QTest::addColumn<QSizeF>("sizeHint");
    QTest::addColumn<bool>("hasWidget");

    for (int i = 0; i < 2; ++i) {
        bool hasWidget = (i == 0);
        // ### What should these do?
        QTest::newRow("min") << Qt::MinimumSize << QSizeF() << QSizeF() << hasWidget;
        QTest::newRow("pre") << Qt::PreferredSize << QSizeF() << QSizeF() << hasWidget;
        QTest::newRow("max") << Qt::MaximumSize << QSizeF() << QSizeF() << hasWidget;
        QTest::newRow("mindes") << Qt::MinimumDescent << QSizeF() << QSizeF() << hasWidget;
        QTest::newRow("nsize") << Qt::NSizeHints << QSizeF() << QSizeF() << hasWidget;
    }
}

// protected QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const
void tst_QGraphicsProxyWidget::sizeHint()
{
    QFETCH(Qt::SizeHint, which);
    QFETCH(QSizeF, constraint);
    QFETCH(QSizeF, sizeHint);
    QFETCH(bool, hasWidget);
    QSKIP("Broken test");
    SubQGraphicsProxyWidget proxy;
    QWidget *widget = new QWidget;
    if (hasWidget)
        proxy.setWidget(widget);
    QCOMPARE(proxy.call_sizeHint(which, constraint), sizeHint);
    if (!hasWidget)
        delete widget;
}

void tst_QGraphicsProxyWidget::sizePolicy()
{
    for (int p = 0; p < 2; ++p) {
        bool hasWidget = (p == 0);
        SubQGraphicsProxyWidget proxy;
        QWidget *widget = new QWidget;
        QSizePolicy proxyPol(QSizePolicy::Maximum, QSizePolicy::Expanding);
        proxy.setSizePolicy(proxyPol);
        QSizePolicy widgetPol(QSizePolicy::Fixed, QSizePolicy::Minimum);
        widget->setSizePolicy(widgetPol);

        QCOMPARE(proxy.sizePolicy(), proxyPol);
        QCOMPARE(widget->sizePolicy(), widgetPol);
        if (hasWidget) {
            proxy.setWidget(widget);
            QCOMPARE(proxy.sizePolicy(), widgetPol);
        } else {
            QCOMPARE(proxy.sizePolicy(), proxyPol);
        }
        QCOMPARE(widget->sizePolicy(), widgetPol);

        proxy.setSizePolicy(widgetPol);
        widget->setSizePolicy(proxyPol);
        if (hasWidget)
            QCOMPARE(proxy.sizePolicy(), proxyPol);
        else
            QCOMPARE(proxy.sizePolicy(), widgetPol);

        if (!hasWidget)
            delete widget;
    }
}

void tst_QGraphicsProxyWidget::minimumSize()
{
    SubQGraphicsProxyWidget proxy;
    QWidget *widget = new QWidget;
    QSize minSize(50, 50);
    widget->setMinimumSize(minSize);
    proxy.resize(30, 30);
    widget->resize(30,30);
    QCOMPARE(proxy.size(), QSizeF(30, 30));
    proxy.setWidget(widget);
    QCOMPARE(proxy.size().toSize(), minSize);
    QCOMPARE(proxy.minimumSize().toSize(), minSize);
    widget->setMinimumSize(70, 70);
    QCOMPARE(proxy.minimumSize(), QSizeF(70, 70));
    QCOMPARE(proxy.size(), QSizeF(70, 70));
}

void tst_QGraphicsProxyWidget::maximumSize()
{
    SubQGraphicsProxyWidget proxy;
    QWidget *widget = new QWidget;
    QSize maxSize(150, 150);
    widget->setMaximumSize(maxSize);
    proxy.resize(200, 200);
    widget->resize(200,200);
    QCOMPARE(proxy.size(), QSizeF(200, 200));
    proxy.setWidget(widget);
    QCOMPARE(proxy.size().toSize(), maxSize);
    QCOMPARE(proxy.maximumSize().toSize(), maxSize);
    widget->setMaximumSize(70, 70);
    QCOMPARE(proxy.maximumSize(), QSizeF(70, 70));
    QCOMPARE(proxy.size(), QSizeF(70, 70));
}

class View : public QGraphicsView
{
public:
    View(QGraphicsScene *scene, QWidget *parent = 0)
        : QGraphicsView(scene, parent), npaints(0)
    { }
    QRegion paintEventRegion;
    int npaints;
protected:
    void paintEvent(QPaintEvent *event)
    {
        ++npaints;
        paintEventRegion += event->region();
        QGraphicsView::paintEvent(event);
    }
};

class ScrollWidget : public QWidget
{
    Q_OBJECT
public:
    ScrollWidget() : npaints(0)
    {
        resize(200, 200);
    }
    QRegion paintEventRegion;
    int npaints;

public slots:
    void updateScroll()
    {
        update(0, 0, 200, 10);
        scroll(0, 10, QRect(0, 0, 100, 20));
    }

protected:
    void paintEvent(QPaintEvent *event)
    {
        ++npaints;
        paintEventRegion += event->region();
        QPainter painter(this);
        painter.fillRect(event->rect(), Qt::blue);
    }
};

void tst_QGraphicsProxyWidget::scrollUpdate()
{
    ScrollWidget *widget = new ScrollWidget;

    QGraphicsScene scene;
    scene.addWidget(widget);

    View view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QTRY_VERIFY(view.npaints >= 1);
    QTest::qWait(20);
    widget->paintEventRegion = QRegion();
    widget->npaints = 0;
    view.paintEventRegion = QRegion();
    view.npaints = 0;
    QTimer::singleShot(0, widget, SLOT(updateScroll()));
    QTRY_COMPARE(view.npaints, 2);
    // QRect(0, 0, 200, 12) is the first update, expanded (-2, -2, 2, 2)
    // QRect(0, 12, 102, 10) is the scroll update, expanded (-2, -2, 2, 2),
    // intersected with the above update.
    QCOMPARE(view.paintEventRegion.rects(),
             QVector<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10));
    QCOMPARE(widget->npaints, 2);
    QCOMPARE(widget->paintEventRegion.rects(),
             QVector<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10));
}

void tst_QGraphicsProxyWidget::setWidget_simple()
{
    QGraphicsProxyWidget proxy;
    QLineEdit *lineEdit = new QLineEdit;
    proxy.setWidget(lineEdit);

    QVERIFY(lineEdit->testAttribute(Qt::WA_DontShowOnScreen));
    // Size hints
    // ### size hints are tested in a different test
    // QCOMPARE(proxy.effectiveSizeHint(Qt::MinimumSize).toSize(), lineEdit->minimumSizeHint());
    // QCOMPARE(proxy.effectiveSizeHint(Qt::MaximumSize).toSize(), lineEdit->maximumSize());
    // QCOMPARE(proxy.effectiveSizeHint(Qt::PreferredSize).toSize(), lineEdit->sizeHint());
    QCOMPARE(proxy.size().toSize(), lineEdit->minimumSizeHint().expandedTo(lineEdit->size()));
    QRect rect = lineEdit->rect();
    rect.setSize(rect.size().expandedTo(lineEdit->minimumSizeHint()));
    QCOMPARE(proxy.rect().toRect(), rect);

    // Properties
    // QCOMPARE(proxy.focusPolicy(), lineEdit->focusPolicy());
    // QCOMPARE(proxy.palette(), lineEdit->palette());
#ifndef QTEST_NO_CURSOR
    QCOMPARE(proxy.cursor().shape(), lineEdit->cursor().shape());
#endif
    QCOMPARE(proxy.layoutDirection(), lineEdit->layoutDirection());
    QCOMPARE(proxy.style(), lineEdit->style());
    QCOMPARE(proxy.font(), lineEdit->font());
    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
}

void tst_QGraphicsProxyWidget::setWidget_ownership()
{
    QPointer<QLineEdit> lineEdit = new QLineEdit;
    QPointer<QLineEdit> lineEdit2 = new QLineEdit;
    QVERIFY(lineEdit);
    {
        // Create a proxy and transfer ownership to it
        QGraphicsProxyWidget proxy;
        proxy.setWidget(lineEdit);
        QCOMPARE(proxy.widget(), (QWidget *)lineEdit);

        // Remove the widget without destroying it.
        proxy.setWidget(0);
        QVERIFY(!proxy.widget());
        QVERIFY(lineEdit);

        // Assign the widget again and switch to another widget.
        proxy.setWidget(lineEdit);
        proxy.setWidget(lineEdit2);
        QCOMPARE(proxy.widget(), (QWidget *)lineEdit2);

        // Assign the first widget, and destroy the proxy.
        proxy.setWidget(lineEdit);
    }
    QVERIFY(!lineEdit);
    QVERIFY(lineEdit2);

    QGraphicsScene scene;
    QPointer<QGraphicsProxyWidget> proxy = scene.addWidget(lineEdit2);

    delete lineEdit2;
    QVERIFY(!proxy);
}

void tst_QGraphicsProxyWidget::resize_simple_data()
{
    QTest::addColumn<QSizeF>("size");

    QTest::newRow("200, 200") << QSizeF(200, 200);
#if !defined(Q_PROCESSOR_ARM) && !defined(Q_OS_WINCE)
    QTest::newRow("1000, 1000") << QSizeF(1000, 1000);
    // Since 4.5, 10000x10000 runs out of memory.
    // QTest::newRow("10000, 10000") << QSizeF(10000, 10000);
#endif
}

void tst_QGraphicsProxyWidget::resize_simple()
{
    QFETCH(QSizeF, size);

    QGraphicsProxyWidget proxy;
    QWidget *widget = new QWidget;
    widget->setGeometry(0, 0, (int)size.width(), (int)size.height());
    proxy.setWidget(widget);
    widget->show();
    QCOMPARE(widget->pos(), QPoint());

    // The proxy resizes itself, the line edit follows
    proxy.resize(size);
    QCOMPARE(proxy.size(), size);
    QCOMPARE(proxy.size().toSize(), widget->size());

    // The line edit resizes itself, the proxy follows (no loopback/live lock)
    QSize doubleSize = size.toSize() * 2;
    widget->resize(doubleSize);
    QCOMPARE(widget->size(), doubleSize);
    QCOMPARE(widget->size(), proxy.size().toSize());
}

void tst_QGraphicsProxyWidget::symmetricMove()
{
    QGraphicsProxyWidget proxy;
    QLineEdit *lineEdit = new QLineEdit;
    proxy.setWidget(lineEdit);
    lineEdit->show();

    proxy.setPos(10, 10);
    QCOMPARE(proxy.pos(), QPointF(10, 10));
    QCOMPARE(lineEdit->pos(), QPoint(10, 10));

    lineEdit->move(5, 5);
    QCOMPARE(proxy.pos(), QPointF(5, 5));
    QCOMPARE(lineEdit->pos(), QPoint(5, 5));
}

void tst_QGraphicsProxyWidget::symmetricResize()
{
    QGraphicsProxyWidget proxy;
    QLineEdit *lineEdit = new QLineEdit;
    proxy.setWidget(lineEdit);
    lineEdit->show();

    proxy.resize(256, 256);
    QCOMPARE(proxy.size(), QSizeF(256, 256));
    QCOMPARE(lineEdit->size(), QSize(256, 256));

    lineEdit->resize(512, 512);
    QCOMPARE(proxy.size(), QSizeF(512, 512));
    QCOMPARE(lineEdit->size(), QSize(512, 512));
}

void tst_QGraphicsProxyWidget::symmetricVisible()
{
    QGraphicsProxyWidget proxy;
    QLineEdit *lineEdit = new QLineEdit;
    proxy.setWidget(lineEdit);
    lineEdit->show();

    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());

    proxy.hide();
    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
    proxy.show();
    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
    lineEdit->hide();
    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
    lineEdit->show();
    QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
 }

void tst_QGraphicsProxyWidget::symmetricEnabled()
{
    QGraphicsProxyWidget proxy;
    QLineEdit *lineEdit = new QLineEdit;
    proxy.setWidget(lineEdit);
    lineEdit->show();

    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
    proxy.setEnabled(false);
    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
    proxy.setEnabled(true);
    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
    lineEdit->setEnabled(false);
    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
    lineEdit->setEnabled(true);
    QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
}

void tst_QGraphicsProxyWidget::tabFocus_simpleWidget()
{
    QGraphicsScene scene;
    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
    editProxy->show();

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit);

    // Tab into line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QTRY_VERIFY(!leftDial->hasFocus());
    QTRY_VERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(editProxy->hasFocus());
    QVERIFY(edit->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);

    // Tab into right dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QTRY_VERIFY(!view->hasFocus());
    QVERIFY(!view->viewport()->hasFocus());
    QVERIFY(!scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QTRY_VERIFY(rightDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);

    // Backtab into line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QTRY_VERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QTRY_VERIFY(scene.hasFocus());
    QVERIFY(editProxy->hasFocus());
    QVERIFY(edit->hasFocus());
    QVERIFY(!rightDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);

    // Backtab into left dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QTRY_VERIFY(!view->hasFocus());
    QVERIFY(!view->viewport()->hasFocus());
    QVERIFY(!scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QTRY_VERIFY(leftDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);

    delete view;
}

void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets()
{
    QGraphicsScene scene;
    QLineEdit *edit = new QLineEdit;
    QLineEdit *edit2 = new QLineEdit;
    QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
    editProxy->show();
    QGraphicsProxyWidget *editProxy2 = scene.addWidget(edit2);
    editProxy2->show();
    editProxy2->setPos(0, editProxy->rect().height() * 1.1);

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit);
    EventSpy eventSpy2(edit2);

    // Tab into line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!leftDial->hasFocus());
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(editProxy->hasFocus());
    QVERIFY(edit->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);

    // Tab into second line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(editProxy2->hasFocus());
    QVERIFY(edit2->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);

    // Tab into right dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!view->hasFocus());
    QVERIFY(!view->viewport()->hasFocus());
    QVERIFY(!scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(!editProxy2->hasFocus());
    QVERIFY(!edit2->hasFocus());
    QVERIFY(rightDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);

    // Backtab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(editProxy2->hasFocus());
    QVERIFY(edit2->hasFocus());
    QVERIFY(!rightDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);

    // Backtab into line edit 1
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(editProxy->hasFocus());
    QVERIFY(edit->hasFocus());
    QVERIFY(!editProxy2->hasFocus());
    QVERIFY(!edit2->hasFocus());
    QVERIFY(!rightDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2);

    // Backtab into left dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!view->hasFocus());
    QVERIFY(!view->viewport()->hasFocus());
    QVERIFY(!scene.hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(leftDial->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);

    delete view;
}

void tst_QGraphicsProxyWidget::tabFocus_complexWidget()
{
    QGraphicsScene scene;

    QLineEdit *edit1 = new QLineEdit;
    edit1->setText("QLineEdit 1");
    QLineEdit *edit2 = new QLineEdit;
    edit2->setText("QLineEdit 2");
    QVBoxLayout *vlayout = new QVBoxLayout;
    vlayout->addWidget(edit1);
    vlayout->addWidget(edit2);

    QGroupBox *box = new QGroupBox("QGroupBox");
    box->setCheckable(true);
    box->setChecked(true);
    box->setLayout(vlayout);

    QGraphicsProxyWidget *proxy = scene.addWidget(box);
    proxy->show();

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit1);
    EventSpy eventSpy2(edit2);
    EventSpy eventSpyBox(box);

    // Tab into group box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!leftDial->hasFocus());
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(proxy->hasFocus());
    QVERIFY(box->hasFocus());

    // Tab into line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    edit1->hasFocus();
    QVERIFY(!box->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);

    // Tab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    edit2->hasFocus();
    QVERIFY(!edit1->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);

    // Tab into right dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!edit2->hasFocus());
    rightDial->hasFocus();
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);

    // Backtab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!rightDial->hasFocus());
    edit2->hasFocus();
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);

    // Backtab into line edit 1
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit2->hasFocus());
    edit1->hasFocus();
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2);
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);

    // Backtab into line box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit1->hasFocus());
    box->hasFocus();
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);

    // Backtab into left dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!box->hasFocus());
    leftDial->hasFocus();

    delete view;
}

void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets()
{
    // ### add event spies to this test.
    QGraphicsScene scene;

    QLineEdit *edit1 = new QLineEdit;
    edit1->setText("QLineEdit 1");
    QLineEdit *edit2 = new QLineEdit;
    edit2->setText("QLineEdit 2");
    QFontComboBox *fontComboBox = new QFontComboBox;
    QVBoxLayout *vlayout = new QVBoxLayout;
    vlayout->addWidget(edit1);
    vlayout->addWidget(fontComboBox);
    vlayout->addWidget(edit2);

    QGroupBox *box = new QGroupBox("QGroupBox");
    box->setCheckable(true);
    box->setChecked(true);
    box->setLayout(vlayout);

    QLineEdit *edit1_2 = new QLineEdit;
    edit1_2->setText("QLineEdit 1_2");
    QLineEdit *edit2_2 = new QLineEdit;
    edit2_2->setText("QLineEdit 2_2");
    QFontComboBox *fontComboBox2 = new QFontComboBox;
    vlayout = new QVBoxLayout;
    vlayout->addWidget(edit1_2);
    vlayout->addWidget(fontComboBox2);
    vlayout->addWidget(edit2_2);

    QGroupBox *box_2 = new QGroupBox("QGroupBox 2");
    box_2->setCheckable(true);
    box_2->setChecked(true);
    box_2->setLayout(vlayout);

    QGraphicsProxyWidget *proxy = scene.addWidget(box);
    proxy->show();

    QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2);
    proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0);
    proxy_2->show();

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);
    view->setRenderHint(QPainter::Antialiasing);
    view->rotate(45);
    view->scale(0.5, 0.5);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit1);
    EventSpy eventSpy2(edit2);
    EventSpy eventSpy3(fontComboBox);
    EventSpy eventSpy1_2(edit1_2);
    EventSpy eventSpy2_2(edit2_2);
    EventSpy eventSpy2_3(fontComboBox2);
    EventSpy eventSpyBox(box);

    // Tab into group box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!leftDial->hasFocus());
    QVERIFY(view->hasFocus());
    QVERIFY(view->viewport()->hasFocus());
    QVERIFY(scene.hasFocus());
    QVERIFY(proxy->hasFocus());
    QVERIFY(box->hasFocus());

    // Tab into line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    edit1->hasFocus();
    QVERIFY(!box->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);

    // Tab to the font combobox
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    fontComboBox->hasFocus();
    QVERIFY(!edit2->hasFocus());
    QCOMPARE(eventSpy3.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 0);
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);

    // Tab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    edit2->hasFocus();
    QVERIFY(!edit1->hasFocus());
    QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);
    QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);

    // Tab into right box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!edit2->hasFocus());
    box_2->hasFocus();

    // Tab into right top line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!box_2->hasFocus());
    edit1_2->hasFocus();
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 0);

    // Tab into right font combobox
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!edit1_2->hasFocus());
    fontComboBox2->hasFocus();
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 0);

    // Tab into right bottom line edit
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!edit1_2->hasFocus());
    edit2_2->hasFocus();
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 0);

    // Tab into right dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
    QApplication::processEvents();
    QVERIFY(!edit2->hasFocus());
    rightDial->hasFocus();
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);

    // Backtab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!rightDial->hasFocus());
    edit2_2->hasFocus();

    // Backtab into the right font combobox
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit2_2->hasFocus());
    fontComboBox2->hasFocus();

    // Backtab into line edit 1
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit2_2->hasFocus());
    edit1_2->hasFocus();

    // Backtab into line box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit1_2->hasFocus());
    box_2->hasFocus();

    // Backtab into line edit 2
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!rightDial->hasFocus());
    edit2->hasFocus();

    // Backtab into the font combobox
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit2->hasFocus());
    fontComboBox->hasFocus();

    // Backtab into line edit 1
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!fontComboBox->hasFocus());
    edit1->hasFocus();

    // Backtab into line box
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!edit1->hasFocus());
    box->hasFocus();

    // Backtab into left dial
    QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
    QApplication::processEvents();
    QVERIFY(!box->hasFocus());
    leftDial->hasFocus();

    delete view;
}

void tst_QGraphicsProxyWidget::setFocus_simpleWidget()
{
    QGraphicsScene scene;
    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
    editProxy->show();

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));
    QCOMPARE(QApplication::activeWindow(), &window);

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit);

    // Calling setFocus for the line edit when the view doesn't have input
    // focus does not steal focus from the dial. The edit, proxy and scene
    // have focus but only in their own little space.
    edit->setFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(edit->hasFocus());
    QVERIFY(!view->hasFocus());
    QVERIFY(!view->viewport()->hasFocus());
    QVERIFY(leftDial->hasFocus());

    // Clearing focus is a local operation.
    edit->clearFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(!view->hasFocus());
    QVERIFY(leftDial->hasFocus());

    view->setFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(view->hasFocus());
    QVERIFY(!leftDial->hasFocus());
    QVERIFY(!edit->hasFocus());

    scene.clearFocus();
    QVERIFY(!scene.hasFocus());

    edit->setFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(edit->hasFocus());
    QVERIFY(editProxy->hasFocus());

    // Symmetry
    editProxy->clearFocus();
    QVERIFY(!edit->hasFocus());

    delete view;
}

void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets()
{
    QGraphicsScene scene;
    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
    editProxy->show();
    QLineEdit *edit2 = new QLineEdit;
    QGraphicsProxyWidget *edit2Proxy = scene.addWidget(edit2);
    edit2Proxy->show();
    edit2Proxy->setPos(editProxy->boundingRect().width() * 1.01, 0);

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));
    QCOMPARE(QApplication::activeWindow(), &window);

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit);

    view->setFocus();
    QVERIFY(!edit->hasFocus());

    edit->setFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(edit->hasFocus());
    QVERIFY(editProxy->hasFocus());

    edit2->setFocus();
    QVERIFY(scene.hasFocus());
    QVERIFY(!edit->hasFocus());
    QVERIFY(!editProxy->hasFocus());
    QVERIFY(edit2->hasFocus());
    QVERIFY(edit2Proxy->hasFocus());

    delete view;
}

void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets()
{
    // ### add event spies to this test.
    QGraphicsScene scene;

    QLineEdit *edit1 = new QLineEdit;
    edit1->setText("QLineEdit 1");
    QLineEdit *edit2 = new QLineEdit;
    edit2->setText("QLineEdit 2");
    QVBoxLayout *vlayout = new QVBoxLayout;
    vlayout->addWidget(edit1);
    vlayout->addWidget(edit2);

    QGroupBox *box = new QGroupBox("QGroupBox");
    box->setCheckable(true);
    box->setChecked(true);
    box->setLayout(vlayout);

    QLineEdit *edit1_2 = new QLineEdit;
    edit1_2->setText("QLineEdit 1_2");
    QLineEdit *edit2_2 = new QLineEdit;
    edit2_2->setText("QLineEdit 2_2");
    vlayout = new QVBoxLayout;
    vlayout->addWidget(edit1_2);
    vlayout->addWidget(edit2_2);

    QGroupBox *box_2 = new QGroupBox("QGroupBox 2");
    box_2->setCheckable(true);
    box_2->setChecked(true);
    box_2->setLayout(vlayout);

    QGraphicsProxyWidget *proxy = scene.addWidget(box);
    proxy->show();

    QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2);
    proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0);
    proxy_2->show();

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;
    QGraphicsView *view = new QGraphicsView(&scene);

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(view);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    window.show();
    QApplication::setActiveWindow(&window);
    window.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&window));
    QCOMPARE(QApplication::activeWindow(), &window);

    leftDial->setFocus();
    QApplication::processEvents();
    QTRY_VERIFY(leftDial->hasFocus());

    EventSpy eventSpy(edit1);
    EventSpy eventSpy2(edit2);
    EventSpy eventSpyBox(box);
    EventSpy eventSpy_2(edit1_2);
    EventSpy eventSpy2_2(edit2_2);
    EventSpy eventSpyBox_2(box_2);

    view->setFocus();

    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 0);

    edit1->setFocus();
    QApplication::processEvents();
    QVERIFY(scene.hasFocus());
    QVERIFY(edit1->hasFocus());
    QVERIFY(!box->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0);

    edit2_2->setFocus();
    QApplication::processEvents();
    QVERIFY(!edit1->hasFocus());
    QVERIFY(!box_2->hasFocus());
    QVERIFY(edit2_2->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0);
    QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);

    box->setFocus();
    QApplication::processEvents();
    QVERIFY(!edit2_2->hasFocus());
    QVERIFY(!edit1->hasFocus());
    QVERIFY(box->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 0);
    QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);
    QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0);

    edit2_2->setFocus();
    QApplication::processEvents();
    QVERIFY(edit2_2->hasFocus());
    QVERIFY(!edit1->hasFocus());
    QVERIFY(!box->hasFocus());
    QVERIFY(!box_2->hasFocus());
    QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 2);
    QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1);
    QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 1);
    QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);
    QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0);

    delete view;
}

void tst_QGraphicsProxyWidget::popup_basic()
{
    QScopedPointer<QComboBox> box(new QComboBox);
    QStyleOptionComboBox opt;
    opt.initFrom(box.data());
    opt.editable = box->isEditable();
    if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt))
        QSKIP("Does not work due to SH_Combobox_Popup");

    // ProxyWidget should automatically create proxy's when the widget creates a child
    QGraphicsScene *scene = new QGraphicsScene;
    QGraphicsView view(scene);
    view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
    view.setGeometry(0, 100, 480, 500);
    view.show();

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    box->setGeometry(0, 0, 320, 40);
    box->addItems(QStringList() << "monday" << "tuesday" << "wednesday"
                  << "thursday" << "saturday" << "sunday");
    QCOMPARE(proxy->childItems().count(), 0);
    proxy->setWidget(box.data());
    proxy->show();
    scene->addItem(proxy);

    QCOMPARE(box->pos(), QPoint());
    QCOMPARE(proxy->pos(), QPointF());

    QVERIFY(QTest::qWaitForWindowExposed(&view));
    QTest::qWait(125);
    QApplication::processEvents();

    QTest::mousePress(view.viewport(), Qt::LeftButton, 0,
                      view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));

    QTRY_COMPARE(box->pos(), QPoint());

    QCOMPARE(proxy->childItems().count(), 1);
    QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0];
    QVERIFY(child->isWidget());
    QVERIFY(child->widget());
    QCOMPARE(child->widget()->parent(), static_cast<QObject*>(box.data()));

    QTRY_COMPARE(proxy->pos(), QPointF(box->pos()));
    QCOMPARE(child->x(), qreal(box->x()));
    QCOMPARE(child->y(), qreal(box->rect().bottom()));
#ifndef Q_OS_WIN
    // The popup's coordinates on Windows are in global space,
    // regardless.
    QCOMPARE(child->widget()->x(), box->x());
    QCOMPARE(child->widget()->y(), box->rect().bottom());
    QCOMPARE(child->geometry().toRect(), child->widget()->geometry());
#endif
    QTest::qWait(12);
}

void tst_QGraphicsProxyWidget::popup_subwidget()
{
    QGroupBox *groupBox = new QGroupBox;
    groupBox->setTitle("GroupBox");
    groupBox->setCheckable(true);

    QComboBox *box = new QComboBox;
    box->addItems(QStringList() << "monday" << "tuesday" << "wednesday"
                  << "thursday" << "saturday" << "sunday");

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(new QLineEdit("QLineEdit"));
    layout->addWidget(box);
    layout->addWidget(new QLineEdit("QLineEdit"));
    groupBox->setLayout(layout);

    QGraphicsScene scene;
    QGraphicsProxyWidget *groupBoxProxy = scene.addWidget(groupBox);
    groupBox->show();
    groupBox->move(10000, 10000);
    QCOMPARE(groupBox->pos(), QPoint(10000, 10000));
    QCOMPARE(groupBoxProxy->pos(), QPointF(10000, 10000));

    QGraphicsView view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    box->showPopup();

    QVERIFY(!groupBoxProxy->childItems().isEmpty());

    QStyleOptionComboBox opt;
    opt.initFrom(box);
    opt.editable = box->isEditable();
    if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt))
        QSKIP("Does not work due to SH_Combobox_Popup");
    QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(groupBoxProxy->childItems())[0];
    QWidget *popup = child->widget();
    QCOMPARE(popup->parent(), static_cast<QObject*>(box));
    QCOMPARE(popup->x(), box->mapToGlobal(QPoint()).x());
    QCOMPARE(popup->y(), QRect(box->mapToGlobal(QPoint()), box->size()).bottom());
    QCOMPARE(popup->size(), child->size().toSize());
}

#ifndef QTEST_NO_CURSOR
void tst_QGraphicsProxyWidget::changingCursor_basic()
{
    // Confirm that mouse events are working properly by checking that
    // when moving the mouse over a line edit it will change the cursor into the I
    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();

    SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget;
    QLineEdit *widget = new QLineEdit;
    proxy->setWidget(widget);
    proxy->show();
    scene.addItem(proxy);
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QVERIFY(view.isActiveWindow());

    // in
    QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
    sendMouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
    QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);

    // out
    QTest::mouseMove(view.viewport(), QPoint(1, 1));
    sendMouseMove(view.viewport(), QPoint(1, 1));
    QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor);
}
#endif

void tst_QGraphicsProxyWidget::tooltip_basic()
{
    QString toolTip = "Qt rocks!";
    QString toolTip2 = "Qt rocks even more!";

    QPushButton *button = new QPushButton("button");
    QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget;
    QGraphicsProxyWidgetPrivate *proxyd = static_cast<QGraphicsProxyWidgetPrivate *>(QGraphicsItemPrivate::get(proxy));
    proxy->setWidget(button);
    proxyd->lastWidgetUnderMouse = button; // force widget under mouse

    QVERIFY(button->toolTip().isEmpty());
    QVERIFY(proxy->toolTip().isEmpty());
    // Check that setting the tooltip on the proxy also set it on the widget
    proxy->setToolTip(toolTip);
    QCOMPARE(proxy->toolTip(), toolTip);
    QCOMPARE(button->toolTip(), toolTip);
    // Check that setting the tooltip on the widget also set it on the proxy
    button->setToolTip(toolTip2);
    QCOMPARE(proxy->toolTip(), toolTip2);
    QCOMPARE(button->toolTip(), toolTip2);

    QGraphicsScene scene;
    scene.addItem(proxy);

    QGraphicsView view(&scene);
    view.setFixedSize(200, 200);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    {
        QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
                             view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
        QApplication::sendEvent(view.viewport(), &helpEvent);
        QTest::qWait(350);

        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.mapFromScene(proxy->boundingRect().center()),
                             view.viewport()->mapToGlobal(view.mapFromScene(proxy->boundingRect().center())));
        QApplication::sendEvent(view.viewport(), &helpEvent);
        QTest::qWait(350);

        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);
    }
}

void tst_QGraphicsProxyWidget::childPos_data()
{
    QTest::addColumn<bool>("moveCombo");
    QTest::addColumn<QPoint>("comboPos");
    QTest::addColumn<QPointF>("proxyPos");

    QTest::newRow("0") << true << QPoint() << QPointF();
    QTest::newRow("1") << true << QPoint(10, 0) << QPointF(10, 0);
    QTest::newRow("2") << true << QPoint(100, 0) << QPointF(100, 0);
    QTest::newRow("3") << true << QPoint(1000, 0) << QPointF(1000, 0);
    QTest::newRow("4") << true << QPoint(10000, 0) << QPointF(10000, 0);
    QTest::newRow("5") << true << QPoint(-10000, 0) << QPointF(-10000, 0);
    QTest::newRow("6") << true << QPoint(-1000, 0) << QPointF(-1000, 0);
    QTest::newRow("7") << true << QPoint(-100, 0) << QPointF(-100, 0);
    QTest::newRow("8") << true << QPoint(-10, 0) << QPointF(-10, 0);
    QTest::newRow("0-") << false << QPoint() << QPointF();
    QTest::newRow("1-") << false << QPoint(10, 0) << QPointF(10, 0);
    QTest::newRow("2-") << false << QPoint(100, 0) << QPointF(100, 0);
    QTest::newRow("3-") << false << QPoint(1000, 0) << QPointF(1000, 0);
    QTest::newRow("4-") << false << QPoint(10000, 0) << QPointF(10000, 0);
    QTest::newRow("5-") << false << QPoint(-10000, 0) << QPointF(-10000, 0);
    QTest::newRow("6-") << false << QPoint(-1000, 0) << QPointF(-1000, 0);
    QTest::newRow("7-") << false << QPoint(-100, 0) << QPointF(-100, 0);
    QTest::newRow("8-") << false << QPoint(-10, 0) << QPointF(-10, 0);
}

void tst_QGraphicsProxyWidget::childPos()
{
#ifdef Q_OS_IRIX
    QSKIP("This test is not reliable on IRIX.");
#endif
    QFETCH(bool, moveCombo);
    QFETCH(QPoint, comboPos);
    QFETCH(QPointF, proxyPos);

    QGraphicsScene scene;

    QComboBox *box = new QComboBox;
    box->addItem("Item 1");
    box->addItem("Item 2");
    box->addItem("Item 3");
    box->addItem("Item 4");

    if (moveCombo)
        box->move(comboPos);

    QGraphicsProxyWidget *proxy = scene.addWidget(box);
    proxy->show();
    QVERIFY(proxy->isVisible());
    QVERIFY(box->isVisible());

    if (!moveCombo)
        proxy->setPos(proxyPos);

    QCOMPARE(proxy->pos(), proxyPos);
    QCOMPARE(box->pos(), comboPos);

    for (int i = 0; i < 2; ++i) {
        box->showPopup();
        QApplication::processEvents();
        QApplication::processEvents();

        QWidget *menu = 0;
        foreach (QObject *child, box->children()) {
            if ((menu = qobject_cast<QWidget *>(child)))
                break;
        }
        QVERIFY(menu);
        QVERIFY(menu->isVisible());
        QVERIFY(menu->testAttribute(Qt::WA_DontShowOnScreen));

        QCOMPARE(proxy->childItems().size(), 1);
        QGraphicsProxyWidget *proxyChild = 0;
        foreach (QGraphicsItem *child, proxy->childItems()) {
            if (child->isWidget() && (proxyChild = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child))))
                break;
        }
        QVERIFY(proxyChild);
        QVERIFY(proxyChild->isVisible());
        qreal expectedXPosition = 0.0;
#if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC)
        // The Mac style wants the popup to show up at QPoint(4 - 11, 1).
        // See QMacStyle::subControlRect SC_ComboBoxListBoxPopup.
        if (QApplication::style()->inherits("QMacStyle"))
            expectedXPosition = qreal(4 - 11);
#endif
        QCOMPARE(proxyChild->pos().x(), expectedXPosition);
        menu->hide();
    }
}

void tst_QGraphicsProxyWidget::autoShow()
{
    QGraphicsScene scene;
    QGraphicsView view(&scene);

    QGraphicsProxyWidget *proxy1 = scene.addWidget(new QPushButton("Button1"));

    QPushButton *button2 = new QPushButton("Button2");
    button2->hide();
    QGraphicsProxyWidget *proxy2 = new QGraphicsProxyWidget();
    proxy2->setWidget(button2);
    scene.addItem(proxy2);

    view.show();
    QApplication::processEvents();

    QCOMPARE(proxy1->isVisible(), true);
    QCOMPARE(proxy2->isVisible(), false);

}

void tst_QGraphicsProxyWidget::windowOpacity()
{
    QGraphicsScene scene;
    QGraphicsView view(&scene);

    QWidget *widget = new QWidget;
    widget->resize(100, 100);
    QGraphicsProxyWidget *proxy = scene.addWidget(widget);
    proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache);

    QApplication::setActiveWindow(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QVERIFY(view.isActiveWindow());

    qRegisterMetaType<QList<QRectF> >("QList<QRectF>");
    QSignalSpy signalSpy(&scene, SIGNAL(changed(QList<QRectF>)));

    EventSpy eventSpy(widget);
    QVERIFY(widget->isVisible());

    widget->setWindowOpacity(0.5);
    QApplication::processEvents();

    // Make sure setWindowOpacity triggers an update on the scene,
    // and not on the widget or the proxy itself. The entire proxy needs an update
    // in case it has a window decoration. Update: QGraphicsItem::CacheMode is
    // disabled on platforms without alpha channel support in QPixmap (e.g.,
    // X11 without XRender).
    int paints = 0;
#ifdef Q_WS_X11
    paints = !X11->use_xrender;
#endif
    QTRY_COMPARE(eventSpy.counts[QEvent::UpdateRequest], 0);
    QTRY_COMPARE(eventSpy.counts[QEvent::Paint], paints);

    QCOMPARE(signalSpy.count(), 1);
    const QList<QVariant> arguments = signalSpy.takeFirst();
    const QList<QRectF> updateRects = qvariant_cast<QList<QRectF> >(arguments.at(0));
    QCOMPARE(updateRects.size(), 1);
    QCOMPARE(updateRects.at(0), proxy->sceneBoundingRect());
}

void tst_QGraphicsProxyWidget::stylePropagation()
{
    QPointer<QStyle> windowsStyle = QStyleFactory::create("windows");

    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget proxy;
    proxy.setWidget(edit);

    EventSpy editSpy(edit);
    EventSpy proxySpy(&proxy);

    // Widget to proxy
    QCOMPARE(proxy.style(), QApplication::style());
    edit->setStyle(windowsStyle);
    QCOMPARE(editSpy.counts[QEvent::StyleChange], 1);
    QCOMPARE(proxySpy.counts[QEvent::StyleChange], 1);
    QCOMPARE(proxy.style(), (QStyle *)windowsStyle);
    edit->setStyle(0);
    QCOMPARE(editSpy.counts[QEvent::StyleChange], 2);
    QCOMPARE(proxySpy.counts[QEvent::StyleChange], 2);
    QCOMPARE(proxy.style(), QApplication::style());
    QCOMPARE(edit->style(), QApplication::style());
    QVERIFY(windowsStyle); // not deleted

    // Proxy to widget
    proxy.setStyle(windowsStyle);
    QCOMPARE(editSpy.counts[QEvent::StyleChange], 3);
    QCOMPARE(proxySpy.counts[QEvent::StyleChange], 3);
    QCOMPARE(edit->style(), (QStyle *)windowsStyle);
    proxy.setStyle(0);
    QCOMPARE(editSpy.counts[QEvent::StyleChange], 4);
    QCOMPARE(proxySpy.counts[QEvent::StyleChange], 4);
    QCOMPARE(proxy.style(), QApplication::style());
    QCOMPARE(edit->style(), QApplication::style());
    QVERIFY(windowsStyle); // not deleted

    delete windowsStyle;
}

void tst_QGraphicsProxyWidget::palettePropagation()
{
    // Construct a palette with an unlikely setup
    QPalette lineEditPalette = QApplication::palette("QLineEdit");
    QPalette palette = lineEditPalette;
    palette.setBrush(QPalette::Text, Qt::red);

    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget proxy;
    proxy.setWidget(edit);

    EventSpy editSpy(edit);
    EventSpy proxySpy(&proxy);

    // Widget to proxy (no change)
    QVERIFY(!edit->testAttribute(Qt::WA_SetPalette));
    edit->setPalette(palette);
    QCOMPARE(editSpy.counts[QEvent::PaletteChange], 1);
    QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0);
    QVERIFY(edit->testAttribute(Qt::WA_SetPalette));
    QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
    QCOMPARE(proxy.palette(), QPalette());
    edit->setPalette(QPalette());
    QCOMPARE(editSpy.counts[QEvent::PaletteChange], 2);
    QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0);
    QVERIFY(!edit->testAttribute(Qt::WA_SetPalette));
    QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
    QCOMPARE(proxy.palette(), QPalette());

    // Proxy to widget
    proxy.setPalette(palette);
    QVERIFY(proxy.testAttribute(Qt::WA_SetPalette));
    QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3);
    QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1);
    QVERIFY(!edit->testAttribute(Qt::WA_SetPalette));
    QCOMPARE(edit->palette(), palette);
    QCOMPARE(edit->palette(), proxy.palette());
    QCOMPARE(edit->palette().color(QPalette::Text), QColor(Qt::red));
    proxy.setPalette(QPalette());
    QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
    QVERIFY(!edit->testAttribute(Qt::WA_SetPalette));
    QCOMPARE(editSpy.counts[QEvent::PaletteChange], 4);
    QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 2);
    QCOMPARE(edit->palette(), lineEditPalette);
}

void tst_QGraphicsProxyWidget::fontPropagation()
{
    // Construct a font with an unlikely setup
    QGraphicsScene scene;
    QFont lineEditFont = QApplication::font("QLineEdit");
    QFont font = lineEditFont;
    font.setPointSize(43);

    QLineEdit *edit = new QLineEdit;
    QGraphicsProxyWidget proxy;
    proxy.setWidget(edit);

    scene.addItem(&proxy);
    EventSpy editSpy(edit);
    EventSpy proxySpy(&proxy);

    // Widget to proxy (no change)
    QVERIFY(!edit->testAttribute(Qt::WA_SetFont));
    edit->setFont(font);
    QCOMPARE(editSpy.counts[QEvent::FontChange], 1);
    QCOMPARE(proxySpy.counts[QEvent::FontChange], 0);
    QVERIFY(edit->testAttribute(Qt::WA_SetFont));
    QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
    QCOMPARE(proxy.font(), lineEditFont);
    edit->setFont(QFont());
    QCOMPARE(editSpy.counts[QEvent::FontChange], 2);
    QCOMPARE(proxySpy.counts[QEvent::FontChange], 0);
    QVERIFY(!edit->testAttribute(Qt::WA_SetFont));
    QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
    QCOMPARE(proxy.font(), lineEditFont);

    // Proxy to widget
    proxy.setFont(font);
    QApplication::processEvents();  // wait for QEvent::Polish
    QVERIFY(proxy.testAttribute(Qt::WA_SetFont));
    QCOMPARE(editSpy.counts[QEvent::FontChange], 3);
    QCOMPARE(proxySpy.counts[QEvent::FontChange], 1);
    QVERIFY(!edit->testAttribute(Qt::WA_SetFont));
    QCOMPARE(edit->font(), font);
    QCOMPARE(edit->font(), proxy.font());
    QCOMPARE(edit->font().pointSize(), 43);
    proxy.setFont(QFont());
    QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
    QVERIFY(!edit->testAttribute(Qt::WA_SetFont));
    QCOMPARE(editSpy.counts[QEvent::FontChange], 4);
    QCOMPARE(proxySpy.counts[QEvent::FontChange], 2);
    QCOMPARE(edit->font(), lineEditFont);

    proxy.setFont(font);
    QLineEdit *edit2 = new QLineEdit;
    proxy.setWidget(edit2);
    delete edit;
    QCOMPARE(edit2->font().pointSize(), 43);
}

class MainWidget : public QMainWindow
{
Q_OBJECT
public:
    MainWidget() : QMainWindow() {
      view = new QGraphicsView(this);
      scene = new QGraphicsScene(this);
      item = new QGraphicsWidget();
      widget = new QGraphicsProxyWidget(item);
      layout = new QGraphicsLinearLayout(item);
      layout->addItem(widget);
      item->setLayout(layout);
      button = new QPushButton("Push me");
      widget->setWidget(button);
      setCentralWidget(view);
      scene->addItem(item);
      view->setScene(scene);
      scene->setFocusItem(item);
      layout->activate();
    }
    QGraphicsView* view;
    QGraphicsScene* scene;
    QGraphicsWidget * item;
    QGraphicsLinearLayout * layout;
    QGraphicsProxyWidget * widget;
    QPushButton * button;
};

void tst_QGraphicsProxyWidget::dontCrashWhenDie()
{
    MainWidget *w = new MainWidget();
    w->show();
    QVERIFY(QTest::qWaitForWindowExposed(w));
    QTest::qWait(100);
    QTest::mouseMove(w->view->viewport(), w->view->mapFromScene(w->widget->mapToScene(w->widget->boundingRect().center())));
    delete w->item;

    QApplication::processEvents();
    delete w;
    // This leaves an invisible proxy widget behind.
    qDeleteAll(QApplication::topLevelWidgets());
}

void tst_QGraphicsProxyWidget::createProxyForChildWidget()
{
    QGraphicsScene scene;

    QLineEdit *edit1 = new QLineEdit;
    edit1->setText("QLineEdit 1");
    QLineEdit *edit2 = new QLineEdit;
    edit2->setText("QLineEdit 2");
    QCheckBox *checkbox = new QCheckBox("QCheckBox");
    QVBoxLayout *vlayout = new QVBoxLayout;

    vlayout->addWidget(edit1);
    vlayout->addWidget(edit2);
    vlayout->addWidget(checkbox);
    vlayout->insertStretch(-1);

    QGroupBox *box = new QGroupBox("QGroupBox");
    box->setCheckable(true);
    box->setChecked(true);
    box->setLayout(vlayout);

    QDial *leftDial = new QDial;
    QDial *rightDial = new QDial;

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(leftDial);
    layout->addWidget(box);
    layout->addWidget(rightDial);
    window.setLayout(layout);

    QVERIFY(window.graphicsProxyWidget() == 0);
    QVERIFY(checkbox->graphicsProxyWidget() == 0);

    QGraphicsProxyWidget *windowProxy = scene.addWidget(&window);
    QGraphicsView view(&scene);
    view.show();
    view.resize(500,500);

    QVERIFY(window.graphicsProxyWidget() == windowProxy);
    QVERIFY(box->graphicsProxyWidget() == 0);
    QVERIFY(checkbox->graphicsProxyWidget() == 0);

    QPointer<QGraphicsProxyWidget> checkboxProxy = windowProxy->createProxyForChildWidget(checkbox);

    QGraphicsProxyWidget *boxProxy = box->graphicsProxyWidget();

    QVERIFY(boxProxy);
    QVERIFY(checkbox->graphicsProxyWidget() == checkboxProxy);
    QVERIFY(checkboxProxy->parentItem() == boxProxy);
    QVERIFY(boxProxy->parentItem() == windowProxy);

    QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint()));
    QVERIFY(checkboxProxy->size() == checkbox->size());
    QVERIFY(boxProxy->size() == box->size());

    window.resize(500,500);
    QVERIFY(windowProxy->size() == QSize(500,500));
    QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint()));
    QVERIFY(checkboxProxy->size() == checkbox->size());
    QVERIFY(boxProxy->size() == box->size());

    QTest::qWait(10);


    QSignalSpy spy(checkbox, SIGNAL(clicked()));

    QTest::mousePress(view.viewport(), Qt::LeftButton, 0,
                      view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8))));
    QTRY_COMPARE(spy.count(), 0);
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0,
                        view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8))));
    QTRY_COMPARE(spy.count(), 1);



    boxProxy->setWidget(0);

    QVERIFY(checkbox->graphicsProxyWidget() == 0);
    QVERIFY(box->graphicsProxyWidget() == 0);
    QVERIFY(checkboxProxy == 0);

    delete boxProxy;
}

class ContextMenuWidget : public QLabel
{
    Q_OBJECT
public:
    ContextMenuWidget()
        : QLabel(QStringLiteral("ContextMenuWidget"))
        , embeddedPopup(false)
        , gotContextMenuEvent(false)
        , m_embeddedPopupSet(false)
        , m_timer(0)
    { }
    bool embeddedPopup;
    bool gotContextMenuEvent;
protected:
    bool event(QEvent *event)
    {
        if (event->type() == QEvent::ContextMenu) {
            if (!m_timer) {
                m_timer = new QTimer(this);
                m_timer->setInterval(10);
                connect(m_timer, SIGNAL(timeout()), this, SLOT(checkMenu()));
                m_timer->start();
            }
        }
        return QWidget::event(event);
    }
    void contextMenuEvent(QContextMenuEvent *)
    {
        gotContextMenuEvent = true;
    }

private slots:
    void checkMenu()
    {
        QMenu *menu = findChild<QMenu *>();
        if (!m_embeddedPopupSet) {
            m_embeddedPopupSet = true;
            embeddedPopup = menu != 0;
        }
        if (menu && menu->isVisible())
            menu->hide();
        hide();
    }

private:
    bool m_embeddedPopupSet;
    QTimer *m_timer;
};

void tst_QGraphicsProxyWidget::actionsContextMenu_data()
{
    QTest::addColumn<bool>("actionsContextMenu");
    QTest::addColumn<bool>("hasFocus");

    QTest::newRow("without actionsContextMenu and with focus") << false << true;
    QTest::newRow("without actionsContextMenu and without focus") << false << false;
    QTest::newRow("with actionsContextMenu and focus") << true << true;
    QTest::newRow("with actionsContextMenu without focus") << true << false;
}

void tst_QGraphicsProxyWidget::actionsContextMenu()
{
    QFETCH(bool, hasFocus);
    QFETCH(bool, actionsContextMenu);

    ContextMenuWidget *widget = new ContextMenuWidget;
    if (actionsContextMenu) {
        widget->addAction(new QAction("item 1", widget));
        widget->addAction(new QAction("item 2", widget));
        widget->addAction(new QAction("item 3", widget));
        widget->setContextMenuPolicy(Qt::ActionsContextMenu);
    }
    QGraphicsScene scene;
    QGraphicsProxyWidget *proxyWidget = scene.addWidget(widget);
    QGraphicsView view(&scene);
    view.setWindowTitle(QStringLiteral("actionsContextMenu"));
    view.resize(200, 200);
    view.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(100, 100));
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    view.setFocus();
    QTRY_VERIFY(view.hasFocus());

    if (hasFocus)
        proxyWidget->setFocus();
    else
        proxyWidget->clearFocus();

    QApplication::processEvents();

    QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse,
                                       view.viewport()->rect().center(),
                                       view.viewport()->mapToGlobal(view.viewport()->rect().center()));
    contextMenuEvent.accept();
    qApp->sendEvent(view.viewport(), &contextMenuEvent);

    if (hasFocus) {
        if (actionsContextMenu) {
            //actionsContextMenu embedded popup but no contextMenuEvent (widget has focus)
            QVERIFY(widget->embeddedPopup);
            QVERIFY(!widget->gotContextMenuEvent);
        } else {
            //no embedded popup but contextMenuEvent (widget has focus)
            QVERIFY(!widget->embeddedPopup);
            QVERIFY(widget->gotContextMenuEvent);
        }
    } else {
        //qgraphicsproxywidget doesn't have the focus, the widget must not receive any contextMenuEvent and must not create any QMenu
        QVERIFY(!widget->embeddedPopup);
        QVERIFY(!widget->gotContextMenuEvent);
    }

}


void tst_QGraphicsProxyWidget::deleteProxyForChildWidget()
{
    QDialog dialog;
    dialog.resize(320, 120);
    dialog.move(80, 40);

    QGraphicsScene scene;
    scene.setSceneRect(QRectF(0, 0, 640, 480));
    QGraphicsView view(&scene);
    QComboBox *combo = new QComboBox;
    combo->addItems(QStringList() << "red" << "green" << "blue" << "white" << "black" << "yellow" << "cyan" << "magenta");
    dialog.setLayout(new QVBoxLayout);
    dialog.layout()->addWidget(combo);

    QGraphicsProxyWidget *proxy = scene.addWidget(&dialog);
    view.show();

    proxy->setWidget(0);
    //just don't crash
    QApplication::processEvents();
    delete combo;
}

void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data()
{
    QTest::addColumn<bool>("bypass");

    QTest::newRow("autoembed") << false;
    QTest::newRow("bypass") << true;
}

void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget()
{
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
    QSKIP("Test case unstable on this platform, QTBUG-33067");
#endif
    QFETCH(bool, bypass);

    QWidget *widget = new QWidget;
    widget->resize(100, 100);

    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.show();

    QGraphicsProxyWidget *proxy = scene.addWidget(widget);

    QCOMPARE(proxy->widget(), widget);
    QVERIFY(proxy->childItems().isEmpty());

    Qt::WindowFlags flags;
    flags |= Qt::Dialog;
    if (bypass)
        flags |= Qt::BypassGraphicsProxyWidget;
    QFileDialog *dialog = new QFileDialog(widget, flags);
    dialog->setOption(QFileDialog::DontUseNativeDialog, true);
    dialog->show();

    QCOMPARE(proxy->childItems().size(), bypass ? 0 : 1);
    if (!bypass)
        QCOMPARE(((QGraphicsProxyWidget *)proxy->childItems().first())->widget(), (QWidget *)dialog);

    dialog->hide();
    QApplication::processEvents();
    delete dialog;
    delete widget;
}

static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos)
{
    event->setScenePos(pos);
    event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos)));
    event->setButtons(Qt::LeftButton);
    event->setModifiers(0);
    event->setPossibleActions(Qt::CopyAction);
    event->setProposedAction(Qt::CopyAction);
    event->setDropAction(Qt::CopyAction);
    event->setWidget(view->viewport());
    event->setSource(view->viewport());
    event->ignore();
}

void tst_QGraphicsProxyWidget::dragDrop()
{
    QPushButton *button = new QPushButton; // acceptDrops(false)
    QLineEdit *edit = new QLineEdit; // acceptDrops(true)

    QGraphicsScene scene;
    QGraphicsProxyWidget *buttonProxy = scene.addWidget(button);
    QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
    QVERIFY(buttonProxy->acceptDrops());
    QVERIFY(editProxy->acceptDrops());

    button->setGeometry(0, 0, 100, 50);
    edit->setGeometry(0, 60, 100, 50);

    QGraphicsView view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QMimeData data;
    data.setText("hei");
    {
        QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragEnter);
        makeDndEvent(&event, &view, QPointF(50, 25));
        event.setMimeData(&data);
        qApp->sendEvent(&scene, &event);
        QVERIFY(event.isAccepted());
    }
    {
        QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove);
        makeDndEvent(&event, &view, QPointF(50, 25));
        event.setMimeData(&data);
        qApp->sendEvent(&scene, &event);
        QVERIFY(!event.isAccepted());
    }
    {
        QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove);
        makeDndEvent(&event, &view, QPointF(50, 75));
        event.setMimeData(&data);
        qApp->sendEvent(&scene, &event);
        QVERIFY(event.isAccepted());
    }
    {
        QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDrop);
        makeDndEvent(&event, &view, QPointF(50, 75));
        event.setMimeData(&data);
        qApp->sendEvent(&scene, &event);
        QVERIFY(event.isAccepted());
    }
    QCOMPARE(edit->text(), QString("hei"));
}

void tst_QGraphicsProxyWidget::windowFlags_data()
{
    QTest::addColumn<int>("proxyFlags");
    QTest::addColumn<int>("widgetFlags");
    QTest::addColumn<int>("resultingProxyFlags");
    QTest::addColumn<int>("resultingWidgetFlags");

    QTest::newRow("proxy(0) widget(0)") << 0 << 0 << 0 << int(Qt::Window);
    QTest::newRow("proxy(window)") << int(Qt::Window) << 0 << int(Qt::Window) << int(Qt::Window);
    QTest::newRow("proxy(window) widget(window)") << int(Qt::Window) << int(Qt::Window) << int(Qt::Window) << int(Qt::Window);
    QTest::newRow("proxy(0) widget(window)") << int(0) << int(Qt::Window) << int(0) << int(Qt::Window);
}

void tst_QGraphicsProxyWidget::windowFlags()
{
    QFETCH(int, proxyFlags);
    QFETCH(int, widgetFlags);
    QFETCH(int, resultingProxyFlags);
    QFETCH(int, resultingWidgetFlags);
    Qt::WindowFlags proxyWFlags = Qt::WindowFlags(proxyFlags);
    Qt::WindowFlags widgetWFlags = Qt::WindowFlags(widgetFlags);
    Qt::WindowFlags resultingProxyWFlags = Qt::WindowFlags(resultingProxyFlags);
    Qt::WindowFlags resultingWidgetWFlags = Qt::WindowFlags(resultingWidgetFlags);

    QGraphicsProxyWidget proxy(0, proxyWFlags);
    QVERIFY((proxy.windowFlags() & proxyWFlags) == proxyWFlags);

    QWidget *widget = new QWidget(0, widgetWFlags);
    QVERIFY((widget->windowFlags() & widgetWFlags) == widgetWFlags);

    proxy.setWidget(widget);

    if (resultingProxyFlags == 0)
        QVERIFY(!proxy.windowFlags());
    else
        QVERIFY((proxy.windowFlags() & resultingProxyWFlags) == resultingProxyWFlags);
    QVERIFY((widget->windowFlags() & resultingWidgetWFlags) == resultingWidgetWFlags);
}

void tst_QGraphicsProxyWidget::comboboxWindowFlags()
{
    QComboBox *comboBox = new QComboBox;
    comboBox->addItem("Item 1");
    comboBox->addItem("Item 2");
    comboBox->addItem("Item 3");
    QWidget *embedWidget = comboBox;

    QGraphicsScene scene;
    QGraphicsProxyWidget *proxy = scene.addWidget(embedWidget);
    proxy->setWindowFlags(Qt::Window);
    QVERIFY(embedWidget->isWindow());
    QVERIFY(proxy->isWindow());

    comboBox->showPopup();

    QCOMPARE(proxy->childItems().size(), 1);
    QGraphicsItem *popupProxy = proxy->childItems().first();
    QVERIFY(popupProxy->isWindow());
    QVERIFY((static_cast<QGraphicsWidget *>(popupProxy)->windowFlags() & Qt::Popup) == Qt::Popup);
}

void tst_QGraphicsProxyWidget::updateAndDelete()
{
#ifdef Q_OS_MAC
    QSKIP("Test case unstable on this platform, QTBUG-23700");
#endif
    QGraphicsScene scene;
    QGraphicsProxyWidget *proxy = scene.addWidget(new QPushButton("Hello World"));
    View view(&scene);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    QTRY_VERIFY(view.npaints > 0);
    // Wait a bit to clear all pending paint events
    QTest::qWait(10);

    const QRect itemDeviceBoundingRect = proxy->deviceTransform(view.viewportTransform())
                                         .mapRect(proxy->boundingRect()).toRect();
    const QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2);

    view.npaints = 0;
    view.paintEventRegion = QRegion();

    // Update and hide.
    proxy->update();
    proxy->hide();
    QTRY_COMPARE(view.npaints, 1);
    QCOMPARE(view.paintEventRegion, expectedRegion);

    proxy->show();
    QTest::qWait(50);
    view.npaints = 0;
    view.paintEventRegion = QRegion();

    // Update and delete.
    proxy->update();
    delete proxy;
    QTRY_COMPARE(view.npaints, 1);
    QCOMPARE(view.paintEventRegion, expectedRegion);
}

class InputMethod_LineEdit : public QLineEdit
{
    bool event(QEvent *e)
    {
        if (e->type() == QEvent::InputMethod)
            ++inputMethodEvents;
        return QLineEdit::event(e);
    }
public:
    int inputMethodEvents;
};

void tst_QGraphicsProxyWidget::inputMethod()
{
    QGraphicsScene scene;

    // check that the proxy is initialized with the correct input method sensitivity
    for (int i = 0; i < 2; ++i)
    {
        QLineEdit *lineEdit = new QLineEdit;
        lineEdit->setAttribute(Qt::WA_InputMethodEnabled, !!i);
        QGraphicsProxyWidget *proxy = scene.addWidget(lineEdit);
        QCOMPARE(!!(proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod), !!i);
    }

    // check that input method events are only forwarded to widgets with focus
    for (int i = 0; i < 2; ++i)
    {
        InputMethod_LineEdit *lineEdit = new InputMethod_LineEdit;
        lineEdit->setAttribute(Qt::WA_InputMethodEnabled, true);
        QGraphicsProxyWidget *proxy = scene.addWidget(lineEdit);

        if (i)
            lineEdit->setFocus();

        lineEdit->inputMethodEvents = 0;
        QInputMethodEvent event;
        qApp->sendEvent(proxy, &event);
        QCOMPARE(lineEdit->inputMethodEvents, i);
    }

    scene.clear();
    QGraphicsView view(&scene);
    QWidget *w = new QWidget;
    w->setLayout(new QVBoxLayout(w));
    QLineEdit *lineEdit = new QLineEdit;
    lineEdit->setEchoMode(QLineEdit::Password);
    w->layout()->addWidget(lineEdit);
    lineEdit->setAttribute(Qt::WA_InputMethodEnabled, true);
    QGraphicsProxyWidget *proxy = scene.addWidget(w);
    view.show();
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QVERIFY(!(proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod));
    lineEdit->setFocus();
    QVERIFY((proxy->flags() & QGraphicsItem::ItemAcceptsInputMethod));
}

void tst_QGraphicsProxyWidget::clickFocus()
{
    QGraphicsScene scene;
    scene.setItemIndexMethod(QGraphicsScene::NoIndex);
    QLineEdit *le1 = new QLineEdit;
    QGraphicsProxyWidget *proxy = scene.addWidget(le1);

    QGraphicsView view(&scene);

    {
        EventSpy proxySpy(proxy);
        EventSpy widgetSpy(proxy->widget());

        view.setFrameStyle(0);
        view.resize(300, 300);
        view.show();
        QApplication::setActiveWindow(&view);
        QVERIFY(QTest::qWaitForWindowActive(&view));

        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());

        QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0);
        QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
        QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0);
        QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);

        QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center());
        // Spontaneous mouse click sets focus on a clickable widget.
        for (int retry = 0; retry < 50 && !proxy->hasFocus(); retry++)
            QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter));
        QVERIFY(proxy->hasFocus());
        QVERIFY(proxy->widget()->hasFocus());
        QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1);
        QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1);

        scene.setFocusItem(0);
        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());
        QCOMPARE(proxySpy.counts[QEvent::FocusOut], 1);
        QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 1);

        // Non-spontaneous mouse click sets focus if the widget has been clicked before
        {
            QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
            event.setScenePos(lineEditCenter);
            event.setButton(Qt::LeftButton);
            qApp->sendEvent(&scene, &event);
            QVERIFY(proxy->hasFocus());
            QVERIFY(proxy->widget()->hasFocus());
            QCOMPARE(proxySpy.counts[QEvent::FocusIn], 2);
            QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 2);
        }
    }

    scene.setFocusItem(0);
    proxy->setWidget(new QLineEdit); // resets focusWidget
    delete le1;

    {
        QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center());
        EventSpy proxySpy(proxy);
        EventSpy widgetSpy(proxy->widget());
        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());
        QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
        QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);

        // Non-spontaneous mouse click does not set focus on the embedded widget.
        {
            QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress);
            event.setScenePos(lineEditCenter);
            event.setButton(Qt::LeftButton);
            qApp->sendEvent(&scene, &event);
            QVERIFY(!proxy->hasFocus());
            QVERIFY(!proxy->widget()->hasFocus());
            QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0);
            QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0);
        }

        scene.setFocusItem(0);
        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());
        QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
        QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);

        // Spontaneous click on non-clickable widget does not give focus.
        proxy->widget()->setFocusPolicy(Qt::NoFocus);
        QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter));
        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());

        // Multiple clicks should only result in one FocusIn.
        proxy->widget()->setFocusPolicy(Qt::StrongFocus);
        scene.setFocusItem(0);
        QVERIFY(!proxy->hasFocus());
        QVERIFY(!proxy->widget()->hasFocus());
        QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter));
        QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter));
        QVERIFY(proxy->hasFocus());
        QVERIFY(proxy->widget()->hasFocus());
        QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1);
        QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1);
    }
}

void tst_QGraphicsProxyWidget::windowFrameMargins()
{
    // Make sure the top margin is non-zero when passing Qt::Window.
    QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Window);

    qreal left, top, right, bottom;
    proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
    QVERIFY(top > 0);

    proxy->setWidget(new QPushButton("testtest"));
    proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
    QVERIFY(top > 0);

    QGraphicsScene scene;
    scene.addItem(proxy);
    proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
    QVERIFY(top > 0);

    proxy->unsetWindowFrameMargins();
    proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
    QVERIFY(top > 0);
}

class HoverButton : public QPushButton
{
public:
    HoverButton(QWidget *parent = 0) : QPushButton(parent), hoverLeaveReceived(false)
    {}

    bool hoverLeaveReceived;

    bool event(QEvent* e)
    {
        if(QEvent::HoverLeave == e->type())
            hoverLeaveReceived = true;
        return QPushButton::event(e);
    }
};

class Scene : public QGraphicsScene
{
Q_OBJECT
public:
    Scene() {
        QWidget *background = new QWidget;
        background->setGeometry(0, 0, 500, 500);
        hoverButton = new HoverButton;
        hoverButton->setParent(background);
        hoverButton->setText("Second button");
        hoverButton->setGeometry(10, 10, 200, 50);
        addWidget(background);

        QPushButton *hideButton = new QPushButton("I'm a button with a very very long text");
        hideButton->setGeometry(10, 10, 400, 50);
        topButton = addWidget(hideButton);
        connect(hideButton, SIGNAL(clicked()), this, SLOT(hideButton()));
        topButton->setFocus();
    }

    QGraphicsProxyWidget *topButton;
    HoverButton *hoverButton;

public slots:
    void hideButton() {
       QCursor::setPos(600,600);
       topButton->hide();
    }
};

void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget()
{
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(QTEST_NO_CURSOR)
    QSKIP("Test case unstable on this platform");
#endif
    QGraphicsView view;
    Scene scene;
    view.setScene(&scene);
    view.resize(600, 600);
    QApplication::setActiveWindow(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(QApplication::activeWindow(), &view);
    QCursor::setPos(view.mapToGlobal(view.mapFromScene(scene.topButton->boundingRect().center())));
    QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(scene.topButton->scenePos()));
    QTRY_COMPARE(scene.hoverButton->hoverLeaveReceived, true);
}

void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135
{
    const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
    const QSize size = availableGeometry.size() / 5;
    QGraphicsScene scene;
    QGraphicsView view(&scene);
    view.setWindowTitle(QTest::currentTestFunction());
    view.resize(size);
    view.move(availableGeometry.bottomRight() - QPoint(size.width(), size.height()) - QPoint(100, 100));
    QWidget *embeddedWidget = new QWidget;
    embeddedWidget->setFixedSize(size / 2);
    scene.addWidget(embeddedWidget);
    QApplication::setActiveWindow(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    const QPoint embeddedCenter = embeddedWidget->geometry().center();
    const QPoint embeddedCenterGlobal = embeddedWidget->mapToGlobal(embeddedCenter);
    QCOMPARE(embeddedWidget->mapFromGlobal(embeddedCenterGlobal), embeddedCenter);
    // This should be equivalent to the view center give or take rounding
    // errors due to odd window margins
    const QPoint viewCenter = view.geometry().center();
    QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 2,
             qPrintable(QStringLiteral("%1, %2 != %3, %4")
                        .arg(viewCenter.x()).arg(viewCenter.y())
                        .arg(embeddedCenterGlobal.x()).arg(embeddedCenterGlobal.y())));
}

void tst_QGraphicsProxyWidget::mapToGlobalWithoutScene() // QTBUG-44509
{
    QGraphicsProxyWidget proxyWidget;
    QWidget *embeddedWidget = new QWidget;
    proxyWidget.setWidget(embeddedWidget);
    const QPoint localPos(0, 0);
    const QPoint globalPos = embeddedWidget->mapToGlobal(localPos);
    QCOMPARE(embeddedWidget->mapFromGlobal(globalPos), localPos);
}

// QTBUG_43780: Embedded widgets have isWindow()==true but showing them should not
// trigger the top-level widget code path of show() that closes all popups
// (for example combo popups).
void tst_QGraphicsProxyWidget::QTBUG_43780_visibility()
{
    const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
    const QSize size = availableGeometry.size() / 4;
    QWidget mainWindow;
    QVBoxLayout *layout = new QVBoxLayout(&mainWindow);
    QComboBox *combo = new QComboBox(&mainWindow);
    combo->addItems(QStringList() << "i1" << "i2" << "i3");
    layout->addWidget(combo);
    QGraphicsScene *scene = new QGraphicsScene(&mainWindow);
    QGraphicsView *view = new QGraphicsView(scene, &mainWindow);
    layout->addWidget(view);
    mainWindow.setWindowTitle(QTest::currentTestFunction());
    mainWindow.resize(size);
    mainWindow.move(availableGeometry.topLeft()
                    + QPoint(availableGeometry.width() - size.width(),
                             availableGeometry.height() - size.height()) / 2);
    QLabel *label = new QLabel(QTest::currentTestFunction());
    scene->addWidget(label);
    label->hide();
    mainWindow.show();
    combo->setFocus();
    mainWindow.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&mainWindow));
    combo->showPopup();
    QWidget *comboPopup = combo->view()->window();
    QVERIFY(comboPopup);
    QVERIFY(QTest::qWaitForWindowExposed(comboPopup));
    label->show();
    QTRY_VERIFY(label->isVisible());
    QVERIFY(comboPopup->isVisible());
}

QTEST_MAIN(tst_QGraphicsProxyWidget)
#include "tst_qgraphicsproxywidget.moc"
