/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtTest/QtTest>
#include <QtTest/private/qtesthelpers_p.h>

#include <qabstractitemview.h>
#include <qstandarditemmodel.h>
#include <qapplication.h>
#include <qevent.h>
#include <qlistview.h>
#include <qlistwidget.h>
#include <qtableview.h>
#include <qtablewidget.h>
#include <qtreeview.h>
#include <qtreewidget.h>
#include <qheaderview.h>
#include <qspinbox.h>
#include <qitemdelegate.h>
#include <qpushbutton.h>
#include <qscrollbar.h>
#include <qboxlayout.h>
#include <qitemdelegate.h>
#include <qlineedit.h>
#include <qscreen.h>
#include <qscopedpointer.h>
#include <qstyleditemdelegate.h>
#include <qstringlistmodel.h>
#include <qsortfilterproxymodel.h>
#include <qproxystyle.h>
#include <qdialog.h>

Q_DECLARE_METATYPE(Qt::ItemFlags);

using namespace QTestPrivate;

// Move cursor out of widget area to avoid undesired interaction on Mac.
static inline void moveCursorAway(const QWidget *topLevel)
{
#ifndef QT_NO_CURSOR
    QCursor::setPos(topLevel->geometry().topRight() + QPoint(100, 0));
#else
    Q_UNUSED(topLevel)
#endif
}

class GeometriesTestView : public QTableView
{
    Q_OBJECT
public:
    GeometriesTestView() : QTableView(), updateGeometriesCalled(false) {}
    bool updateGeometriesCalled;
protected slots:
    void updateGeometries() Q_DECL_OVERRIDE { updateGeometriesCalled = true; QTableView::updateGeometries(); }
};

class tst_QAbstractItemView : public QObject
{
    Q_OBJECT

public:
    void basic_tests(QAbstractItemView *view);

private slots:
    void cleanup();
    void getSetCheck();
    void emptyModels_data();
    void emptyModels();
    void setModel_data();
    void setModel();
    void noModel();
    void dragSelect();
    void rowDelegate();
    void columnDelegate();
    void selectAll();
    void ctrlA();
    void persistentEditorFocus();
    void setItemDelegate();
    void setItemDelegate_data();
    // The dragAndDrop() test doesn't work, and is thus disabled on Mac and Windows
    // for the following reasons:
    //   Mac: use of GetCurrentEventButtonState() in QDragManager::drag()
    //   Win: unknown reason
#if !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
#if 0
    void dragAndDrop();
    void dragAndDropOnChild();
#endif
#endif
    void noFallbackToRoot();
    void setCurrentIndex_data();
    void setCurrentIndex();

    void task221955_selectedEditor();
    void task250754_fontChange();
    void task200665_itemEntered();
    void task257481_emptyEditor();
    void shiftArrowSelectionAfterScrolling();
    void shiftSelectionAfterRubberbandSelection();
    void ctrlRubberbandSelection();
    void QTBUG6407_extendedSelection();
    void QTBUG6753_selectOnSelection();
    void testDelegateDestroyEditor();
    void testClickedSignal();
    void testChangeEditorState();
    void deselectInSingleSelection();
    void testNoActivateOnDisabledItem();
    void testFocusPolicy_data();
    void testFocusPolicy();
    void QTBUG31411_noSelection();
    void QTBUG39324_settingSameInstanceOfIndexWidget();
    void sizeHintChangeTriggersLayout();
    void shiftSelectionAfterChangingModelContents();
    void QTBUG48968_reentrant_updateEditorGeometries();
    void QTBUG50102_SH_ItemView_ScrollMode();
    void QTBUG50535_update_on_new_selection_model();
    void testSelectionModelInSyncWithView();
    void testClickToSelect();
    void testDialogAsEditor();
    void QTBUG46785_mouseout_hover_state();
    void testClearModelInClickedSignal();
    void inputMethodEnabled_data();
    void inputMethodEnabled();
};

class MyAbstractItemDelegate : public QAbstractItemDelegate
{
public:
    MyAbstractItemDelegate() : QAbstractItemDelegate() { calledVirtualDtor = false; }
    void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const {}
    QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return size; }
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const
    {
        openedEditor = new QWidget(parent);
        return openedEditor;
    }
    void destroyEditor(QWidget *editor, const QModelIndex &) const
    {
        calledVirtualDtor = true;
        editor->deleteLater();
    }
    void changeSize() { size = QSize(50, 50); emit sizeHintChanged(QModelIndex()); }
    mutable bool calledVirtualDtor;
    mutable QWidget *openedEditor;
    QSize size;
};

class DialogItemDelegate : public QStyledItemDelegate
{
public:
    DialogItemDelegate() : QStyledItemDelegate() { }
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const
    {
        openedEditor = new QDialog(parent);
        return openedEditor;
    }

    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        Q_UNUSED(model)
        Q_UNUSED(index)

        QDialog *dialog = qobject_cast<QDialog *>(editor);
        result = static_cast<QDialog::DialogCode>(dialog->result());
    }

    mutable QDialog::DialogCode result;
    mutable QDialog *openedEditor;
};

// Testing get/set functions
void tst_QAbstractItemView::getSetCheck()
{
    QListView view;
    QAbstractItemView *obj1 = &view;
    // QAbstractItemDelegate * QAbstractItemView::itemDelegate()
    // void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *)
    MyAbstractItemDelegate *var1 = new MyAbstractItemDelegate;
    obj1->setItemDelegate(var1);
    QCOMPARE((QAbstractItemDelegate*)var1, obj1->itemDelegate());
    obj1->setItemDelegate((QAbstractItemDelegate *)0);
    QCOMPARE((QAbstractItemDelegate *)0, obj1->itemDelegate());
    delete var1;

    // EditTriggers QAbstractItemView::editTriggers()
    // void QAbstractItemView::setEditTriggers(EditTriggers)
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::AnyKeyPressed));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::AnyKeyPressed), obj1->editTriggers());
    obj1->setEditTriggers(QAbstractItemView::EditTriggers(QAbstractItemView::AllEditTriggers));
    QCOMPARE(QAbstractItemView::EditTriggers(QAbstractItemView::AllEditTriggers), obj1->editTriggers());

    // bool QAbstractItemView::tabKeyNavigation()
    // void QAbstractItemView::setTabKeyNavigation(bool)
    obj1->setTabKeyNavigation(false);
    QCOMPARE(false, obj1->tabKeyNavigation());
    obj1->setTabKeyNavigation(true);
    QCOMPARE(true, obj1->tabKeyNavigation());

    // bool QAbstractItemView::dragEnabled()
    // void QAbstractItemView::setDragEnabled(bool)
#ifndef QT_NO_DRAGANDDROP
    obj1->setDragEnabled(false);
    QCOMPARE(false, obj1->dragEnabled());
    obj1->setDragEnabled(true);
    QCOMPARE(true, obj1->dragEnabled());
#endif
    // bool QAbstractItemView::alternatingRowColors()
    // void QAbstractItemView::setAlternatingRowColors(bool)
    obj1->setAlternatingRowColors(false);
    QCOMPARE(false, obj1->alternatingRowColors());
    obj1->setAlternatingRowColors(true);
    QCOMPARE(true, obj1->alternatingRowColors());

    // State QAbstractItemView::state()
    // void QAbstractItemView::setState(State)
    obj1->setState(QAbstractItemView::NoState);
    QCOMPARE(QAbstractItemView::NoState, obj1->state());
    obj1->setState(QAbstractItemView::DraggingState);
    QCOMPARE(QAbstractItemView::DraggingState, obj1->state());
    obj1->setState(QAbstractItemView::DragSelectingState);
    QCOMPARE(QAbstractItemView::DragSelectingState, obj1->state());
    obj1->setState(QAbstractItemView::EditingState);
    QCOMPARE(QAbstractItemView::EditingState, obj1->state());
    obj1->setState(QAbstractItemView::ExpandingState);
    QCOMPARE(QAbstractItemView::ExpandingState, obj1->state());
    obj1->setState(QAbstractItemView::CollapsingState);
    QCOMPARE(QAbstractItemView::CollapsingState, obj1->state());

    // QWidget QAbstractScrollArea::viewport()
    // void setViewport(QWidget*)
    QWidget *vp = new QWidget;
    obj1->setViewport(vp);
    QCOMPARE(vp, obj1->viewport());

    QCOMPARE(16, obj1->autoScrollMargin());
    obj1->setAutoScrollMargin(20);
    QCOMPARE(20, obj1->autoScrollMargin());
    obj1->setAutoScrollMargin(16);
    QCOMPARE(16, obj1->autoScrollMargin());
}

void tst_QAbstractItemView::cleanup()
{
    QVERIFY(QApplication::topLevelWidgets().isEmpty());
}

void tst_QAbstractItemView::emptyModels_data()
{
    QTest::addColumn<QString>("viewType");

    QTest::newRow("QListView") << "QListView";
    QTest::newRow("QTableView") << "QTableView";
    QTest::newRow("QTreeView") << "QTreeView";
    QTest::newRow("QHeaderView") << "QHeaderView";
}

void tst_QAbstractItemView::emptyModels()
{
    QFETCH(QString, viewType);

    QScopedPointer<QAbstractItemView> view;
    if (viewType == "QListView")
        view.reset(new QListView());
    else if (viewType == "QTableView")
        view.reset(new QTableView());
    else if (viewType == "QTreeView")
        view.reset(new QTreeView());
    else if (viewType == "QHeaderView")
        view.reset(new QHeaderView(Qt::Vertical));
    else
        QVERIFY(0);
    centerOnScreen(view.data());
    moveCursorAway(view.data());
    view->show();
    QVERIFY(QTest::qWaitForWindowExposed(view.data()));

    QVERIFY(!view->model());
    QVERIFY(!view->selectionModel());
    //QVERIFY(view->itemDelegate() != 0);

    basic_tests(view.data());
}

void tst_QAbstractItemView::setModel_data()
{
    QTest::addColumn<QString>("viewType");

    QTest::newRow("QListView") << "QListView";
    QTest::newRow("QTableView") << "QTableView";
    QTest::newRow("QTreeView") << "QTreeView";
    QTest::newRow("QHeaderView") << "QHeaderView";
}

void tst_QAbstractItemView::setModel()
{
    QFETCH(QString, viewType);

    QScopedPointer<QAbstractItemView> view;

    if (viewType == "QListView")
        view.reset(new QListView());
    else if (viewType == "QTableView")
        view.reset(new QTableView());
    else if (viewType == "QTreeView")
        view.reset(new QTreeView());
    else if (viewType == "QHeaderView")
        view.reset(new QHeaderView(Qt::Vertical));
    else
        QVERIFY(0);
    centerOnScreen(view.data());
    moveCursorAway(view.data());
    view->show();
    QVERIFY(QTest::qWaitForWindowExposed(view.data()));

    QStandardItemModel model(20,20);
    view->setModel(0);
    view->setModel(&model);
    basic_tests(view.data());
}

void tst_QAbstractItemView::basic_tests(QAbstractItemView *view)
{
    // setSelectionModel
    // Will assert as it should
    //view->setSelectionModel(0);
    // setItemDelegate
    //view->setItemDelegate(0);
    // Will asswert as it should

    // setSelectionMode
    view->setSelectionMode(QAbstractItemView::SingleSelection);
    QCOMPARE(view->selectionMode(), QAbstractItemView::SingleSelection);
    view->setSelectionMode(QAbstractItemView::ContiguousSelection);
    QCOMPARE(view->selectionMode(), QAbstractItemView::ContiguousSelection);
    view->setSelectionMode(QAbstractItemView::ExtendedSelection);
    QCOMPARE(view->selectionMode(), QAbstractItemView::ExtendedSelection);
    view->setSelectionMode(QAbstractItemView::MultiSelection);
    QCOMPARE(view->selectionMode(), QAbstractItemView::MultiSelection);
    view->setSelectionMode(QAbstractItemView::NoSelection);
    QCOMPARE(view->selectionMode(), QAbstractItemView::NoSelection);

    // setSelectionBehavior
    view->setSelectionBehavior(QAbstractItemView::SelectItems);
    QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectItems);
    view->setSelectionBehavior(QAbstractItemView::SelectRows);
    QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectRows);
    view->setSelectionBehavior(QAbstractItemView::SelectColumns);
    QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectColumns);

    // setEditTriggers
    view->setEditTriggers(QAbstractItemView::EditKeyPressed);
    QCOMPARE(view->editTriggers(), QAbstractItemView::EditKeyPressed);
    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
    QCOMPARE(view->editTriggers(), QAbstractItemView::NoEditTriggers);
    view->setEditTriggers(QAbstractItemView::CurrentChanged);
    QCOMPARE(view->editTriggers(), QAbstractItemView::CurrentChanged);
    view->setEditTriggers(QAbstractItemView::DoubleClicked);
    QCOMPARE(view->editTriggers(), QAbstractItemView::DoubleClicked);
    view->setEditTriggers(QAbstractItemView::SelectedClicked);
    QCOMPARE(view->editTriggers(), QAbstractItemView::SelectedClicked);
    view->setEditTriggers(QAbstractItemView::AnyKeyPressed);
    QCOMPARE(view->editTriggers(), QAbstractItemView::AnyKeyPressed);
    view->setEditTriggers(QAbstractItemView::AllEditTriggers);
    QCOMPARE(view->editTriggers(), QAbstractItemView::AllEditTriggers);

    // setAutoScroll
    view->setAutoScroll(false);
    QCOMPARE(view->hasAutoScroll(), false);
    view->setAutoScroll(true);
    QCOMPARE(view->hasAutoScroll(), true);

    // setTabKeyNavigation
    view->setTabKeyNavigation(false);
    QCOMPARE(view->tabKeyNavigation(), false);
    view->setTabKeyNavigation(true);
    QCOMPARE(view->tabKeyNavigation(), true);

#ifndef QT_NO_DRAGANDDROP
    // setDropIndicatorShown
    view->setDropIndicatorShown(false);
    QCOMPARE(view->showDropIndicator(), false);
    view->setDropIndicatorShown(true);
    QCOMPARE(view->showDropIndicator(), true);

    // setDragEnabled
    view->setDragEnabled(false);
    QCOMPARE(view->dragEnabled(), false);
    view->setDragEnabled(true);
    QCOMPARE(view->dragEnabled(), true);
#endif

    // setAlternatingRowColors
    view->setAlternatingRowColors(false);
    QCOMPARE(view->alternatingRowColors(), false);
    view->setAlternatingRowColors(true);
    QCOMPARE(view->alternatingRowColors(), true);

    // setIconSize
    view->setIconSize(QSize(16, 16));
    QCOMPARE(view->iconSize(), QSize(16, 16));
    QSignalSpy spy(view, &QAbstractItemView::iconSizeChanged);
    QVERIFY(spy.isValid());
    view->setIconSize(QSize(32, 32));
    QCOMPARE(view->iconSize(), QSize(32, 32));
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(0).value<QSize>(), QSize(32, 32));
    // Should this happen?
    view->setIconSize(QSize(-1, -1));
    QCOMPARE(view->iconSize(), QSize(-1, -1));
    QCOMPARE(spy.count(), 2);

    QCOMPARE(view->currentIndex(), QModelIndex());
    QCOMPARE(view->rootIndex(), QModelIndex());

    view->keyboardSearch("");
    view->keyboardSearch("foo");
    view->keyboardSearch("1");

    QCOMPARE(view->visualRect(QModelIndex()), QRect());

    view->scrollTo(QModelIndex());

    QCOMPARE(view->sizeHintForIndex(QModelIndex()), QSize());
    QCOMPARE(view->indexAt(QPoint(-1, -1)), QModelIndex());

    if (!view->model()){
        QCOMPARE(view->indexAt(QPoint(10, 10)), QModelIndex());
        QCOMPARE(view->sizeHintForRow(0), -1);
        QCOMPARE(view->sizeHintForColumn(0), -1);
    }else if (view->itemDelegate()){
        view->sizeHintForRow(0);
        view->sizeHintForColumn(0);
    }
    view->openPersistentEditor(QModelIndex());
    view->closePersistentEditor(QModelIndex());

    view->reset();
    view->setRootIndex(QModelIndex());
    view->doItemsLayout();
    view->selectAll();
    view->edit(QModelIndex());
    view->clearSelection();
    view->setCurrentIndex(QModelIndex());

    // protected methods
    view->dataChanged(QModelIndex(), QModelIndex());
    view->rowsInserted(QModelIndex(), -1, -1);
    view->rowsAboutToBeRemoved(QModelIndex(), -1, -1);
    view->selectionChanged(QItemSelection(), QItemSelection());
    if (view->model()){
        view->currentChanged(QModelIndex(), QModelIndex());
        view->currentChanged(QModelIndex(), view->model()->index(0,0));
    }
    view->updateEditorData();
    view->updateEditorGeometries();
    view->updateGeometries();
    view->verticalScrollbarAction(QAbstractSlider::SliderSingleStepAdd);
    view->horizontalScrollbarAction(QAbstractSlider::SliderSingleStepAdd);
    view->verticalScrollbarValueChanged(10);
    view->horizontalScrollbarValueChanged(10);
    view->closeEditor(0, QAbstractItemDelegate::NoHint);
    view->commitData(0);
    view->editorDestroyed(0);

    view->setHorizontalStepsPerItem(2);
    view->horizontalStepsPerItem();
    view->setVerticalStepsPerItem(2);
    view->verticalStepsPerItem();

    // Will assert as it should
    // view->setIndexWidget(QModelIndex(), 0);

    view->moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier);
    view->horizontalOffset();
    view->verticalOffset();

//    view->isIndexHidden(QModelIndex()); // will (correctly) assert
    if(view->model())
        view->isIndexHidden(view->model()->index(0,0));

    view->setSelection(QRect(0, 0, 10, 10), QItemSelectionModel::ClearAndSelect);
    view->setSelection(QRect(-1, -1, -1, -1), QItemSelectionModel::ClearAndSelect);
    view->visualRegionForSelection(QItemSelection());
    view->selectedIndexes();

    view->edit(QModelIndex(), QAbstractItemView::NoEditTriggers, 0);

    view->selectionCommand(QModelIndex(), 0);

#ifndef QT_NO_DRAGANDDROP
    if (!view->model())
        view->startDrag(Qt::CopyAction);

    view->viewOptions();

    view->setState(QAbstractItemView::NoState);
    QVERIFY(view->state()==QAbstractItemView::NoState);
    view->setState(QAbstractItemView::DraggingState);
    QVERIFY(view->state()==QAbstractItemView::DraggingState);
    view->setState(QAbstractItemView::DragSelectingState);
    QVERIFY(view->state()==QAbstractItemView::DragSelectingState);
    view->setState(QAbstractItemView::EditingState);
    QVERIFY(view->state()==QAbstractItemView::EditingState);
    view->setState(QAbstractItemView::ExpandingState);
    QVERIFY(view->state()==QAbstractItemView::ExpandingState);
    view->setState(QAbstractItemView::CollapsingState);
    QVERIFY(view->state()==QAbstractItemView::CollapsingState);
#endif

    view->startAutoScroll();
    view->stopAutoScroll();
    view->doAutoScroll();

    // testing mouseFoo and key functions
//     QTest::mousePress(view, Qt::LeftButton, Qt::NoModifier, QPoint(0,0));
//     mouseMove(view, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
//     QTest::mouseRelease(view, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
//     QTest::mouseClick(view, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
//     mouseDClick(view, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
//     QTest::keyClick(view, Qt::Key_A);
}

void tst_QAbstractItemView::noModel()
{
    // From task #85415

    QStandardItemModel model(20,20);
    QTreeView view;
    setFrameless(&view);

    view.setModel(&model);
    // Make the viewport smaller than the contents, so that we can scroll
    view.resize(100,100);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    // make sure that the scrollbars are not at value 0
    view.scrollTo(view.model()->index(10,10));
    QApplication::processEvents();

    view.setModel(0);
    // Due to the model is removed, this will generate a valueChanged signal on both scrollbars. (value to 0)
    QApplication::processEvents();
    QCOMPARE(view.model(), nullptr);
}

void tst_QAbstractItemView::dragSelect()
{
    // From task #86108

    QStandardItemModel model(64,64);

    QTableView view;
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.setVisible(true);
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    const int delay = 2;
    for (int i = 0; i < 2; ++i) {
        bool tracking = (i == 1);
        view.setMouseTracking(false);
        QTest::mouseMove(&view, QPoint(0, 0), delay);
        view.setMouseTracking(tracking);
        QTest::mouseMove(&view, QPoint(50, 50), delay);
        QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
    }
}

void tst_QAbstractItemView::rowDelegate()
{
    QStandardItemModel model(4,4);
    MyAbstractItemDelegate delegate;

    QTableView view;
    view.setModel(&model);
    view.setItemDelegateForRow(3, &delegate);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QModelIndex index = model.index(3, 0);
    view.openPersistentEditor(index);
    QWidget *w = view.indexWidget(index);
    QVERIFY(w);
    QCOMPARE(w->metaObject()->className(), "QWidget");
}

void tst_QAbstractItemView::columnDelegate()
{
    QStandardItemModel model(4,4);
    MyAbstractItemDelegate delegate;

    QTableView view;
    view.setModel(&model);
    view.setItemDelegateForColumn(3, &delegate);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QModelIndex index = model.index(0, 3);
    view.openPersistentEditor(index);
    QWidget *w = view.indexWidget(index);
    QVERIFY(w);
    QCOMPARE(w->metaObject()->className(), "QWidget");
}

void tst_QAbstractItemView::sizeHintChangeTriggersLayout()
{
    QStandardItemModel model(4, 4);
    MyAbstractItemDelegate delegate;
    MyAbstractItemDelegate rowDelegate;
    MyAbstractItemDelegate columnDelegate;

    GeometriesTestView view;
    view.setModel(&model);
    view.setItemDelegate(&delegate);
    view.setItemDelegateForRow(1, &rowDelegate);
    view.setItemDelegateForColumn(2, &columnDelegate);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    view.updateGeometriesCalled = false;
    delegate.changeSize();
    QCoreApplication::sendPostedEvents();
    QVERIFY(view.updateGeometriesCalled);
    view.updateGeometriesCalled = false;
    rowDelegate.changeSize();
    QCoreApplication::sendPostedEvents();
    QVERIFY(view.updateGeometriesCalled);
    view.updateGeometriesCalled = false;
    columnDelegate.changeSize();
    QCoreApplication::sendPostedEvents();
    QVERIFY(view.updateGeometriesCalled);
}

void tst_QAbstractItemView::selectAll()
{
    QStandardItemModel model(4,4);
    QTableView view;
    view.setModel(&model);

    QAbstractItemView *tst_view = &view;

    QCOMPARE(tst_view->selectedIndexes().count(), 0);
    view.selectAll();
    QCOMPARE(tst_view->selectedIndexes().count(), 4*4);
}

void tst_QAbstractItemView::ctrlA()
{
    QStandardItemModel model(4,4);
    QTableView view;
    view.setModel(&model);

    QAbstractItemView *tst_view = &view;

    QCOMPARE(tst_view->selectedIndexes().count(), 0);
    QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier);
    QCOMPARE(tst_view->selectedIndexes().count(), 4*4);
}

void tst_QAbstractItemView::persistentEditorFocus()
{
    // one row, three columns
    QStandardItemModel model(1, 3);
    for(int i = 0; i < model.columnCount(); ++i)
        model.setData(model.index(0, i), i);
    QTableView view;
    view.setModel(&model);

    view.openPersistentEditor(model.index(0, 1));
    view.openPersistentEditor(model.index(0, 2));

    //these are spinboxes because we put numbers inside
    QList<QSpinBox*> list = view.viewport()->findChildren<QSpinBox*>();
    QCOMPARE(list.count(), 2); //these should be the 2 editors

    view.setCurrentIndex(model.index(0, 0));
    QCOMPARE(view.currentIndex(), model.index(0, 0));
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    for (int i = 0; i < list.count(); ++i) {
        QTRY_VERIFY(list.at(i)->isVisible());
        QPoint p = QPoint(5, 5);
        QMouseEvent mouseEvent(QEvent::MouseButtonPress, p, Qt::LeftButton,
                               Qt::LeftButton, Qt::NoModifier);
        qApp->sendEvent(list.at(i), &mouseEvent);
        if (!qApp->focusWidget())
            QSKIP("Some window managers don't handle focus that well");
        QTRY_COMPARE(qApp->focusWidget(), static_cast<QWidget *>(list.at(i)));
    }
}


#if !defined(Q_OS_MAC) && !defined(Q_OS_WIN)

#if 0

static void sendMouseMove(QWidget *widget, QPoint pos = QPoint())
{
    if (pos.isNull())
        pos = widget->rect().center();
    QMouseEvent event(QEvent::MouseMove, pos, widget->mapToGlobal(pos), Qt::NoButton, 0, 0);
    QCursor::setPos(widget->mapToGlobal(pos));
    qApp->processEvents();
    QVERIFY(QTest::qWaitForWindowExposed(widget));
    QApplication::sendEvent(widget, &event);
}

static void sendMousePress(
    QWidget *widget, QPoint pos = QPoint(), Qt::MouseButton button = Qt::LeftButton)
{
    if (pos.isNull())
         pos = widget->rect().center();
    QMouseEvent event(QEvent::MouseButtonPress, pos, widget->mapToGlobal(pos), button, 0, 0);
    QApplication::sendEvent(widget, &event);
}

static void sendMouseRelease(
    QWidget *widget, QPoint pos = QPoint(), Qt::MouseButton button = Qt::LeftButton)
{
    if (pos.isNull())
         pos = widget->rect().center();
    QMouseEvent event(QEvent::MouseButtonRelease, pos, widget->mapToGlobal(pos), button, 0, 0);
    QApplication::sendEvent(widget, &event);
}

class DnDTestModel : public QStandardItemModel
{
    Q_OBJECT
    bool dropMimeData(const QMimeData *md, Qt::DropAction action, int r, int c, const QModelIndex &p)
    {
        dropAction_result = action;
        QStandardItemModel::dropMimeData(md, action, r, c, p);
        return true;
    }
    Qt::DropActions supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; }

    Qt::DropAction dropAction_result;
public:
    DnDTestModel() : QStandardItemModel(20, 20), dropAction_result(Qt::IgnoreAction) {
        for (int i = 0; i < rowCount(); ++i)
            setData(index(i, 0), QString::number(i));
    }
    Qt::DropAction dropAction() const { return dropAction_result; }
};

class DnDTestView : public QTreeView
{
    Q_OBJECT

    QPoint dropPoint;
    Qt::DropAction dropAction;

    void dragEnterEvent(QDragEnterEvent *event)
    {
        QAbstractItemView::dragEnterEvent(event);
    }

    void dropEvent(QDropEvent *event)
    {
        event->setDropAction(dropAction);
        QTreeView::dropEvent(event);
    }

    void timerEvent(QTimerEvent *event)
    {
        killTimer(event->timerId());
        sendMouseMove(this, dropPoint);
        sendMouseRelease(this);
    }

    void mousePressEvent(QMouseEvent *e)
    {
        QTreeView::mousePressEvent(e);

        startTimer(0);
        setState(DraggingState);
        startDrag(dropAction);
    }

public:
    DnDTestView(Qt::DropAction dropAction, QAbstractItemModel *model)
        : dropAction(dropAction)
    {
        header()->hide();
        setModel(model);
        setDragDropMode(QAbstractItemView::DragDrop);
        setAcceptDrops(true);
        setDragEnabled(true);
    }

    void dragAndDrop(QPoint drag, QPoint drop)
    {
        dropPoint = drop;
        setCurrentIndex(indexAt(drag));
        sendMousePress(viewport(), drag);
    }
};

class DnDTestWidget : public QWidget
{
    Q_OBJECT

    Qt::DropAction dropAction_request;
    Qt::DropAction dropAction_result;
    QWidget *dropTarget;

    void timerEvent(QTimerEvent *event)
    {
        killTimer(event->timerId());
        sendMouseMove(dropTarget);
        sendMouseRelease(dropTarget);
    }

    void mousePressEvent(QMouseEvent *)
    {
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
        mimeData->setData("application/x-qabstractitemmodeldatalist", QByteArray(""));
        drag->setMimeData(mimeData);
        startTimer(0);
        dropAction_result = drag->start(dropAction_request);
    }

public:
    Qt::DropAction dropAction() const { return dropAction_result; }

    void dragAndDrop(QWidget *dropTarget, Qt::DropAction dropAction)
    {
        this->dropTarget = dropTarget;
        dropAction_request = dropAction;
        sendMousePress(this);
    }
};

void tst_QAbstractItemView::dragAndDrop()
{
    // From Task 137729


    const int attempts = 10;
    int successes = 0;
    for (int i = 0; i < attempts; ++i) {
        Qt::DropAction dropAction = Qt::MoveAction;

        DnDTestModel model;
        DnDTestView view(dropAction, &model);
        DnDTestWidget widget;

        const int size = 200;
        widget.setFixedSize(size, size);
        view.setFixedSize(size, size);

        widget.move(0, 0);
        view.move(int(size * 1.5), int(size * 1.5));

        widget.show();
        view.show();
        QVERIFY(QTest::qWaitForWindowExposed(&widget));
        QVERIFY(QTest::qWaitForWindowExposed(&view));

        widget.dragAndDrop(&view, dropAction);
        if (model.dropAction() == dropAction
            && widget.dropAction() == dropAction)
            ++successes;
    }

    if (successes < attempts) {
        QString msg = QString("# successes (%1) < # attempts (%2)").arg(successes).arg(attempts);
        QWARN(msg.toLatin1());
    }
    QVERIFY(successes > 0); // allow for some "event unstability" (i.e. unless
                            // successes == 0, QAbstractItemView is probably ok!)
}

void tst_QAbstractItemView::dragAndDropOnChild()
{

    const int attempts = 10;
    int successes = 0;
    for (int i = 0; i < attempts; ++i) {
        Qt::DropAction dropAction = Qt::MoveAction;

        DnDTestModel model;
        QModelIndex parent = model.index(0, 0);
        model.insertRow(0, parent);
        model.insertColumn(0, parent);
        QModelIndex child = model.index(0, 0, parent);
        model.setData(child, "child");
        QCOMPARE(model.rowCount(parent), 1);
        DnDTestView view(dropAction, &model);
        view.setExpanded(parent, true);
        view.setDragDropMode(QAbstractItemView::InternalMove);

        const int size = 200;
        view.setFixedSize(size, size);
        view.move(int(size * 1.5), int(size * 1.5));
        view.show();
        QVERIFY(QTest::qWaitForWindowExposed(&view));

        view.dragAndDrop(view.visualRect(parent).center(),
                         view.visualRect(child).center());
        if (model.dropAction() == dropAction)
            ++successes;
    }

    QCOMPARE(successes, 0);
}

#endif // 0
#endif // !Q_OS_MAC && !Q_OS_WIN

class TestModel : public QStandardItemModel
{
public:
    TestModel(int rows, int columns) : QStandardItemModel(rows, columns)
    {
        setData_count = 0;
    }

    virtual bool setData(const QModelIndex &/*index*/, const QVariant &/*value*/, int /*role = Qt::EditRole*/)
    {
        ++setData_count;
        return true;
    }

    int setData_count;
};

typedef QList<int> IntList;

void tst_QAbstractItemView::setItemDelegate_data()
{
    // default is rows, a -1 will switch to columns
    QTest::addColumn<IntList>("rowsOrColumnsWithDelegate");
    QTest::addColumn<QPoint>("cellToEdit");
    QTest::newRow("4 columndelegates")
                << (IntList() << -1 << 0 << 1 << 2 << 3)
                << QPoint(0, 0);
    QTest::newRow("2 identical rowdelegates on the same row")
                << (IntList() << 0 << 0)
                << QPoint(0, 0);
    QTest::newRow("2 identical columndelegates on the same column")
                << (IntList() << -1 << 2 << 2)
                << QPoint(2, 0);
    QTest::newRow("2 duplicate delegates, 1 row and 1 column")
                << (IntList() << 0 << -1 << 2)
                << QPoint(2, 0);
    QTest::newRow("4 duplicate delegates, 2 row and 2 column")
                << (IntList() << 0 << 0 << -1 << 2 << 2)
                << QPoint(2, 0);

}

void tst_QAbstractItemView::setItemDelegate()
{
    QFETCH(IntList, rowsOrColumnsWithDelegate);
    QFETCH(QPoint, cellToEdit);
    QTableView v;
    QItemDelegate *delegate = new QItemDelegate(&v);
    TestModel model(5, 5);
    v.setModel(&model);

    bool row = true;
    foreach (int rc, rowsOrColumnsWithDelegate) {
        if (rc == -1) {
            row = !row;
        } else {
            if (row) {
                v.setItemDelegateForRow(rc, delegate);
            } else {
                v.setItemDelegateForColumn(rc, delegate);
            }
        }
    }
    centerOnScreen(&v);
    moveCursorAway(&v);
    v.show();
#if 0 // Used to be included in Qt4 for Q_WS_X11
    QCursor::setPos(v.geometry().center());
#endif
    QApplication::setActiveWindow(&v);
    QVERIFY(QTest::qWaitForWindowActive(&v));

    QModelIndex index = model.index(cellToEdit.y(), cellToEdit.x());
    v.edit(index);

    // This will close the editor
    QTRY_VERIFY(QApplication::focusWidget());
    QWidget *editor = QApplication::focusWidget();
    QVERIFY(editor);
    editor->hide();
    delete editor;
    QCOMPARE(model.setData_count, 1);
    delete delegate;
}

void tst_QAbstractItemView::noFallbackToRoot()
{
    QStandardItemModel model(0, 1);
    for (int i = 0; i < 5; ++i)
        model.appendRow(new QStandardItem("top" + QString::number(i)));
    QStandardItem *par1 = model.item(1);
    for (int j = 0; j < 15; ++j)
        par1->appendRow(new QStandardItem("sub" + QString::number(j)));
    QStandardItem *par2 = par1->child(2);
    for (int k = 0; k < 10; ++k)
        par2->appendRow(new QStandardItem("bot" + QString::number(k)));
    QStandardItem *it1 = par2->child(5);

    QModelIndex parent1 = model.indexFromItem(par1);
    QModelIndex parent2 = model.indexFromItem(par2);
    QModelIndex item1 = model.indexFromItem(it1);

    QTreeView v;
    v.setModel(&model);
    v.setRootIndex(parent1);
    v.setCurrentIndex(item1);
    QCOMPARE(v.currentIndex(), item1);
    QVERIFY(model.removeRows(0, 10, parent2));
    QCOMPARE(v.currentIndex(), parent2);
    QVERIFY(model.removeRows(0, 15, parent1));
    QCOMPARE(v.currentIndex(), QModelIndex());
}

void tst_QAbstractItemView::setCurrentIndex_data()
{
    QTest::addColumn<QString>("viewType");
    QTest::addColumn<int>("itemFlags");
    QTest::addColumn<bool>("result");

    QStringList widgets;
    widgets << "QListView" << "QTreeView" << "QHeaderView" << "QTableView";

    foreach(QString widget, widgets) {
        QTest::newRow((widget+QLatin1String(": no flags")).toLocal8Bit().constData())
            << widget << (int)0 << false;
        QTest::newRow((widget+QLatin1String(": checkable")).toLocal8Bit().constData())
            << widget << (int)Qt::ItemIsUserCheckable << false;
        QTest::newRow((widget+QLatin1String(": selectable")).toLocal8Bit().constData())
            << widget << (int)Qt::ItemIsSelectable << false;
        QTest::newRow((widget+QLatin1String(": enabled")).toLocal8Bit().constData())
            << widget << (int)Qt::ItemIsEnabled << true;
        QTest::newRow((widget+QLatin1String(": enabled|selectable")).toLocal8Bit().constData())
            << widget << (int)(Qt::ItemIsSelectable|Qt::ItemIsEnabled) << true;
    }
}

void tst_QAbstractItemView::setCurrentIndex()
{
    QFETCH(QString, viewType);
    QFETCH(int, itemFlags);
    QFETCH(bool, result);

    QScopedPointer<QAbstractItemView> view;

    if (viewType == "QListView")
        view.reset(new QListView());
    else if (viewType == "QTableView")
        view.reset(new QTableView());
    else if (viewType == "QTreeView")
        view.reset(new QTreeView());
    else if (viewType == "QHeaderView")
        view.reset(new QHeaderView(Qt::Vertical));
    else
        QVERIFY(0);

    centerOnScreen(view.data());
    moveCursorAway(view.data());
    view->show();
    QVERIFY(QTest::qWaitForWindowExposed(view.data()));

    QStandardItemModel *model = new QStandardItemModel(view.data());
    QStandardItem *item = new QStandardItem("first item");
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    model->appendRow(item);

    item = new QStandardItem("test item");
    item->setFlags(Qt::ItemFlags(itemFlags));
    model->appendRow(item);

    view->setModel(model);

    view->setCurrentIndex(model->index(0,0));
    QCOMPARE(view->currentIndex(), model->index(0,0));
    view->setCurrentIndex(model->index(1,0));
    QVERIFY(view->currentIndex() == model->index(result ? 1 : 0,0));
}

void tst_QAbstractItemView::task221955_selectedEditor()
{
    QPushButton *button;

    QTreeWidget tree;
    tree.setColumnCount(2);

    tree.addTopLevelItem(new QTreeWidgetItem(QStringList() << "Foo" <<"1"));
    tree.addTopLevelItem(new QTreeWidgetItem(QStringList() << "Bar" <<"2"));
    tree.addTopLevelItem(new QTreeWidgetItem(QStringList() << "Baz" <<"3"));

    QTreeWidgetItem *dummy = new QTreeWidgetItem();
    tree.addTopLevelItem(dummy);
    tree.setItemWidget(dummy, 0, button = new QPushButton("More..."));
    button->setAutoFillBackground(true);  // as recommended in doc

    centerOnScreen(&tree);
    moveCursorAway(&tree);
    tree.show();
    tree.setFocus();
    tree.setCurrentIndex(tree.model()->index(1,0));
    QApplication::setActiveWindow(&tree);
    QVERIFY(QTest::qWaitForWindowActive(&tree));

    QVERIFY(! tree.selectionModel()->selectedIndexes().contains(tree.model()->index(3,0)));

    //We set the focus to the button, the index need to be selected
    button->setFocus();
    QTest::qWait(100);
    QVERIFY(tree.selectionModel()->selectedIndexes().contains(tree.model()->index(3,0)));

    tree.setCurrentIndex(tree.model()->index(1,0));
    QVERIFY(! tree.selectionModel()->selectedIndexes().contains(tree.model()->index(3,0)));

    //Same thing but with the flag NoSelection,   nothing can be selected.
    tree.setFocus();
    tree.setSelectionMode(QAbstractItemView::NoSelection);
    tree.clearSelection();
    QVERIFY(tree.selectionModel()->selectedIndexes().isEmpty());
    QTest::qWait(10);
    button->setFocus();
    QTest::qWait(50);
    QVERIFY(tree.selectionModel()->selectedIndexes().isEmpty());
}

void tst_QAbstractItemView::task250754_fontChange()
{
    QString app_css = qApp->styleSheet();
    qApp->setStyleSheet("/*  */");

    QWidget w;
    QTreeView tree(&w);
    QVBoxLayout *vLayout = new QVBoxLayout(&w);
    vLayout->addWidget(&tree);

    QStandardItemModel *m = new QStandardItemModel(this);
    for (int i=0; i<20; ++i) {
        QStandardItem *item = new QStandardItem(QStringLiteral("Item number ") + QString::number(i));
        for (int j=0; j<5; ++j) {
            QStandardItem *child = new QStandardItem(QStringLiteral("Child Item number ") + QString::number(j));
            item->setChild(j, 0, child);
        }
        m->setItem(i, 0, item);
    }
    tree.setModel(m);

    tree.setHeaderHidden(true); // The header is (in certain styles) dpi dependent
    w.resize(160, 350); // Minimum width for windows with frame on Windows 8
    centerOnScreen(&w);
    moveCursorAway(&w);
    w.showNormal();
    QVERIFY(QTest::qWaitForWindowExposed(&w));
    QFont font = tree.font();
    font.setPixelSize(10);
    tree.setFont(font);
    QTRY_VERIFY(!tree.verticalScrollBar()->isVisible());

    font.setPixelSize(60);
    tree.setFont(font);
    //now with the huge items, the scrollbar must be visible
    QTRY_VERIFY(tree.verticalScrollBar()->isVisible());

    qApp->setStyleSheet(app_css);
}

void tst_QAbstractItemView::task200665_itemEntered()
{
    //we test that view will emit entered
    //when the scrollbar move but not the mouse itself
    QStandardItemModel model(1000,1);
    QListView view;
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    QCursor::setPos( view.geometry().center() );
    QCoreApplication::processEvents();
    QSignalSpy spy(&view, SIGNAL(entered(QModelIndex)));
    view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum());

    QTRY_COMPARE(spy.count(), 1);
}

void tst_QAbstractItemView::task257481_emptyEditor()
{
    QIcon icon = qApp->style()->standardIcon(QStyle::SP_ComputerIcon);

    QStandardItemModel model;

    model.appendRow( new QStandardItem(icon, QString()) );
    model.appendRow( new QStandardItem(icon, "Editor works") );
    model.appendRow( new QStandardItem( QString() ) );

    QTreeView treeView;
    treeView.setRootIsDecorated(false);
    treeView.setModel(&model);
    centerOnScreen(&treeView);
    moveCursorAway(&treeView);
    treeView.show();
    QVERIFY(QTest::qWaitForWindowExposed(&treeView));

    treeView.edit(model.index(0,0));
    QList<QLineEdit *> lineEditors = treeView.viewport()->findChildren<QLineEdit *>();
    QCOMPARE(lineEditors.count(), 1);
    QVERIFY(!lineEditors.first()->size().isEmpty());

    QTest::qWait(30);

    treeView.edit(model.index(1,0));
    lineEditors = treeView.viewport()->findChildren<QLineEdit *>();
    QCOMPARE(lineEditors.count(), 1);
    QVERIFY(!lineEditors.first()->size().isEmpty());

    QTest::qWait(30);

    treeView.edit(model.index(2,0));
    lineEditors = treeView.viewport()->findChildren<QLineEdit *>();
    QCOMPARE(lineEditors.count(), 1);
    QVERIFY(!lineEditors.first()->size().isEmpty());
}

void tst_QAbstractItemView::shiftArrowSelectionAfterScrolling()
{
    QStandardItemModel model;
    for (int i=0; i<10; ++i) {
        QStandardItem *item = new QStandardItem(QString::number(i));
        model.setItem(i, 0, item);
    }

    QListView view;
    view.setFixedSize(160, 250); // Minimum width for windows with frame on Windows 8
    view.setFlow(QListView::LeftToRight);
    view.setGridSize(QSize(100, 100));
    view.setSelectionMode(QListView::ExtendedSelection);
    view.setViewMode(QListView::IconMode);
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QModelIndex index0 = model.index(0, 0);
    QModelIndex index1 = model.index(1, 0);
    QModelIndex index9 = model.index(9, 0);

    view.selectionModel()->setCurrentIndex(index0, QItemSelectionModel::NoUpdate);
    QCOMPARE(view.currentIndex(), index0);

    view.scrollTo(index9);
    QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier);

    QCOMPARE(view.currentIndex(), index1);
    QModelIndexList selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(index0));
    QVERIFY(selected.contains(index1));
}

void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection()
{
    QStandardItemModel model;
    for (int i=0; i<3; ++i) {
        QStandardItem *item = new QStandardItem(QString::number(i));
        model.setItem(i, 0, item);
    }

    QListView view;
    view.setFixedSize(160, 450); // Minimum width for windows with frame on Windows 8
    view.setFlow(QListView::LeftToRight);
    view.setGridSize(QSize(100, 100));
    view.setSelectionMode(QListView::ExtendedSelection);
    view.setViewMode(QListView::IconMode);
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QModelIndex index0 = model.index(0, 0);
    QModelIndex index1 = model.index(1, 0);
    QModelIndex index2 = model.index(2, 0);

    view.setCurrentIndex(index0);
    QCOMPARE(view.currentIndex(), index0);

    // Determine the points where the rubberband selection starts and ends
    QPoint pressPos = view.visualRect(index1).bottomRight() + QPoint(1, 1);
    QPoint releasePos = view.visualRect(index1).center();
    QVERIFY(!view.indexAt(pressPos).isValid());
    QCOMPARE(view.indexAt(releasePos), index1);

    // Select item 1 using a rubberband selection
    // The mouse move event has to be created manually because the QTest framework does not
    // contain a function for mouse moves with buttons pressed
    QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos);
    QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
    bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
    QVERIFY(moveEventReceived);
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos);
    QCOMPARE(view.currentIndex(), index1);

    // Shift-click item 2
    QPoint item2Pos = view.visualRect(index2).center();
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, item2Pos);
    QCOMPARE(view.currentIndex(), index2);

    // Verify that the selection worked OK
    QModelIndexList selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(index1));
    QVERIFY(selected.contains(index2));

    // Select item 0 to revert the selection
    view.setCurrentIndex(index0);
    QCOMPARE(view.currentIndex(), index0);

    // Repeat the same steps as above, but with a Shift-Arrow selection
    QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos);
    QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
    moveEventReceived = qApp->notify(view.viewport(), &moveEvent2);
    QVERIFY(moveEventReceived);
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos);
    QCOMPARE(view.currentIndex(), index1);

    // Press Shift-Down
    QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier);
    QCOMPARE(view.currentIndex(), index2);

    // Verify that the selection worked OK
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(index1));
    QVERIFY(selected.contains(index2));
}

void tst_QAbstractItemView::ctrlRubberbandSelection()
{
    QStandardItemModel model;
    for (int i=0; i<3; ++i) {
        QStandardItem *item = new QStandardItem(QString::number(i));
        model.setItem(i, 0, item);
    }

    QListView view;
    view.setFixedSize(160, 450); // Minimum width for windows with frame on Windows 8
    view.setFlow(QListView::LeftToRight);
    view.setGridSize(QSize(100, 100));
    view.setSelectionMode(QListView::ExtendedSelection);
    view.setViewMode(QListView::IconMode);
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    QModelIndex index1 = model.index(1, 0);
    QModelIndex index2 = model.index(2, 0);

    // Select item 1
    view.setCurrentIndex(index1);
    QModelIndexList selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(index1));

    // Now press control and draw a rubberband around items 1 and 2.
    // The mouse move event has to be created manually because the QTest framework does not
    // contain a function for mouse moves with buttons pressed.
    QPoint pressPos = view.visualRect(index1).topLeft() - QPoint(1, 1);
    QPoint releasePos = view.visualRect(index2).bottomRight() + QPoint(1, 1);
    QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pressPos);
    QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier);
    bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
    QVERIFY(moveEventReceived);
    QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, releasePos);

    // Verify that item 2 is selected now
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(index2));
}

void tst_QAbstractItemView::QTBUG6407_extendedSelection()
{
    QListWidget view;
    view.setSelectionMode(QAbstractItemView::ExtendedSelection);
    for(int i = 0; i < 50; ++i)
        view.addItem(QString::number(i));

    QFont font = view.font();
    font.setPixelSize(10);
    view.setFont(font);
    view.resize(200,240);
    centerOnScreen(&view);
    moveCursorAway(&view);

    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow());

    view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum());
    QTest::qWait(20);

    QModelIndex index49 = view.model()->index(49,0);
    QPoint p = view.visualRect(index49).center();
    QVERIFY(view.viewport()->rect().contains(p));
    QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, p);
    QCOMPARE(view.currentIndex(), index49);
    QCOMPARE(view.selectedItems().count(), 1);

    QModelIndex index47 = view.model()->index(47,0);
    p = view.visualRect(index47).center();
    QVERIFY(view.viewport()->rect().contains(p));
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p);
    QCOMPARE(view.currentIndex(), index47);
    QCOMPARE(view.selectedItems().count(), 3); //49, 48, 47;

    QModelIndex index44 = view.model()->index(44,0);
    p = view.visualRect(index44).center();
    QVERIFY(view.viewport()->rect().contains(p));
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p);
    QCOMPARE(view.currentIndex(), index44);
    QCOMPARE(view.selectedItems().count(), 6); //49 .. 44;

}

void tst_QAbstractItemView::QTBUG6753_selectOnSelection()
{
    QTableWidget table(5, 5);
    for (int i = 0; i < table.rowCount(); ++i)
        for (int j = 0; j < table.columnCount(); ++j)
            table.setItem(i, j, new QTableWidgetItem("choo-be-doo-wah"));

    centerOnScreen(&table);
    moveCursorAway(&table);
    table.show();
    QVERIFY(QTest::qWaitForWindowExposed(&table));

    table.setSelectionMode(QAbstractItemView::ExtendedSelection);
    table.selectAll();
    QVERIFY(QTest::qWaitForWindowExposed(&table));
    QModelIndex item = table.model()->index(1,1);
    QRect itemRect = table.visualRect(item);
    QTest::mouseMove(table.viewport(), itemRect.center());
    QTest::mouseClick(table.viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());
    QTest::qWait(20);

    QCOMPARE(table.selectedItems().count(), 1);
    QCOMPARE(table.selectedItems().first(), table.item(item.row(), item.column()));
}

void tst_QAbstractItemView::testDelegateDestroyEditor()
{
    QTableWidget table(5, 5);
    MyAbstractItemDelegate delegate;
    table.setItemDelegate(&delegate);
    table.edit(table.model()->index(1, 1));
    QAbstractItemView *tv = &table;
    QVERIFY(!delegate.calledVirtualDtor);
    tv->closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint);
    QVERIFY(delegate.calledVirtualDtor);
}

void tst_QAbstractItemView::testClickedSignal()
{
    QTableWidget view(5, 5);

    centerOnScreen(&view);
    moveCursorAway(&view);
    view.showNormal();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow());

    QModelIndex index49 = view.model()->index(49,0);
    QPoint p = view.visualRect(index49).center();
    QVERIFY(view.viewport()->rect().contains(p));

    QSignalSpy clickedSpy(&view, SIGNAL(clicked(QModelIndex)));

    QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, p);
    QCOMPARE(clickedSpy.count(), 1);

    QTest::mouseClick(view.viewport(), Qt::RightButton, 0, p);
    // We expect that right-clicks do not cause the clicked() signal to
    // be emitted.
    QCOMPARE(clickedSpy.count(), 1);

}

class StateChangeDelegate : public QItemDelegate {
  Q_OBJECT

public:
  explicit StateChangeDelegate(QObject* parent = 0) :
    QItemDelegate(parent)
  {}

  void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE {
      Q_UNUSED(index);
      static bool w = true;
      editor->setEnabled(w);
      w = !w;
  }
};

class StateChangeModel : public QStandardItemModel {
  Q_OBJECT

public:
  explicit StateChangeModel(QObject *parent = 0) :
    QStandardItemModel(parent)
  {}

  void emitDataChanged() {
    emit dataChanged(index(0, 0), index(0, 1));
  }
};


void tst_QAbstractItemView::testChangeEditorState()
{
    // Test for QTBUG-25370
    StateChangeModel model;
    {
      QStandardItem* item = new QStandardItem("a");
      model.setItem(0, 0, item);
    }
    {
      QStandardItem* item = new QStandardItem("b");
      model.setItem(0, 1, item);
    }

    QTableView view;
    view.setEditTriggers(QAbstractItemView::CurrentChanged);
    view.setItemDelegate(new StateChangeDelegate(&view));
    view.setModel(&model);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowActive(&view));
    QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow());

    model.emitDataChanged();
    // No segfault - the test passes.
}

void tst_QAbstractItemView::deselectInSingleSelection()
{
    QTableView view;
    QStandardItemModel s;
    s.setRowCount(10);
    s.setColumnCount(10);
    view.setModel(&s);
    centerOnScreen(&view);
    moveCursorAway(&view);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    view.setSelectionMode(QAbstractItemView::SingleSelection);
    view.setEditTriggers(QAbstractItemView::NoEditTriggers);
    QApplication::setActiveWindow(&view);
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    // mouse
    QModelIndex index22 = s.index(2, 2);
    QRect rect22 = view.visualRect(index22);
    QPoint clickpos = rect22.center();
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, clickpos);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0);

    // second click with modifier however does select
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);

    // keyboard
    QTest::keyClick(&view, Qt::Key_Space, Qt::NoModifier);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
    QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0);

    // second keypress with modifier however does select
    QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier);
    QCOMPARE(view.currentIndex(), index22);
    QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
}

void tst_QAbstractItemView::testNoActivateOnDisabledItem()
{
    QTreeView treeView;
    QStandardItemModel model(1, 1);
    QStandardItem *item = new QStandardItem("item");
    model.setItem(0, 0, item);
    item->setFlags(Qt::NoItemFlags);
    treeView.setModel(&model);
    centerOnScreen(&treeView);
    moveCursorAway(&treeView);
    treeView.show();

    QApplication::setActiveWindow(&treeView);
    QVERIFY(QTest::qWaitForWindowActive(&treeView));

    QSignalSpy activatedSpy(&treeView, SIGNAL(activated(QModelIndex)));

    // Ensure clicking on a disabled item doesn't emit itemActivated.
    QModelIndex itemIndex = treeView.model()->index(0, 0);
    QPoint clickPos = treeView.visualRect(itemIndex).center();
    QTest::mouseClick(treeView.viewport(), Qt::LeftButton, 0, clickPos);

    QCOMPARE(activatedSpy.count(), 0);
}

void tst_QAbstractItemView::testFocusPolicy_data()
{
    QTest::addColumn<QAbstractItemDelegate*>("delegate");

    QAbstractItemDelegate *styledItemDelegate = new QStyledItemDelegate(this);
    QAbstractItemDelegate *itemDelegate = new QItemDelegate(this);

    QTest::newRow("QStyledItemDelegate") << styledItemDelegate;
    QTest::newRow("QItemDelegate") << itemDelegate;
}

void tst_QAbstractItemView::testFocusPolicy()
{
    QFETCH(QAbstractItemDelegate*, delegate);

    QWidget window;
    QTableView *table = new QTableView(&window);
    table->setItemDelegate(delegate);
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(table);

    QStandardItemModel model;
    model.setRowCount(10);
    model.setColumnCount(10);
    table->setModel(&model);
    table->setCurrentIndex(model.index(1, 1));

    centerOnScreen(&window);
    moveCursorAway(&window);

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

    // itemview accepts focus => editor is closed => return focus to the itemview
    QPoint clickpos = table->visualRect(model.index(1, 1)).center();
    QTest::mouseDClick(table->viewport(), Qt::LeftButton, Qt::NoModifier, clickpos);
    QWidget *editor = qApp->focusWidget();
    QVERIFY(editor);
    QTest::keyClick(editor, Qt::Key_Escape, Qt::NoModifier);
    QCOMPARE(qApp->focusWidget(), table);

    // itemview doesn't accept focus => editor is closed => clear the focus
    table->setFocusPolicy(Qt::NoFocus);
    QTest::mouseDClick(table->viewport(), Qt::LeftButton, Qt::NoModifier, clickpos);
    editor = qApp->focusWidget();
    QVERIFY(editor);
    QTest::keyClick(editor, Qt::Key_Escape, Qt::NoModifier);
    QVERIFY(!qApp->focusWidget());
}

void tst_QAbstractItemView::QTBUG31411_noSelection()
{
    QWidget window;
    QTableView *table = new QTableView(&window);
    table->setSelectionMode(QAbstractItemView::NoSelection);
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(table);

    QStandardItemModel model;
    model.setRowCount(10);
    model.setColumnCount(10);
    table->setModel(&model);
    table->setCurrentIndex(model.index(1, 1));

    centerOnScreen(&window);
    moveCursorAway(&window);

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

    qRegisterMetaType<QItemSelection>();
    QSignalSpy selectionChangeSpy(table->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)));
    QVERIFY(selectionChangeSpy.isValid());

    QPoint clickpos = table->visualRect(model.index(1, 1)).center();
    QTest::mouseClick(table->viewport(), Qt::LeftButton, Qt::NoModifier, clickpos);
    QTest::mouseDClick(table->viewport(), Qt::LeftButton, Qt::NoModifier, clickpos);

    QPointer<QWidget> editor1 = qApp->focusWidget();
    QVERIFY(editor1);
    QTest::keyClick(editor1, Qt::Key_Tab, Qt::NoModifier);

    QPointer<QWidget> editor2 = qApp->focusWidget();
    QVERIFY(editor2);
    QTest::keyClick(editor2, Qt::Key_Escape, Qt::NoModifier);

    QCOMPARE(selectionChangeSpy.count(), 0);
}

void tst_QAbstractItemView::QTBUG39324_settingSameInstanceOfIndexWidget()
{
    QStringList list;
    list << "FOO" << "bar";
    QScopedPointer<QStringListModel> model(new QStringListModel(list));

    QScopedPointer<QTableView> table(new QTableView());
    table->setModel(model.data());

    QModelIndex index = model->index(0,0);
    QLineEdit *lineEdit = new QLineEdit();
    table->setIndexWidget(index, lineEdit);
    table->setIndexWidget(index, lineEdit);
    QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
    table->show();
}

void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents()
{
    QStringList list;
    list << "A" << "B" << "C" << "D" << "E" << "F";
    QStringListModel model(list);
    QSortFilterProxyModel proxyModel;
    proxyModel.setSourceModel(&model);
    proxyModel.sort(0, Qt::AscendingOrder);
    proxyModel.setDynamicSortFilter(true);

    QPersistentModelIndex indexA(proxyModel.index(0, 0));
    QPersistentModelIndex indexB(proxyModel.index(1, 0));
    QPersistentModelIndex indexC(proxyModel.index(2, 0));
    QPersistentModelIndex indexD(proxyModel.index(3, 0));
    QPersistentModelIndex indexE(proxyModel.index(4, 0));
    QPersistentModelIndex indexF(proxyModel.index(5, 0));

    QListView view;
    view.setModel(&proxyModel);
    view.setSelectionMode(QAbstractItemView::ExtendedSelection);
    view.show();
    QTest::qWaitForWindowExposed(&view);

    // Click "C"
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexC).center());
    QModelIndexList selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(indexC));

    // Insert new item "B1"
    model.insertRow(0);
    model.setData(model.index(0, 0), "B1");

    // Shift-click "D" -> we expect that "C" and "D" are selected
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexD).center());
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(indexC));
    QVERIFY(selected.contains(indexD));

    // Click "D"
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center());
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(indexD));

    // Remove items "B" and "C"
    model.removeRows(proxyModel.mapToSource(indexB).row(), 1);
    model.removeRows(proxyModel.mapToSource(indexC).row(), 1);
    QVERIFY(!indexB.isValid());
    QVERIFY(!indexC.isValid());

    // Shift-click "F" -> we expect that "D", "E", and "F" are selected
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center());
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 3);
    QVERIFY(selected.contains(indexD));
    QVERIFY(selected.contains(indexE));
    QVERIFY(selected.contains(indexF));

    // Move to "A" by pressing "Up" repeatedly
    while (view.currentIndex() != indexA) {
        QTest::keyClick(&view, Qt::Key_Up);
    }
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(indexA));

    // Change the sort order
    proxyModel.sort(0, Qt::DescendingOrder);

    // Shift-click "F" -> All items should be selected
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center());
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), model.rowCount());

    // Restore the old sort order
    proxyModel.sort(0, Qt::AscendingOrder);

    // Click "D"
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center());
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(indexD));

    // Insert new item "B2"
    model.insertRow(0);
    model.setData(model.index(0, 0), "B2");

    // Press Shift+Down -> "D" and "E" should be selected.
    QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier);
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(indexD));
    QVERIFY(selected.contains(indexE));

    // Click "A" to reset the current selection starting point;
    //then select "D" via QAbstractItemView::setCurrentIndex(const QModelIndex&).
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexA).center());
    view.setCurrentIndex(indexD);
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 1);
    QVERIFY(selected.contains(indexD));

    // Insert new item "B3"
    model.insertRow(0);
    model.setData(model.index(0, 0), "B3");

    // Press Shift+Down -> "D" and "E" should be selected.
    QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier);
    selected = view.selectionModel()->selectedIndexes();
    QCOMPARE(selected.count(), 2);
    QVERIFY(selected.contains(indexD));
    QVERIFY(selected.contains(indexE));
}

void tst_QAbstractItemView::QTBUG48968_reentrant_updateEditorGeometries()
{

    QStandardItemModel *m = new QStandardItemModel(this);
    for (int i=0; i<10; ++i) {
        QStandardItem *item = new QStandardItem(QString("Item number %1").arg(i));
        item->setEditable(true);
        for (int j=0; j<5; ++j) {
            QStandardItem *child = new QStandardItem(QString("Child Item number %1").arg(j));
            item->setChild(j, 0, child);
        }
        m->setItem(i, 0, item);
    }

    QTreeView tree;
    tree.setModel(m);
    tree.setRootIsDecorated(false);
    QObject::connect(&tree, SIGNAL(doubleClicked(QModelIndex)), &tree, SLOT(setRootIndex(QModelIndex)));
    tree.show();
    QTest::qWaitForWindowActive(&tree);

    // Trigger editing idx
    QModelIndex idx = m->index(1, 0);
    const QPoint pos = tree.visualRect(idx).center();
    QTest::mouseClick(tree.viewport(), Qt::LeftButton, Qt::NoModifier, pos);
    QTest::mouseDClick(tree.viewport(), Qt::LeftButton, Qt::NoModifier, pos);

    // Add more children to idx
    QStandardItem *item = m->itemFromIndex(idx);
    for (int j=5; j<10; ++j) {
        QStandardItem *child = new QStandardItem(QString("Child Item number %1").arg(j));
        item->setChild(j, 0, child);
    }

    // No crash, all fine.
}

class ScrollModeProxyStyle: public QProxyStyle
{
public:
    ScrollModeProxyStyle(QAbstractItemView::ScrollMode sm, QStyle *style = 0)
        : QProxyStyle(style)
        , scrollMode(sm == QAbstractItemView::ScrollPerItem ?
                     QAbstractItemView::ScrollPerPixel : QAbstractItemView::ScrollPerItem)
    { }

    int styleHint(QStyle::StyleHint hint, const QStyleOption *opt, const QWidget *w, QStyleHintReturn *returnData) const override
    {
        if (hint == SH_ItemView_ScrollMode)
            return scrollMode;

        return baseStyle()->styleHint(hint, opt, w, returnData);
    }

    QAbstractItemView::ScrollMode scrollMode;
};

void tst_QAbstractItemView::QTBUG50102_SH_ItemView_ScrollMode()
{
    QListView view;

    // Default comes from the style
    auto styleScrollMode = static_cast<QAbstractItemView::ScrollMode>(view.style()->styleHint(QStyle::SH_ItemView_ScrollMode, 0, &view, 0));
    QCOMPARE(view.verticalScrollMode(), styleScrollMode);
    QCOMPARE(view.horizontalScrollMode(), styleScrollMode);

    // Change style, get new value
    ScrollModeProxyStyle proxyStyle1(styleScrollMode);
    view.setStyle(&proxyStyle1);
    auto proxyScrollMode = static_cast<QAbstractItemView::ScrollMode>(view.style()->styleHint(QStyle::SH_ItemView_ScrollMode, 0, &view, 0));
    QVERIFY(styleScrollMode != proxyScrollMode);
    QCOMPARE(view.verticalScrollMode(), proxyScrollMode);
    QCOMPARE(view.horizontalScrollMode(), proxyScrollMode);

    // Explicitly set vertical, same value
    view.setVerticalScrollMode(proxyScrollMode);
    QCOMPARE(view.verticalScrollMode(), proxyScrollMode);
    QCOMPARE(view.horizontalScrollMode(), proxyScrollMode);

    // Change style, won't change value for vertical, will change for horizontal
    ScrollModeProxyStyle proxyStyle2(proxyScrollMode);
    view.setStyle(&proxyStyle2);
    QCOMPARE(view.verticalScrollMode(), proxyScrollMode);
    QCOMPARE(view.horizontalScrollMode(), styleScrollMode);
}

void tst_QAbstractItemView::QTBUG50535_update_on_new_selection_model()
{
    QStandardItemModel model;
    for (int i = 0; i < 10; ++i)
        model.appendRow(new QStandardItem(QStringLiteral("%1").arg(i)));

    class ListView : public QListView
    {
    public:
        ListView()
            : m_paintEventsCount(0), m_deselectedMustBeEmpty(false), m_selectionChangedOk(true)
        {
        }

        void setSelectionModel(QItemSelectionModel *model) Q_DECL_OVERRIDE
        {
            m_deselectedMustBeEmpty = !selectionModel() || !model || selectionModel()->model() != model->model();
            QListView::setSelectionModel(model);
            m_deselectedMustBeEmpty = false;
        }
        int m_paintEventsCount;
        bool selectionChangedOk() const { return m_selectionChangedOk; }

    protected:
        bool viewportEvent(QEvent *event) Q_DECL_OVERRIDE
        {
            if (event->type() == QEvent::Paint)
                ++m_paintEventsCount;
            return QListView::viewportEvent(event);
        }

        void selectionChanged(const QItemSelection &selected,
                              const QItemSelection &deselected) Q_DECL_OVERRIDE
        {
            if (m_deselectedMustBeEmpty && !deselected.isEmpty())
                m_selectionChangedOk = false;

            // Make sure both selections belong to the same model
            foreach (const QModelIndex &nmi, selected.indexes()) {
                foreach (const QModelIndex &omi, deselected.indexes()) {
                    m_selectionChangedOk = m_selectionChangedOk && (nmi.model() == omi.model());
                }
            }
            QListView::selectionChanged(selected, deselected);
        }
    private:
        bool m_deselectedMustBeEmpty;
        bool m_selectionChangedOk;
    };

    // keep the current/selected row in the "low range", i.e. be sure it's visible, otherwise we
    // don't get updates and the test fails.

    ListView view;
    view.setModel(&model);
    view.selectionModel()->setCurrentIndex(model.index(1, 0), QItemSelectionModel::SelectCurrent);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));
    QVERIFY(view.selectionChangedOk());

    QItemSelectionModel selectionModel(&model);
    selectionModel.setCurrentIndex(model.index(2, 0), QItemSelectionModel::Current);

    int oldPaintEventsCount = view.m_paintEventsCount;
    view.setSelectionModel(&selectionModel);
    QTRY_VERIFY(view.m_paintEventsCount > oldPaintEventsCount);
    QVERIFY(view.selectionChangedOk());


    QItemSelectionModel selectionModel2(&model);
    selectionModel2.select(model.index(0, 0), QItemSelectionModel::ClearAndSelect);
    selectionModel2.setCurrentIndex(model.index(1, 0), QItemSelectionModel::Current);

    oldPaintEventsCount = view.m_paintEventsCount;
    view.setSelectionModel(&selectionModel2);
    QTRY_VERIFY(view.m_paintEventsCount > oldPaintEventsCount);
    QVERIFY(view.selectionChangedOk());

    // Tests QAbstractItemView::selectionChanged
    QStandardItemModel model1;
    for (int i = 0; i < 10; ++i)
        model1.appendRow(new QStandardItem(QString::number(i)));
    view.setModel(&model1);

    QItemSelectionModel selectionModel1(&model1);
    selectionModel1.select(model1.index(0, 0), QItemSelectionModel::ClearAndSelect);
    selectionModel1.setCurrentIndex(model1.index(1, 0), QItemSelectionModel::Current);
    view.setSelectionModel(&selectionModel1);
    QVERIFY(view.selectionChangedOk());
}

void tst_QAbstractItemView::testSelectionModelInSyncWithView()
{
    QStandardItemModel model;
    for (int i = 0; i < 10; ++i)
        model.appendRow(new QStandardItem(QStringLiteral("%1").arg(i)));

    class ListView : public QListView
    {
    public:
        using QListView::selectedIndexes;
    };

    ListView view;
    QVERIFY(!view.selectionModel());

    view.setModel(&model);
    QVERIFY(view.selectionModel());
    QVERIFY(view.selectedIndexes().isEmpty());
    QVERIFY(view.selectionModel()->selection().isEmpty());

    view.setCurrentIndex(model.index(0, 0));
    QCOMPARE(view.currentIndex(), model.index(0, 0));
    QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0));

    view.selectionModel()->setCurrentIndex(model.index(1, 0), QItemSelectionModel::SelectCurrent);
    QCOMPARE(view.currentIndex(), model.index(1, 0));
    QCOMPARE(view.selectedIndexes(), QModelIndexList() << model.index(1, 0));
    QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0));
    QCOMPARE(view.selectionModel()->selection().indexes(), QModelIndexList() << model.index(1, 0));

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

    QItemSelectionModel selectionModel(&model);
    selectionModel.setCurrentIndex(model.index(2, 0), QItemSelectionModel::Current);

    view.setSelectionModel(&selectionModel);
    QCOMPARE(view.currentIndex(), model.index(2, 0));
    QCOMPARE(view.selectedIndexes(), QModelIndexList());
    QCOMPARE(view.selectionModel()->currentIndex(), model.index(2, 0));
    QCOMPARE(view.selectionModel()->selection().indexes(),  QModelIndexList());


    QItemSelectionModel selectionModel2(&model);
    selectionModel2.select(model.index(0, 0), QItemSelectionModel::ClearAndSelect);
    selectionModel2.setCurrentIndex(model.index(1, 0), QItemSelectionModel::Current);

    view.setSelectionModel(&selectionModel2);
    QCOMPARE(view.currentIndex(), model.index(1, 0));
    QCOMPARE(view.selectedIndexes(), QModelIndexList() << model.index(0, 0));
    QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0));
    QCOMPARE(view.selectionModel()->selection().indexes(),  QModelIndexList() << model.index(0, 0));
}

class SetSelectionTestView : public QListView
{
    Q_OBJECT
public:
    SetSelectionTestView() : QListView() {}

signals:
    void setSelectionCalled(const QRect &rect);

protected:
    void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) Q_DECL_OVERRIDE
    {
        emit setSelectionCalled(rect);
        QListView::setSelection(rect, flags);
    }
};

void tst_QAbstractItemView::testClickToSelect()
{
    // This test verifies that the QRect that is passed from QAbstractItemView::mousePressEvent
    // to the virtual method QAbstractItemView::setSelection(const QRect &, SelectionFlags)
    // is the 1x1 rect which conains exactly the clicked pixel if no modifiers are pressed.

    QStringList list;
    list << "A" << "B" << "C";
    QStringListModel model(list);

    SetSelectionTestView view;
    view.setModel(&model);
    view.show();
    QTest::qWaitForWindowExposed(&view);

    QSignalSpy spy(&view, &SetSelectionTestView::setSelectionCalled);

    const QModelIndex indexA(model.index(0, 0));
    const QRect visualRectA = view.visualRect(indexA);
    const QPoint centerA = visualRectA.center();

    // Click the center of the visualRect of item "A"
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, centerA);
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.back().front().value<QRect>(), QRect(centerA, QSize(1, 1)));

    // Click a point slightly away from the center
    const QPoint nearCenterA = centerA + QPoint(1, 1);
    QVERIFY(visualRectA.contains(nearCenterA));
    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, nearCenterA);
    QCOMPARE(spy.count(), 2);
    QCOMPARE(spy.back().front().value<QRect>(), QRect(nearCenterA, QSize(1, 1)));
}

void tst_QAbstractItemView::testDialogAsEditor()
{
    DialogItemDelegate delegate;

    QStandardItemModel model;
    model.appendRow(new QStandardItem(QStringLiteral("editme")));

    QListView view;
    view.setItemDelegate(&delegate);
    view.setModel(&model);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    view.edit(model.index(0,0));

    QVERIFY(QTest::qWaitForWindowExposed(delegate.openedEditor));

    delegate.openedEditor->reject();
    QApplication::processEvents();

    QCOMPARE(delegate.result, QDialog::Rejected);

    view.edit(model.index(0,0));

    QVERIFY(QTest::qWaitForWindowExposed(delegate.openedEditor));

    delegate.openedEditor->accept();
    QApplication::processEvents();

    QCOMPARE(delegate.result, QDialog::Accepted);
}

class HoverItemDelegate : public QItemDelegate
{
public:
    HoverItemDelegate()
        : QItemDelegate()
        , m_paintedWithoutHover(false)
    { }

    void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const override
    {
        Q_UNUSED(painter);

        if (!(opt.state & QStyle::State_MouseOver)) {

            // We don't want to set m_paintedWithoutHover for any item so check for the item at 0,0
            if (index.row() == 0 && index.column() == 0) {
                m_paintedWithoutHover = true;
            }
        }
    }

    mutable bool m_paintedWithoutHover;
};

void tst_QAbstractItemView::QTBUG46785_mouseout_hover_state()
{
    HoverItemDelegate delegate;

    QTableWidget table(5, 5);
    table.verticalHeader()->hide();
    table.horizontalHeader()->hide();
    table.setMouseTracking(true);
    table.setItemDelegate(&delegate);
    centerOnScreen(&table);
    table.show();
    QVERIFY(QTest::qWaitForWindowActive(&table));

    QModelIndex item = table.model()->index(0, 0);
    QRect itemRect = table.visualRect(item);

    // Move the mouse into the center of the item at 0,0 to cause a paint event to occur
    QTest::mouseMove(table.viewport(), itemRect.center());
    QTest::mouseClick(table.viewport(), Qt::LeftButton, 0, itemRect.center());

    delegate.m_paintedWithoutHover = false;

    QTest::mouseMove(table.viewport(), QPoint(-50, 0));

    QTRY_VERIFY(delegate.m_paintedWithoutHover);
}

void tst_QAbstractItemView::testClearModelInClickedSignal()
{
    QStringList list{"A", "B"};
    QStringListModel model(list);

    QListView view;
    view.setModel(&model);
    view.show();

    QWidget::connect(&view, &QListView::clicked, [&view](const QModelIndex &index)
    {
        view.setModel(nullptr);
        QCOMPARE(index.data().toString(), QStringLiteral("B"));
    });

    QModelIndex index = view.model()->index(1, 0);
    QVERIFY(index.isValid());
    QPoint p = view.visualRect(index).center();

    QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);

    QCOMPARE(view.model(), nullptr);
}

void tst_QAbstractItemView::inputMethodEnabled_data()
{
    QTest::addColumn<QByteArray>("viewType");
    QTest::addColumn<Qt::ItemFlags>("itemFlags");
    QTest::addColumn<bool>("result");

    QList<QByteArray> widgets;
    widgets << "QListView" << "QTreeView" << "QTableView";

    for (const QByteArray &widget : qAsConst(widgets)) {
        QTest::newRow(widget + ": no flags") << widget << Qt::ItemFlags(Qt::NoItemFlags) << false;
        QTest::newRow(widget + ": checkable") << widget << Qt::ItemFlags(Qt::ItemIsUserCheckable) << false;
        QTest::newRow(widget + ": selectable") << widget << Qt::ItemFlags(Qt::ItemIsSelectable) << false;
        QTest::newRow(widget + ": enabled") << widget << Qt::ItemFlags(Qt::ItemIsEnabled) << false;
        QTest::newRow(widget + ": selectable|enabled")
            << widget << Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled) << false;
        QTest::newRow(widget + ": editable|enabled")
            << widget << Qt::ItemFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled) << true;
        QTest::newRow(widget + ": editable|enabled|selectable")
            << widget << Qt::ItemFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable) << true;
    }
}

void tst_QAbstractItemView::inputMethodEnabled()
{
    QFETCH(QByteArray, viewType);
    QFETCH(Qt::ItemFlags, itemFlags);
    QFETCH(bool, result);

    QScopedPointer<QAbstractItemView> view;
    if (viewType == "QListView")
        view.reset(new QListView());
    else if (viewType == "QTableView")
        view.reset(new QTableView());
    else if (viewType == "QTreeView")
        view.reset(new QTreeView());
    else
        QVERIFY(0);

    centerOnScreen(view.data());
    view->show();
    QVERIFY(QTest::qWaitForWindowExposed(view.data()));

    QStandardItemModel *model = new QStandardItemModel(view.data());
    QStandardItem *item = new QStandardItem("first item");
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    model->appendRow(item);

    QStandardItem *secondItem = new QStandardItem("test item");
    secondItem->setFlags(Qt::ItemFlags(itemFlags));
    model->appendRow(secondItem);

    view->setModel(model);

    // Check current changed
    view->setCurrentIndex(model->index(0, 0));
    QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled));
    view->setCurrentIndex(model->index(1, 0));
    QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result);
    view->setCurrentIndex(model->index(0, 0));
    QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled));

    // Check focus by switching the activation of the window to force a focus in
    view->setCurrentIndex(model->index(1, 0));
    QApplication::setActiveWindow(0);
    QApplication::setActiveWindow(view.data());
    QVERIFY(QTest::qWaitForWindowActive(view.data()));
    QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result);

    view->setCurrentIndex(QModelIndex());
    QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled));
    QApplication::setActiveWindow(0);
    QApplication::setActiveWindow(view.data());
    QVERIFY(QTest::qWaitForWindowActive(view.data()));
    QModelIndex index = model->index(1, 0);
    QPoint p = view->visualRect(index).center();
    QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, p);
    if (itemFlags & Qt::ItemIsEnabled)
        QCOMPARE(view->currentIndex(), index);
    QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result);

    index = model->index(0, 0);
    QApplication::setActiveWindow(0);
    QApplication::setActiveWindow(view.data());
    QVERIFY(QTest::qWaitForWindowActive(view.data()));
    p = view->visualRect(index).center();
    QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, p);
    QCOMPARE(view->currentIndex(), index);
    QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled));

    // There is a case when it goes to the first visible item so we
    // make the flags of the first item match the ones we are testing
    // to check the attribute correctly
    QApplication::setActiveWindow(0);
    view->setCurrentIndex(QModelIndex());
    view->reset();
    item->setFlags(Qt::ItemFlags(itemFlags));
    QApplication::setActiveWindow(view.data());
    QVERIFY(QTest::qWaitForWindowActive(view.data()));
    QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result);
}

QTEST_MAIN(tst_QAbstractItemView)
#include "tst_qabstractitemview.moc"
