Add the QIdentityProxyModel.
authorStephen Kelly <stephen.kelly@kdab.com>
Wed, 10 Nov 2010 10:21:53 +0000 (11:21 +0100)
committerOlivier Goffart <olivier.goffart@nokia.com>
Wed, 11 May 2011 15:37:48 +0000 (17:37 +0200)
Older commit history is in KDE svn:
http://websvn.kde.org/trunk/KDE/kdelibs/kdeui/itemviews/kidentityproxymodel.cpp?view=log

Ammended to update the license headers.

Merge-request: 900
Reviewed-by: Gabriel de Dietrich <gabriel.dietrich-de@nokia.com>
Conflicts:

doc/src/frameworks-technologies/model-view-programming.qdoc
tests/auto/headers/tst_headers.cpp

(cherry picked from b00089261eafbdf5f92ed94d7fb20b402bfcaeb2)

doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp [new file with mode: 0644]
src/corelib/kernel/qabstractitemmodel.h
src/gui/itemviews/itemviews.pri
src/gui/itemviews/qidentityproxymodel.cpp [new file with mode: 0644]
src/gui/itemviews/qidentityproxymodel.h [new file with mode: 0644]
src/gui/itemviews/qsortfilterproxymodel.cpp
tests/auto/gui.pro
tests/auto/qidentityproxymodel/qidentityproxymodel.pro [new file with mode: 0644]
tests/auto/qidentityproxymodel/tst_qidentityproxymodel.cpp [new file with mode: 0644]

diff --git a/doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp b/doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp
new file mode 100644 (file)
index 0000000..8bd6520
--- /dev/null
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
+**        a KDAB Group company, info@kdab.com,
+**        author Stephen Kelly <stephen.kelly@kdab.com>
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+class DateFormatProxyModel : public QIdentityProxyModel
+{
+  // ...
+
+  void setDateFormatString(const QString &formatString)
+  {
+    m_formatString = formatString;
+  }
+
+  QVariant data(const QModelIndex &index, int role)
+  {
+    if (role != Qt::DisplayRole)
+      return QIdentityProxyModel::data(index, role);
+
+    const QDateTime dateTime = sourceModel()->data(SourceClass::DateRole).toDateTime();
+
+    return dateTime.toString(m_formatString);
+  }
+
+private:
+  QString m_formatString;
+};
+//! [0]
index 6de3bf5..7bed3a2 100644 (file)
@@ -162,6 +162,7 @@ class Q_CORE_EXPORT QAbstractItemModel : public QObject
 
     friend class QPersistentModelIndexData;
     friend class QAbstractItemViewPrivate;
+    friend class QIdentityProxyModel;
 public:
 
     explicit QAbstractItemModel(QObject *parent = 0);
index bbc1e98..149bfd6 100644 (file)
@@ -4,6 +4,7 @@ HEADERS += \
        itemviews/qabstractitemview.h \
     itemviews/qabstractitemview_p.h \
        itemviews/qheaderview.h \
+       itemviews/qidentityproxymodel.h \
        itemviews/qlistview.h \
        itemviews/qlistview_p.h \
        itemviews/qbsptree_p.h \
@@ -44,6 +45,7 @@ HEADERS += \
 SOURCES += \
        itemviews/qabstractitemview.cpp \
        itemviews/qheaderview.cpp \
+       itemviews/qidentityproxymodel.cpp \
        itemviews/qlistview.cpp \
        itemviews/qbsptree.cpp \
        itemviews/qtableview.cpp \
diff --git a/src/gui/itemviews/qidentityproxymodel.cpp b/src/gui/itemviews/qidentityproxymodel.cpp
new file mode 100644 (file)
index 0000000..9396e61
--- /dev/null
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
+**        a KDAB Group company, info@kdab.com,
+**        author Stephen Kelly <stephen.kelly@kdab.com>
+** All rights reserved.
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file.  Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+****************************************************************************/
+
+#include "qidentityproxymodel.h"
+
+#ifndef QT_NO_IDENTITYPROXYMODEL
+
+#include "qitemselectionmodel.h"
+#include <private/qabstractproxymodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIdentityProxyModelPrivate : public QAbstractProxyModelPrivate
+{
+  QIdentityProxyModelPrivate()
+    : ignoreNextLayoutAboutToBeChanged(false),
+      ignoreNextLayoutChanged(false)
+  {
+
+  }
+
+  Q_DECLARE_PUBLIC(QIdentityProxyModel)
+
+  bool ignoreNextLayoutAboutToBeChanged;
+  bool ignoreNextLayoutChanged;
+  QList<QPersistentModelIndex> layoutChangePersistentIndexes;
+  QModelIndexList proxyIndexes;
+
+  void _q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
+  void _q_sourceRowsInserted(const QModelIndex &parent, int start, int end);
+  void _q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+  void _q_sourceRowsRemoved(const QModelIndex &parent, int start, int end);
+  void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
+  void _q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
+
+  void _q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
+  void _q_sourceColumnsInserted(const QModelIndex &parent, int start, int end);
+  void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+  void _q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end);
+  void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
+  void _q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
+
+  void _q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+  void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last);
+
+  void _q_sourceLayoutAboutToBeChanged();
+  void _q_sourceLayoutChanged();
+  void _q_sourceModelAboutToBeReset();
+  void _q_sourceModelReset();
+
+};
+
+/*!
+    \since 4.8
+    \class QIdentityProxyModel
+    \brief The QIdentityProxyModel class proxies its source model unmodified
+
+    \ingroup model-view
+
+    QIdentityProxyModel can be used to forward the structure of a source model exactly, with no sorting, filtering or other transformation.
+    This is similar in concept to an identity matrix where A.I = A.
+
+    Because it does no sorting or filtering, this class is most suitable to proxy models which transform the data() of the source model.
+    For example, a proxy model could be created to define the font used, or the background colour, or the tooltip etc. This removes the
+    need to implement all data handling in the same class that creates the structure of the model, and can also be used to create
+    re-usable components.
+
+    This also provides a way to change the data in the case where a source model is supplied by a third party which can not be modified.
+
+    \snippet doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp 0
+
+    \sa QAbstractProxyModel, {Model/View Programming}, QAbstractItemModel
+
+*/
+
+/*!
+    Constructs an identity model with the given \a parent.
+*/
+QIdentityProxyModel::QIdentityProxyModel(QObject* parent)
+  : QAbstractProxyModel(*new QIdentityProxyModelPrivate, parent)
+{
+
+}
+
+/*! \internal
+ */
+QIdentityProxyModel::QIdentityProxyModel(QIdentityProxyModelPrivate &dd, QObject* parent)
+  : QAbstractProxyModel(dd, parent)
+{
+
+}
+
+/*!
+    Destroys this identity model.
+*/
+QIdentityProxyModel::~QIdentityProxyModel()
+{
+}
+
+/*!
+    \reimp
+ */
+int QIdentityProxyModel::columnCount(const QModelIndex& parent) const
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(const QIdentityProxyModel);
+    return d->model->columnCount(mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+bool QIdentityProxyModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(QIdentityProxyModel);
+    return d->model->dropMimeData(data, action, row, column, mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+QModelIndex QIdentityProxyModel::index(int row, int column, const QModelIndex& parent) const
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(const QIdentityProxyModel);
+    if (!hasIndex(row, column, parent))
+        return QModelIndex();
+    const QModelIndex sourceParent = mapToSource(parent);
+    const QModelIndex sourceIndex = d->model->index(row, column, sourceParent);
+    Q_ASSERT(sourceIndex.isValid());
+    return mapFromSource(sourceIndex);
+}
+
+/*!
+    \reimp
+ */
+bool QIdentityProxyModel::insertColumns(int column, int count, const QModelIndex& parent)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(QIdentityProxyModel);
+    return d->model->insertColumns(column, count, mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+bool QIdentityProxyModel::insertRows(int row, int count, const QModelIndex& parent)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(QIdentityProxyModel);
+    return d->model->insertRows(row, count, mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+QModelIndex QIdentityProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
+{
+    Q_D(const QIdentityProxyModel);
+    if (!d->model || !sourceIndex.isValid())
+        return QModelIndex();
+
+    Q_ASSERT(sourceIndex.model() == d->model);
+    return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
+}
+
+/*!
+    \reimp
+ */
+QItemSelection QIdentityProxyModel::mapSelectionFromSource(const QItemSelection& selection) const
+{
+    Q_D(const QIdentityProxyModel);
+    QItemSelection proxySelection;
+
+    if (!d->model)
+        return proxySelection;
+
+    QItemSelection::const_iterator it = selection.constBegin();
+    const QItemSelection::const_iterator end = selection.constEnd();
+    for ( ; it != end; ++it) {
+        Q_ASSERT(it->model() == d->model);
+        const QItemSelectionRange range(mapFromSource(it->topLeft()), mapFromSource(it->bottomRight()));
+        proxySelection.append(range);
+    }
+
+    return proxySelection;
+}
+
+/*!
+    \reimp
+ */
+QItemSelection QIdentityProxyModel::mapSelectionToSource(const QItemSelection& selection) const
+{
+    Q_D(const QIdentityProxyModel);
+    QItemSelection sourceSelection;
+
+    if (!d->model)
+        return sourceSelection;
+
+    QItemSelection::const_iterator it = selection.constBegin();
+    const QItemSelection::const_iterator end = selection.constEnd();
+    for ( ; it != end; ++it) {
+        Q_ASSERT(it->model() == this);
+        const QItemSelectionRange range(mapToSource(it->topLeft()), mapToSource(it->bottomRight()));
+        sourceSelection.append(range);
+    }
+
+    return sourceSelection;
+}
+
+/*!
+    \reimp
+ */
+QModelIndex QIdentityProxyModel::mapToSource(const QModelIndex& proxyIndex) const
+{
+    Q_D(const QIdentityProxyModel);
+    if (!d->model || !proxyIndex.isValid())
+        return QModelIndex();
+    Q_ASSERT(proxyIndex.model() == this);
+    return d->model->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
+}
+
+/*!
+    \reimp
+ */
+QModelIndexList QIdentityProxyModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const
+{
+    Q_D(const QIdentityProxyModel);
+    Q_ASSERT(start.isValid() ? start.model() == this : true);
+    if (!d->model)
+        return QModelIndexList();
+
+    const QModelIndexList sourceList = d->model->match(mapToSource(start), role, value, hits, flags);
+    QModelIndexList::const_iterator it = sourceList.constBegin();
+    const QModelIndexList::const_iterator end = sourceList.constEnd();
+    QModelIndexList proxyList;
+    for ( ; it != end; ++it)
+        proxyList.append(mapFromSource(*it));
+    return proxyList;
+}
+
+/*!
+    \reimp
+ */
+QModelIndex QIdentityProxyModel::parent(const QModelIndex& child) const
+{
+    Q_ASSERT(child.isValid() ? child.model() == this : true);
+    const QModelIndex sourceIndex = mapToSource(child);
+    const QModelIndex sourceParent = sourceIndex.parent();
+    return mapFromSource(sourceParent);
+}
+
+/*!
+    \reimp
+ */
+bool QIdentityProxyModel::removeColumns(int column, int count, const QModelIndex& parent)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(QIdentityProxyModel);
+    return d->model->removeColumns(column, count, mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+bool QIdentityProxyModel::removeRows(int row, int count, const QModelIndex& parent)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(QIdentityProxyModel);
+    return d->model->removeRows(row, count, mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+int QIdentityProxyModel::rowCount(const QModelIndex& parent) const
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == this : true);
+    Q_D(const QIdentityProxyModel);
+    return d->model->rowCount(mapToSource(parent));
+}
+
+/*!
+    \reimp
+ */
+void QIdentityProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
+{
+    beginResetModel();
+
+    if (sourceModel) {
+        disconnect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceRowsAboutToBeInserted(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceRowsInserted(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceRowsAboutToBeRemoved(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceRowsRemoved(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                   this, SLOT(_q_sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        disconnect(sourceModel, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                   this, SLOT(_q_sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        disconnect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceColumnsAboutToBeInserted(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceColumnsInserted(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceColumnsAboutToBeRemoved(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
+                   this, SLOT(_q_sourceColumnsRemoved(const QModelIndex &, int, int)));
+        disconnect(sourceModel, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                   this, SLOT(_q_sourceColumnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        disconnect(sourceModel, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                   this, SLOT(_q_sourceColumnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        disconnect(sourceModel, SIGNAL(modelAboutToBeReset()),
+                   this, SLOT(_q_sourceModelAboutToBeReset()));
+        disconnect(sourceModel, SIGNAL(modelReset()),
+                   this, SLOT(_q_sourceModelReset()));
+        disconnect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+                   this, SLOT(_q_sourceDataChanged(const QModelIndex &, const QModelIndex &)));
+        disconnect(sourceModel, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+                   this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
+        disconnect(sourceModel, SIGNAL(layoutAboutToBeChanged()),
+                   this, SLOT(_q_sourceLayoutAboutToBeChanged()));
+        disconnect(sourceModel, SIGNAL(layoutChanged()),
+                   this, SLOT(_q_sourceLayoutChanged()));
+    }
+
+    QAbstractProxyModel::setSourceModel(sourceModel);
+
+    if (sourceModel) {
+        connect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
+                SLOT(_q_sourceRowsAboutToBeInserted(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+                SLOT(_q_sourceRowsInserted(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
+                SLOT(_q_sourceRowsAboutToBeRemoved(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+                SLOT(_q_sourceRowsRemoved(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                SLOT(_q_sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        connect(sourceModel, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                SLOT(_q_sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        connect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
+                SLOT(_q_sourceColumnsAboutToBeInserted(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
+                SLOT(_q_sourceColumnsInserted(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
+                SLOT(_q_sourceColumnsAboutToBeRemoved(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
+                SLOT(_q_sourceColumnsRemoved(const QModelIndex &, int, int)));
+        connect(sourceModel, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                SLOT(_q_sourceColumnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        connect(sourceModel, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
+                SLOT(_q_sourceColumnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+        connect(sourceModel, SIGNAL(modelAboutToBeReset()),
+                SLOT(_q_sourceModelAboutToBeReset()));
+        connect(sourceModel, SIGNAL(modelReset()),
+                SLOT(_q_sourceModelReset()));
+        connect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+                SLOT(_q_sourceDataChanged(const QModelIndex &, const QModelIndex &)));
+        connect(sourceModel, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+                SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
+        connect(sourceModel, SIGNAL(layoutAboutToBeChanged()),
+                SLOT(_q_sourceLayoutAboutToBeChanged()));
+        connect(sourceModel, SIGNAL(layoutChanged()),
+                SLOT(_q_sourceLayoutChanged()));
+    }
+
+    endResetModel();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginInsertColumns(q->mapFromSource(parent), start, end);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
+{
+    Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true);
+    Q_ASSERT(destParent.isValid() ? destParent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginRemoveColumns(q->mapFromSource(parent), start, end);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsInserted(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(parent)
+    Q_UNUSED(start)
+    Q_UNUSED(end)
+    q->endInsertColumns();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
+{
+    Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true);
+    Q_ASSERT(destParent.isValid() ? destParent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(sourceParent)
+    Q_UNUSED(sourceStart)
+    Q_UNUSED(sourceEnd)
+    Q_UNUSED(destParent)
+    Q_UNUSED(dest)
+    q->endMoveColumns();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(parent)
+    Q_UNUSED(start)
+    Q_UNUSED(end)
+    q->endRemoveColumns();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+    Q_ASSERT(topLeft.isValid() ? topLeft.model() == model : true);
+    Q_ASSERT(bottomRight.isValid() ? bottomRight.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight));
+}
+
+void QIdentityProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last)
+{
+    Q_Q(QIdentityProxyModel);
+    q->headerDataChanged(orientation, first, last);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged()
+{
+    if (ignoreNextLayoutAboutToBeChanged)
+        return;
+
+    Q_Q(QIdentityProxyModel);
+
+    foreach(const QPersistentModelIndex &proxyPersistentIndex, q->persistentIndexList()) {
+        proxyIndexes << proxyPersistentIndex;
+        Q_ASSERT(proxyPersistentIndex.isValid());
+        const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
+        Q_ASSERT(srcPersistentIndex.isValid());
+        layoutChangePersistentIndexes << srcPersistentIndex;
+    }
+
+    q->layoutAboutToBeChanged();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceLayoutChanged()
+{
+    if (ignoreNextLayoutChanged)
+        return;
+
+    Q_Q(QIdentityProxyModel);
+
+    for (int i = 0; i < proxyIndexes.size(); ++i) {
+        q->changePersistentIndex(proxyIndexes.at(i), q->mapFromSource(layoutChangePersistentIndexes.at(i)));
+    }
+
+    layoutChangePersistentIndexes.clear();
+    proxyIndexes.clear();
+
+    q->layoutChanged();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceModelAboutToBeReset()
+{
+    Q_Q(QIdentityProxyModel);
+    q->beginResetModel();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceModelReset()
+{
+    Q_Q(QIdentityProxyModel);
+    q->endResetModel();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginInsertRows(q->mapFromSource(parent), start, end);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
+{
+    Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true);
+    Q_ASSERT(destParent.isValid() ? destParent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    q->beginRemoveRows(q->mapFromSource(parent), start, end);
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsInserted(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(parent)
+    Q_UNUSED(start)
+    Q_UNUSED(end)
+    q->endInsertRows();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
+{
+    Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true);
+    Q_ASSERT(destParent.isValid() ? destParent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(sourceParent)
+    Q_UNUSED(sourceStart)
+    Q_UNUSED(sourceEnd)
+    Q_UNUSED(destParent)
+    Q_UNUSED(dest)
+    q->endMoveRows();
+}
+
+void QIdentityProxyModelPrivate::_q_sourceRowsRemoved(const QModelIndex &parent, int start, int end)
+{
+    Q_ASSERT(parent.isValid() ? parent.model() == model : true);
+    Q_Q(QIdentityProxyModel);
+    Q_UNUSED(parent)
+    Q_UNUSED(start)
+    Q_UNUSED(end)
+    q->endRemoveRows();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qidentityproxymodel.cpp"
+
+#endif // QT_NO_IDENTITYPROXYMODEL
diff --git a/src/gui/itemviews/qidentityproxymodel.h b/src/gui/itemviews/qidentityproxymodel.h
new file mode 100644 (file)
index 0000000..b60aa0b
--- /dev/null
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
+**        a KDAB Group company, info@kdab.com,
+**        author Stephen Kelly <stephen.kelly@kdab.com>
+** All rights reserved.
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file.  Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+****************************************************************************/
+
+
+#ifndef QIDENTITYPROXYMODEL_H
+#define QIDENTITYPROXYMODEL_H
+
+#include <QtGui/qabstractproxymodel.h>
+
+#ifndef QT_NO_IDENTITYPROXYMODEL
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIdentityProxyModelPrivate;
+
+class Q_GUI_EXPORT QIdentityProxyModel : public QAbstractProxyModel
+{
+    Q_OBJECT
+public:
+    explicit QIdentityProxyModel(QObject* parent = 0);
+    ~QIdentityProxyModel();
+
+    int columnCount(const QModelIndex& parent = QModelIndex()) const;
+    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
+    QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
+    QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
+    QModelIndex parent(const QModelIndex& child) const;
+    int rowCount(const QModelIndex& parent = QModelIndex()) const;
+    bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
+
+    QItemSelection mapSelectionFromSource(const QItemSelection& selection) const;
+    QItemSelection mapSelectionToSource(const QItemSelection& selection) const;
+    QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const;
+    void setSourceModel(QAbstractItemModel* sourceModel);
+
+    bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex());
+    bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
+    bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex());
+    bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
+
+protected:
+    QIdentityProxyModel(QIdentityProxyModelPrivate &dd, QObject* parent);
+
+private:
+    Q_DECLARE_PRIVATE(QIdentityProxyModel)
+    Q_DISABLE_COPY(QIdentityProxyModel)
+
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))
+
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(QModelIndex,int,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))
+
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(QModelIndex,QModelIndex))
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last))
+
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutAboutToBeChanged())
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutChanged())
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceModelAboutToBeReset())
+    Q_PRIVATE_SLOT(d_func(), void _q_sourceModelReset())
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_IDENTITYPROXYMODEL
+
+#endif // QIDENTITYPROXYMODEL_H
+
index eb56f56..f73607b 100644 (file)
@@ -1502,7 +1502,7 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
     \l{Model Subclassing Reference}.
 
     \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
-    {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}
+    {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
 */
 
 /*!
index d016f91..afa6ad3 100644 (file)
@@ -84,6 +84,7 @@ SUBDIRS=\
     qheaderview \
     qicoimageformat \
     qicon \
+    qidentityproxymodel \
     qimageiohandler \
     qimagereader \
     qimagewriter \
diff --git a/tests/auto/qidentityproxymodel/qidentityproxymodel.pro b/tests/auto/qidentityproxymodel/qidentityproxymodel.pro
new file mode 100644 (file)
index 0000000..f529e20
--- /dev/null
@@ -0,0 +1,6 @@
+load(qttest_p4)
+
+INCLUDEPATH += $$PWD/../modeltest
+
+SOURCES         += tst_qidentityproxymodel.cpp ../modeltest/dynamictreemodel.cpp ../modeltest/modeltest.cpp
+HEADERS         += ../modeltest/dynamictreemodel.h ../modeltest/modeltest.h
diff --git a/tests/auto/qidentityproxymodel/tst_qidentityproxymodel.cpp b/tests/auto/qidentityproxymodel/tst_qidentityproxymodel.cpp
new file mode 100644 (file)
index 0000000..bbcdb4c
--- /dev/null
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
+**        a KDAB Group company, info@kdab.com,
+**        author Stephen Kelly <stephen.kelly@kdab.com>
+** All rights reserved.
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file.  Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include "../../shared/util.h"
+
+#include <QtCore>
+#include <QtGui>
+
+#include "dynamictreemodel.h"
+#include "qidentityproxymodel.h"
+
+//TESTED CLASS=
+//TESTED_FILES=
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+class tst_QIdentityProxyModel : public QObject
+{
+    Q_OBJECT
+
+public:
+
+    tst_QIdentityProxyModel();
+    virtual ~tst_QIdentityProxyModel();
+
+public slots:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+    void cleanup();
+
+private slots:
+    void insertRows();
+    void removeRows();
+    void moveRows();
+    void reset();
+
+protected:
+    void verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex());
+
+private:
+    QStandardItemModel *m_model;
+    QIdentityProxyModel *m_proxy;
+};
+
+tst_QIdentityProxyModel::tst_QIdentityProxyModel()
+    : m_model(0), m_proxy(0)
+{
+
+}
+
+tst_QIdentityProxyModel::~tst_QIdentityProxyModel()
+{
+
+}
+
+void tst_QIdentityProxyModel::initTestCase()
+{
+    qRegisterMetaType<QModelIndex>("QModelIndex");
+
+    m_model = new QStandardItemModel(0, 1);
+    m_proxy = new QIdentityProxyModel();
+}
+
+void tst_QIdentityProxyModel::cleanupTestCase()
+{
+    delete m_proxy;
+    delete m_model;
+}
+
+void tst_QIdentityProxyModel::init()
+{
+}
+
+void tst_QIdentityProxyModel::cleanup()
+{
+    m_model->clear();
+    m_model->insertColumns(0, 1);
+}
+
+void tst_QIdentityProxyModel::verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent)
+{
+    const int rows = model->rowCount(parent);
+    const int columns = model->columnCount(parent);
+    const QModelIndex proxyParent = m_proxy->mapFromSource(parent);
+
+    QVERIFY(m_proxy->mapToSource(proxyParent) == parent);
+    QVERIFY(rows == m_proxy->rowCount(proxyParent));
+    QVERIFY(columns == m_proxy->columnCount(proxyParent));
+
+    for (int row = 0; row < rows; ++row) {
+        for (int column = 0; column < columns; ++column) {
+            const QModelIndex idx = model->index(row, column, parent);
+            const QModelIndex proxyIdx = m_proxy->mapFromSource(idx);
+            QVERIFY(proxyIdx.model() == m_proxy);
+            QVERIFY(m_proxy->mapToSource(proxyIdx) == idx);
+            QVERIFY(proxyIdx.isValid());
+            QVERIFY(proxyIdx.row() == row);
+            QVERIFY(proxyIdx.column() == column);
+            QVERIFY(proxyIdx.parent() == proxyParent);
+            QVERIFY(proxyIdx.data() == idx.data());
+            QVERIFY(proxyIdx.flags() == idx.flags());
+            const int childCount = m_proxy->rowCount(proxyIdx);
+            const bool hasChildren = m_proxy->hasChildren(proxyIdx);
+            QVERIFY(model->hasChildren(idx) == hasChildren);
+            QVERIFY((childCount > 0) == hasChildren);
+
+            if (hasChildren)
+                verifyIdentity(model, idx);
+        }
+    }
+}
+
+/*
+  tests
+*/
+
+void tst_QIdentityProxyModel::insertRows()
+{
+    QStandardItem *parentItem = m_model->invisibleRootItem();
+    for (int i = 0; i < 4; ++i) {
+        QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
+        parentItem->appendRow(item);
+        parentItem = item;
+    }
+
+    m_proxy->setSourceModel(m_model);
+
+    verifyIdentity(m_model);
+
+    QSignalSpy modelBeforeSpy(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+    QSignalSpy modelAfterSpy(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)));
+    QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+    QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
+
+    QStandardItem *item = new QStandardItem(QString("new item"));
+    parentItem->appendRow(item);
+
+    QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size());
+    QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size());
+
+    QVERIFY(modelBeforeSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1));
+    QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2));
+
+    QVERIFY(modelAfterSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyAfterSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1));
+    QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2));
+
+    verifyIdentity(m_model);
+
+}
+
+void tst_QIdentityProxyModel::removeRows()
+{
+    QStandardItem *parentItem = m_model->invisibleRootItem();
+    for (int i = 0; i < 4; ++i) {
+        QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
+        parentItem->appendRow(item);
+        parentItem = item;
+    }
+
+    m_proxy->setSourceModel(m_model);
+
+    verifyIdentity(m_model);
+
+    QSignalSpy modelBeforeSpy(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+    QSignalSpy modelAfterSpy(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+    QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+    QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+
+    const QModelIndex topLevel = m_model->index(0, 0, QModelIndex());
+    const QModelIndex secondLevel = m_model->index(0, 0, topLevel);
+    const QModelIndex thirdLevel = m_model->index(0, 0, secondLevel);
+
+    QVERIFY(thirdLevel.isValid());
+
+    m_model->removeRow(0, secondLevel);
+
+    QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size());
+    QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size());
+
+    QVERIFY(modelBeforeSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1));
+    QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2));
+
+    QVERIFY(modelAfterSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyAfterSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1));
+    QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2));
+
+    verifyIdentity(m_model);
+}
+
+void tst_QIdentityProxyModel::moveRows()
+{
+    DynamicTreeModel model;
+
+    {
+        ModelInsertCommand insertCommand(&model);
+        insertCommand.setStartRow(0);
+        insertCommand.setEndRow(9);
+        insertCommand.doCommand();
+    }
+    {
+        ModelInsertCommand insertCommand(&model);
+        insertCommand.setAncestorRowNumbers(QList<int>() << 5);
+        insertCommand.setStartRow(0);
+        insertCommand.setEndRow(9);
+        insertCommand.doCommand();
+    }
+
+    m_proxy->setSourceModel(&model);
+
+    verifyIdentity(&model);
+
+    QSignalSpy modelBeforeSpy(&model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
+    QSignalSpy modelAfterSpy(&model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+    QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
+    QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+
+    {
+        ModelMoveCommand moveCommand(&model, 0);
+        moveCommand.setAncestorRowNumbers(QList<int>() << 5);
+        moveCommand.setStartRow(3);
+        moveCommand.setEndRow(4);
+        moveCommand.setDestRow(1);
+        moveCommand.doCommand();
+    }
+
+    QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size());
+    QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size());
+
+    QVERIFY(modelBeforeSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1));
+    QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2));
+    QVERIFY(modelBeforeSpy.first().at(3).value<QModelIndex>() == m_proxy->mapToSource(proxyBeforeSpy.first().at(3).value<QModelIndex>()));
+    QVERIFY(modelBeforeSpy.first().at(4) == proxyBeforeSpy.first().at(4));
+
+    QVERIFY(modelAfterSpy.first().first().value<QModelIndex>() == m_proxy->mapToSource(proxyAfterSpy.first().first().value<QModelIndex>()));
+    QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1));
+    QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2));
+    QVERIFY(modelAfterSpy.first().at(3).value<QModelIndex>() == m_proxy->mapToSource(proxyAfterSpy.first().at(3).value<QModelIndex>()));
+    QVERIFY(modelAfterSpy.first().at(4) == proxyAfterSpy.first().at(4));
+
+    verifyIdentity(&model);
+
+    m_proxy->setSourceModel(0);
+}
+
+void tst_QIdentityProxyModel::reset()
+{
+    DynamicTreeModel model;
+
+    {
+        ModelInsertCommand insertCommand(&model);
+        insertCommand.setStartRow(0);
+        insertCommand.setEndRow(9);
+        insertCommand.doCommand();
+    }
+    {
+        ModelInsertCommand insertCommand(&model);
+        insertCommand.setAncestorRowNumbers(QList<int>() << 5);
+        insertCommand.setStartRow(0);
+        insertCommand.setEndRow(9);
+        insertCommand.doCommand();
+    }
+
+    m_proxy->setSourceModel(&model);
+
+    verifyIdentity(&model);
+
+    QSignalSpy modelBeforeSpy(&model, SIGNAL(modelAboutToBeReset()));
+    QSignalSpy modelAfterSpy(&model, SIGNAL(modelReset()));
+    QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(modelAboutToBeReset()));
+    QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(modelReset()));
+
+    {
+        ModelResetCommandFixed resetCommand(&model, 0);
+        resetCommand.setAncestorRowNumbers(QList<int>() << 5);
+        resetCommand.setStartRow(3);
+        resetCommand.setEndRow(4);
+        resetCommand.setDestRow(1);
+        resetCommand.doCommand();
+    }
+
+    QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size());
+    QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size());
+
+    verifyIdentity(&model);
+    m_proxy->setSourceModel(0);
+}
+
+QTEST_MAIN(tst_QIdentityProxyModel)
+#include "tst_qidentityproxymodel.moc"