| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,336 @@ | ||
| /**************************************************************************** | ||
| ** | ||
| ** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com> | ||
| ** All rights reserved. | ||
| ** Contact: Nokia Corporation (qt-info@nokia.com) | ||
| ** | ||
| ** This file is part of the test suite of the Qt Toolkit. | ||
| ** | ||
| ** $QT_BEGIN_LICENSE:LGPL$ | ||
| ** GNU Lesser General Public License Usage | ||
| ** This file may be used under the terms of the GNU Lesser General Public | ||
| ** License version 2.1 as published by the Free Software Foundation and | ||
| ** appearing in the file LICENSE.LGPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU Lesser | ||
| ** General Public License version 2.1 requirements will be met: | ||
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | ||
| ** | ||
| ** In addition, as a special exception, Nokia gives you certain additional | ||
| ** rights. These rights are described in the Nokia Qt LGPL Exception | ||
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | ||
| ** | ||
| ** GNU General Public License Usage | ||
| ** Alternatively, this file may be used under the terms of the GNU General | ||
| ** Public License version 3.0 as published by the Free Software Foundation | ||
| ** and appearing in the file LICENSE.GPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU General | ||
| ** Public License version 3.0 requirements will be met: | ||
| ** http://www.gnu.org/copyleft/gpl.html. | ||
| ** | ||
| ** Other Usage | ||
| ** Alternatively, this file may be used in accordance with the terms and | ||
| ** conditions contained in a signed written agreement between you and Nokia. | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** $QT_END_LICENSE$ | ||
| ** | ||
| ****************************************************************************/ | ||
|
|
||
| #include "dynamictreemodel.h" | ||
|
|
||
| #include <QtCore/QHash> | ||
| #include <QtCore/QList> | ||
| #include <QtCore/QTimer> | ||
|
|
||
|
|
||
| DynamicTreeModel::DynamicTreeModel(QObject *parent) | ||
| : QAbstractItemModel(parent), | ||
| nextId(1) | ||
| { | ||
| } | ||
|
|
||
| QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const | ||
| { | ||
| // if (column != 0) | ||
| // return QModelIndex(); | ||
|
|
||
|
|
||
| if ( column < 0 || row < 0 ) | ||
| return QModelIndex(); | ||
|
|
||
| QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId()); | ||
|
|
||
| const qint64 grandParent = findParentId(parent.internalId()); | ||
| if (grandParent >= 0) { | ||
| QList<QList<qint64> > parentTable = m_childItems.value(grandParent); | ||
| Q_ASSERT(parent.column() < parentTable.size()); | ||
| QList<qint64> parentSiblings = parentTable.at(parent.column()); | ||
| Q_ASSERT(parent.row() < parentSiblings.size()); | ||
| } | ||
|
|
||
| if (childIdColumns.size() == 0) | ||
| return QModelIndex(); | ||
|
|
||
| if (column >= childIdColumns.size()) | ||
| return QModelIndex(); | ||
|
|
||
| QList<qint64> rowIds = childIdColumns.at(column); | ||
|
|
||
| if ( row >= rowIds.size()) | ||
| return QModelIndex(); | ||
|
|
||
| qint64 id = rowIds.at(row); | ||
|
|
||
| return createIndex(row, column, reinterpret_cast<void *>(id)); | ||
|
|
||
| } | ||
|
|
||
| qint64 DynamicTreeModel::findParentId(qint64 searchId) const | ||
| { | ||
| if (searchId <= 0) | ||
| return -1; | ||
|
|
||
| QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems); | ||
| while (i.hasNext()) | ||
| { | ||
| i.next(); | ||
| QListIterator<QList<qint64> > j(i.value()); | ||
| while (j.hasNext()) | ||
| { | ||
| QList<qint64> l = j.next(); | ||
| if (l.contains(searchId)) | ||
| { | ||
| return i.key(); | ||
| } | ||
| } | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const | ||
| { | ||
| if (!index.isValid()) | ||
| return QModelIndex(); | ||
|
|
||
| qint64 searchId = index.internalId(); | ||
| qint64 parentId = findParentId(searchId); | ||
| // Will never happen for valid index, but what the hey... | ||
| if (parentId <= 0) | ||
| return QModelIndex(); | ||
|
|
||
| qint64 grandParentId = findParentId(parentId); | ||
| if (grandParentId < 0) | ||
| grandParentId = 0; | ||
|
|
||
| int column = 0; | ||
| QList<qint64> childList = m_childItems.value(grandParentId).at(column); | ||
|
|
||
| int row = childList.indexOf(parentId); | ||
|
|
||
| return createIndex(row, column, reinterpret_cast<void *>(parentId)); | ||
|
|
||
| } | ||
|
|
||
| int DynamicTreeModel::rowCount(const QModelIndex &index ) const | ||
| { | ||
| QList<QList<qint64> > cols = m_childItems.value(index.internalId()); | ||
|
|
||
| if (cols.size() == 0 ) | ||
| return 0; | ||
|
|
||
| if (index.column() > 0) | ||
| return 0; | ||
|
|
||
| return cols.at(0).size(); | ||
| } | ||
|
|
||
| int DynamicTreeModel::columnCount(const QModelIndex &index ) const | ||
| { | ||
| // Q_UNUSED(index); | ||
| return m_childItems.value(index.internalId()).size(); | ||
| } | ||
|
|
||
| QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const | ||
| { | ||
| if (!index.isValid()) | ||
| return QVariant(); | ||
|
|
||
| if (Qt::DisplayRole == role) | ||
| { | ||
| return m_items.value(index.internalId()); | ||
| } | ||
| return QVariant(); | ||
| } | ||
|
|
||
| void DynamicTreeModel::clear() | ||
| { | ||
| beginResetModel(); | ||
| m_items.clear(); | ||
| m_childItems.clear(); | ||
| nextId = 1; | ||
| endResetModel(); | ||
| } | ||
|
|
||
|
|
||
| ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) | ||
| : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| QModelIndex ModelChangeCommand::findIndex(QList<int> rows) | ||
| { | ||
| const int col = 0; | ||
| QModelIndex parent = QModelIndex(); | ||
| QListIterator<int> i(rows); | ||
| while (i.hasNext()) | ||
| { | ||
| parent = m_model->index(i.next(), col, parent); | ||
| Q_ASSERT(parent.isValid()); | ||
| } | ||
| return parent; | ||
| } | ||
|
|
||
| ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) | ||
| : ModelChangeCommand(model, parent) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| void ModelInsertCommand::doCommand() | ||
| { | ||
| QModelIndex parent = findIndex(m_rowNumbers); | ||
| m_model->beginInsertRows(parent, m_startRow, m_endRow); | ||
| qint64 parentId = parent.internalId(); | ||
| for (int row = m_startRow; row <= m_endRow; row++) | ||
| { | ||
| for(int col = 0; col < m_numCols; col++ ) | ||
| { | ||
| if (m_model->m_childItems[parentId].size() <= col) | ||
| { | ||
| m_model->m_childItems[parentId].append(QList<qint64>()); | ||
| } | ||
| // QString name = QUuid::createUuid().toString(); | ||
| qint64 id = m_model->newId(); | ||
| QString name = QString::number(id); | ||
|
|
||
| m_model->m_items.insert(id, name); | ||
| m_model->m_childItems[parentId][col].insert(row, id); | ||
|
|
||
| } | ||
| } | ||
| m_model->endInsertRows(); | ||
| } | ||
|
|
||
|
|
||
| ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) | ||
| : ModelChangeCommand(model, parent) | ||
| { | ||
|
|
||
| } | ||
| bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) | ||
| { | ||
| return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); | ||
| } | ||
|
|
||
| void ModelMoveCommand::doCommand() | ||
| { | ||
| QModelIndex srcParent = findIndex(m_rowNumbers); | ||
| QModelIndex destParent = findIndex(m_destRowNumbers); | ||
|
|
||
| if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| for (int column = 0; column < m_numCols; ++column) | ||
| { | ||
| QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); | ||
|
|
||
| for (int i = m_startRow; i <= m_endRow ; i++) | ||
| { | ||
| m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); | ||
| } | ||
| int d; | ||
| if (m_destRow < m_startRow) | ||
| d = m_destRow; | ||
| else | ||
| { | ||
| if (srcParent == destParent) | ||
| d = m_destRow - (m_endRow - m_startRow + 1); | ||
| else | ||
| d = m_destRow - (m_endRow - m_startRow) + 1; | ||
| } | ||
|
|
||
| foreach(const qint64 id, l) | ||
| { | ||
| m_model->m_childItems[destParent.internalId()][column].insert(d++, id); | ||
| } | ||
| } | ||
|
|
||
| emitPostSignal(); | ||
| } | ||
|
|
||
| void ModelMoveCommand::emitPostSignal() | ||
| { | ||
| m_model->endMoveRows(); | ||
| } | ||
|
|
||
| ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent) | ||
| : ModelMoveCommand(model, parent) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| ModelResetCommand::~ModelResetCommand() | ||
| { | ||
|
|
||
| } | ||
|
|
||
| bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) | ||
| { | ||
| Q_UNUSED(srcParent); | ||
| Q_UNUSED(srcStart); | ||
| Q_UNUSED(srcEnd); | ||
| Q_UNUSED(destParent); | ||
| Q_UNUSED(destRow); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void ModelResetCommand::emitPostSignal() | ||
| { | ||
| m_model->reset(); | ||
| } | ||
|
|
||
| ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent) | ||
| : ModelMoveCommand(model, parent) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| ModelResetCommandFixed::~ModelResetCommandFixed() | ||
| { | ||
|
|
||
| } | ||
|
|
||
| bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) | ||
| { | ||
| Q_UNUSED(srcParent); | ||
| Q_UNUSED(srcStart); | ||
| Q_UNUSED(srcEnd); | ||
| Q_UNUSED(destParent); | ||
| Q_UNUSED(destRow); | ||
|
|
||
| m_model->beginResetModel(); | ||
| return true; | ||
| } | ||
|
|
||
| void ModelResetCommandFixed::emitPostSignal() | ||
| { | ||
| m_model->endResetModel(); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| /**************************************************************************** | ||
| ** | ||
| ** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com> | ||
| ** All rights reserved. | ||
| ** Contact: Nokia Corporation (qt-info@nokia.com) | ||
| ** | ||
| ** This file is part of the test suite of the Qt Toolkit. | ||
| ** | ||
| ** $QT_BEGIN_LICENSE:LGPL$ | ||
| ** GNU Lesser General Public License Usage | ||
| ** This file may be used under the terms of the GNU Lesser General Public | ||
| ** License version 2.1 as published by the Free Software Foundation and | ||
| ** appearing in the file LICENSE.LGPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU Lesser | ||
| ** General Public License version 2.1 requirements will be met: | ||
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | ||
| ** | ||
| ** In addition, as a special exception, Nokia gives you certain additional | ||
| ** rights. These rights are described in the Nokia Qt LGPL Exception | ||
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | ||
| ** | ||
| ** GNU General Public License Usage | ||
| ** Alternatively, this file may be used under the terms of the GNU General | ||
| ** Public License version 3.0 as published by the Free Software Foundation | ||
| ** and appearing in the file LICENSE.GPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU General | ||
| ** Public License version 3.0 requirements will be met: | ||
| ** http://www.gnu.org/copyleft/gpl.html. | ||
| ** | ||
| ** Other Usage | ||
| ** Alternatively, this file may be used in accordance with the terms and | ||
| ** conditions contained in a signed written agreement between you and Nokia. | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** $QT_END_LICENSE$ | ||
| ** | ||
| ****************************************************************************/ | ||
|
|
||
| #ifndef DYNAMICTREEMODEL_H | ||
| #define DYNAMICTREEMODEL_H | ||
|
|
||
| #include <QtCore/QAbstractItemModel> | ||
|
|
||
| #include <QtCore/QHash> | ||
| #include <QtCore/QList> | ||
|
|
||
|
|
||
| class DynamicTreeModel : public QAbstractItemModel | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
| DynamicTreeModel(QObject *parent = 0); | ||
|
|
||
| QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; | ||
| QModelIndex parent(const QModelIndex &index) const; | ||
| int rowCount(const QModelIndex &index = QModelIndex()) const; | ||
| int columnCount(const QModelIndex &index = QModelIndex()) const; | ||
|
|
||
| QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; | ||
|
|
||
| void clear(); | ||
|
|
||
| protected slots: | ||
|
|
||
| /** | ||
| Finds the parent id of the string with id @p searchId. | ||
| Returns -1 if not found. | ||
| */ | ||
| qint64 findParentId(qint64 searchId) const; | ||
|
|
||
| private: | ||
| QHash<qint64, QString> m_items; | ||
| QHash<qint64, QList<QList<qint64> > > m_childItems; | ||
| qint64 nextId; | ||
| qint64 newId() { return nextId++; }; | ||
|
|
||
| QModelIndex m_nextParentIndex; | ||
| int m_nextRow; | ||
|
|
||
| int m_depth; | ||
| int maxDepth; | ||
|
|
||
| friend class ModelInsertCommand; | ||
| friend class ModelMoveCommand; | ||
| friend class ModelResetCommand; | ||
| friend class ModelResetCommandFixed; | ||
|
|
||
| }; | ||
|
|
||
|
|
||
| class ModelChangeCommand : public QObject | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
|
|
||
| ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); | ||
|
|
||
| virtual ~ModelChangeCommand() {} | ||
|
|
||
| void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; } | ||
|
|
||
| QModelIndex findIndex(QList<int> rows); | ||
|
|
||
| void setStartRow(int row) { m_startRow = row; } | ||
|
|
||
| void setEndRow(int row) { m_endRow = row; } | ||
|
|
||
| void setNumCols(int cols) { m_numCols = cols; } | ||
|
|
||
| virtual void doCommand() = 0; | ||
|
|
||
| protected: | ||
| DynamicTreeModel* m_model; | ||
| QList<int> m_rowNumbers; | ||
| int m_numCols; | ||
| int m_startRow; | ||
| int m_endRow; | ||
|
|
||
| }; | ||
|
|
||
| typedef QList<ModelChangeCommand*> ModelChangeCommandList; | ||
|
|
||
| class ModelInsertCommand : public ModelChangeCommand | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
|
|
||
| ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); | ||
| virtual ~ModelInsertCommand() {} | ||
|
|
||
| virtual void doCommand(); | ||
| }; | ||
|
|
||
|
|
||
| class ModelMoveCommand : public ModelChangeCommand | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| ModelMoveCommand(DynamicTreeModel *model, QObject *parent); | ||
|
|
||
| virtual ~ModelMoveCommand() {} | ||
|
|
||
| virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); | ||
|
|
||
| virtual void doCommand(); | ||
|
|
||
| virtual void emitPostSignal(); | ||
|
|
||
| void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; } | ||
|
|
||
| void setDestRow(int row) { m_destRow = row; } | ||
|
|
||
| protected: | ||
| QList<int> m_destRowNumbers; | ||
| int m_destRow; | ||
| }; | ||
|
|
||
| /** | ||
| A command which does a move and emits a reset signal. | ||
| */ | ||
| class ModelResetCommand : public ModelMoveCommand | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0); | ||
|
|
||
| virtual ~ModelResetCommand(); | ||
|
|
||
| virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); | ||
| virtual void emitPostSignal(); | ||
|
|
||
| }; | ||
|
|
||
| /** | ||
| A command which does a move and emits a beginResetModel and endResetModel signals. | ||
| */ | ||
| class ModelResetCommandFixed : public ModelMoveCommand | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0); | ||
|
|
||
| virtual ~ModelResetCommandFixed(); | ||
|
|
||
| virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); | ||
| virtual void emitPostSignal(); | ||
|
|
||
| }; | ||
|
|
||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| /**************************************************************************** | ||
| ** | ||
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | ||
| ** All rights reserved. | ||
| ** Contact: Nokia Corporation (qt-info@nokia.com) | ||
| ** | ||
| ** This file is part of the test suite of the Qt Toolkit. | ||
| ** | ||
| ** $QT_BEGIN_LICENSE:LGPL$ | ||
| ** GNU Lesser General Public License Usage | ||
| ** This file may be used under the terms of the GNU Lesser General Public | ||
| ** License version 2.1 as published by the Free Software Foundation and | ||
| ** appearing in the file LICENSE.LGPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU Lesser | ||
| ** General Public License version 2.1 requirements will be met: | ||
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | ||
| ** | ||
| ** In addition, as a special exception, Nokia gives you certain additional | ||
| ** rights. These rights are described in the Nokia Qt LGPL Exception | ||
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | ||
| ** | ||
| ** GNU General Public License Usage | ||
| ** Alternatively, this file may be used under the terms of the GNU General | ||
| ** Public License version 3.0 as published by the Free Software Foundation | ||
| ** and appearing in the file LICENSE.GPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU General | ||
| ** Public License version 3.0 requirements will be met: | ||
| ** http://www.gnu.org/copyleft/gpl.html. | ||
| ** | ||
| ** Other Usage | ||
| ** Alternatively, this file may be used in accordance with the terms and | ||
| ** conditions contained in a signed written agreement between you and Nokia. | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** $QT_END_LICENSE$ | ||
| ** | ||
| ****************************************************************************/ | ||
|
|
||
|
|
||
| #ifndef MODELTEST_H | ||
| #define MODELTEST_H | ||
|
|
||
| #include <QtCore/QObject> | ||
| #include <QtCore/QAbstractItemModel> | ||
| #include <QtCore/QStack> | ||
|
|
||
| class ModelTest : public QObject | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
| ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); | ||
|
|
||
| private Q_SLOTS: | ||
| void nonDestructiveBasicTest(); | ||
| void rowCount(); | ||
| void columnCount(); | ||
| void hasIndex(); | ||
| void index(); | ||
| void parent(); | ||
| void data(); | ||
|
|
||
| protected Q_SLOTS: | ||
| void runAllTests(); | ||
| void layoutAboutToBeChanged(); | ||
| void layoutChanged(); | ||
| void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); | ||
| void rowsInserted( const QModelIndex & parent, int start, int end ); | ||
| void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); | ||
| void rowsRemoved( const QModelIndex & parent, int start, int end ); | ||
|
|
||
| private: | ||
| void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); | ||
|
|
||
| QAbstractItemModel *model; | ||
|
|
||
| struct Changing { | ||
| QModelIndex parent; | ||
| int oldSize; | ||
| QVariant last; | ||
| QVariant next; | ||
| }; | ||
| QStack<Changing> insert; | ||
| QStack<Changing> remove; | ||
|
|
||
| bool fetchingMore; | ||
|
|
||
| QList<QPersistentModelIndex> changing; | ||
| }; | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| Model Test | ||
| ---------- | ||
|
|
||
| This is a simple tool from Qt for checking various errors of custom | ||
| implementations of models for item views. | ||
|
|
||
| http://developer.qt.nokia.com/wiki/Model_Test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,309 @@ | ||
| /**************************************************************************** | ||
| ** | ||
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | ||
| ** All rights reserved. | ||
| ** Contact: Nokia Corporation (qt-info@nokia.com) | ||
| ** | ||
| ** This file is part of the test suite of the Qt Toolkit. | ||
| ** | ||
| ** $QT_BEGIN_LICENSE:LGPL$ | ||
| ** GNU Lesser General Public License Usage | ||
| ** This file may be used under the terms of the GNU Lesser General Public | ||
| ** License version 2.1 as published by the Free Software Foundation and | ||
| ** appearing in the file LICENSE.LGPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU Lesser | ||
| ** General Public License version 2.1 requirements will be met: | ||
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | ||
| ** | ||
| ** In addition, as a special exception, Nokia gives you certain additional | ||
| ** rights. These rights are described in the Nokia Qt LGPL Exception | ||
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | ||
| ** | ||
| ** GNU General Public License Usage | ||
| ** Alternatively, this file may be used under the terms of the GNU General | ||
| ** Public License version 3.0 as published by the Free Software Foundation | ||
| ** and appearing in the file LICENSE.GPL included in the packaging of this | ||
| ** file. Please review the following information to ensure the GNU General | ||
| ** Public License version 3.0 requirements will be met: | ||
| ** http://www.gnu.org/copyleft/gpl.html. | ||
| ** | ||
| ** Other Usage | ||
| ** Alternatively, this file may be used in accordance with the terms and | ||
| ** conditions contained in a signed written agreement between you and Nokia. | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** | ||
| ** $QT_END_LICENSE$ | ||
| ** | ||
| ****************************************************************************/ | ||
|
|
||
|
|
||
| #include <QtTest/QtTest> | ||
| #include <QtGui/QtGui> | ||
|
|
||
| #include "modeltest.h" | ||
| #include "dynamictreemodel.h" | ||
|
|
||
|
|
||
| class tst_ModelTest : public QObject | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
| tst_ModelTest() {} | ||
| virtual ~tst_ModelTest() {} | ||
|
|
||
| public slots: | ||
| void initTestCase(); | ||
| void cleanupTestCase(); | ||
| void init(); | ||
| void cleanup(); | ||
|
|
||
| private slots: | ||
| void stringListModel(); | ||
| void treeWidgetModel(); | ||
| void standardItemModel(); | ||
| void testInsertThroughProxy(); | ||
| void moveSourceItems(); | ||
| void testResetThroughProxy(); | ||
| }; | ||
|
|
||
|
|
||
|
|
||
| void tst_ModelTest::initTestCase() | ||
| { | ||
| } | ||
|
|
||
| void tst_ModelTest::cleanupTestCase() | ||
| { | ||
| } | ||
|
|
||
| void tst_ModelTest::init() | ||
| { | ||
|
|
||
| } | ||
|
|
||
| void tst_ModelTest::cleanup() | ||
| { | ||
| } | ||
| /* | ||
| tests | ||
| */ | ||
|
|
||
| void tst_ModelTest::stringListModel() | ||
| { | ||
| QStringListModel model; | ||
| QSortFilterProxyModel proxy; | ||
|
|
||
| ModelTest t1(&model); | ||
| ModelTest t2(&proxy); | ||
|
|
||
| proxy.setSourceModel(&model); | ||
|
|
||
| model.setStringList(QStringList() << "2" << "3" << "1"); | ||
| model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" ); | ||
|
|
||
| proxy.setDynamicSortFilter(true); | ||
| proxy.setFilterRegExp(QRegExp("[^b]")); | ||
| } | ||
|
|
||
| void tst_ModelTest::treeWidgetModel() | ||
| { | ||
| QTreeWidget widget; | ||
|
|
||
| ModelTest t1(widget.model()); | ||
|
|
||
| QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root")); | ||
| for (int i = 0; i < 20; ++i) { | ||
| new QTreeWidgetItem(root, QStringList(QString::number(i))); | ||
| } | ||
| QTreeWidgetItem *remove = root->child(2); | ||
| root->removeChild(remove); | ||
| QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent")); | ||
| new QTreeWidgetItem(parent, QStringList("child")); | ||
| widget.setItemHidden(parent, true); | ||
|
|
||
| widget.sortByColumn(0); | ||
| } | ||
|
|
||
| void tst_ModelTest::standardItemModel() | ||
| { | ||
| QStandardItemModel model(10,10); | ||
| QSortFilterProxyModel proxy; | ||
|
|
||
|
|
||
| ModelTest t1(&model); | ||
| ModelTest t2(&proxy); | ||
|
|
||
| proxy.setSourceModel(&model); | ||
|
|
||
| model.insertRows(2, 5); | ||
| model.removeRows(4, 5); | ||
|
|
||
| model.insertColumns(2, 5); | ||
| model.removeColumns(4, 5); | ||
|
|
||
| model.insertRows(0,5, model.index(1,1)); | ||
| model.insertColumns(0,5, model.index(1,3)); | ||
| } | ||
|
|
||
| void tst_ModelTest::testInsertThroughProxy() | ||
| { | ||
| DynamicTreeModel *model = new DynamicTreeModel(this); | ||
|
|
||
| QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); | ||
| proxy->setSourceModel(model); | ||
|
|
||
| new ModelTest(proxy, this); | ||
|
|
||
| ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); | ||
| insertCommand->setNumCols(4); | ||
| insertCommand->setStartRow(0); | ||
| insertCommand->setEndRow(9); | ||
| // Parent is QModelIndex() | ||
| insertCommand->doCommand(); | ||
|
|
||
| insertCommand = new ModelInsertCommand(model, this); | ||
| insertCommand->setNumCols(4); | ||
| insertCommand->setAncestorRowNumbers(QList<int>() << 5); | ||
| insertCommand->setStartRow(0); | ||
| insertCommand->setEndRow(9); | ||
| insertCommand->doCommand(); | ||
|
|
||
| ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); | ||
| moveCommand->setNumCols(4); | ||
| moveCommand->setStartRow(0); | ||
| moveCommand->setEndRow(0); | ||
| moveCommand->setDestRow(9); | ||
| moveCommand->setDestAncestors(QList<int>() << 5); | ||
| moveCommand->doCommand(); | ||
| } | ||
|
|
||
| /** | ||
| Makes the persistent index list publicly accessible | ||
| */ | ||
| class AccessibleProxyModel : public QSortFilterProxyModel | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} | ||
|
|
||
| QModelIndexList persistent() | ||
| { | ||
| return persistentIndexList(); | ||
| } | ||
| }; | ||
|
|
||
| class ObservingObject : public QObject | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) | ||
| : QObject(parent), | ||
| m_proxy(proxy) | ||
| { | ||
| connect(m_proxy, SIGNAL(layoutAboutToBeChanged()), SLOT(storePersistent())); | ||
| connect(m_proxy, SIGNAL(layoutChanged()), SLOT(checkPersistent())); | ||
| } | ||
|
|
||
| public slots: | ||
|
|
||
| void storePersistent(const QModelIndex &parent) | ||
| { | ||
| for (int row = 0; row < m_proxy->rowCount(parent); ++row) { | ||
| QModelIndex proxyIndex = m_proxy->index(row, 0, parent); | ||
| QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex); | ||
| Q_ASSERT(proxyIndex.isValid()); | ||
| Q_ASSERT(sourceIndex.isValid()); | ||
| m_persistentSourceIndexes.append(sourceIndex); | ||
| m_persistentProxyIndexes.append(proxyIndex); | ||
| if (m_proxy->hasChildren(proxyIndex)) | ||
| storePersistent(proxyIndex); | ||
| } | ||
| } | ||
|
|
||
| void storePersistent() | ||
| { | ||
| foreach(const QModelIndex &idx, m_persistentProxyIndexes) | ||
| Q_ASSERT(idx.isValid()); // This is called from layoutAboutToBeChanged. Persistent indexes should be valid | ||
|
|
||
| Q_ASSERT(m_proxy->persistent().isEmpty()); | ||
| storePersistent(QModelIndex()); | ||
| Q_ASSERT(!m_proxy->persistent().isEmpty()); | ||
| } | ||
|
|
||
| void checkPersistent() | ||
| { | ||
| for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { | ||
| QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); | ||
| QModelIndex updatedSource = m_persistentSourceIndexes.at(row); | ||
| } | ||
| for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { | ||
| QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); | ||
| QModelIndex updatedSource = m_persistentSourceIndexes.at(row); | ||
| QCOMPARE(m_proxy->mapToSource(updatedProxy), updatedSource); | ||
| } | ||
| m_persistentSourceIndexes.clear(); | ||
| m_persistentProxyIndexes.clear(); | ||
| } | ||
|
|
||
| private: | ||
| AccessibleProxyModel *m_proxy; | ||
| QList<QPersistentModelIndex> m_persistentSourceIndexes; | ||
| QList<QPersistentModelIndex> m_persistentProxyIndexes; | ||
| }; | ||
|
|
||
| void tst_ModelTest::moveSourceItems() | ||
| { | ||
| DynamicTreeModel *model = new DynamicTreeModel(this); | ||
| AccessibleProxyModel *proxy = new AccessibleProxyModel(this); | ||
| proxy->setSourceModel(model); | ||
|
|
||
| ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); | ||
| insertCommand->setStartRow(0); | ||
| insertCommand->setEndRow(2); | ||
| insertCommand->doCommand(); | ||
|
|
||
| insertCommand = new ModelInsertCommand(model, this); | ||
| insertCommand->setAncestorRowNumbers(QList<int>() << 1); | ||
| insertCommand->setStartRow(0); | ||
| insertCommand->setEndRow(2); | ||
| insertCommand->doCommand(); | ||
|
|
||
| ObservingObject observer(proxy); | ||
|
|
||
| ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); | ||
| moveCommand->setStartRow(0); | ||
| moveCommand->setEndRow(0); | ||
| moveCommand->setDestAncestors(QList<int>() << 1); | ||
| moveCommand->setDestRow(0); | ||
| moveCommand->doCommand(); | ||
| } | ||
|
|
||
| void tst_ModelTest::testResetThroughProxy() | ||
| { | ||
| DynamicTreeModel *model = new DynamicTreeModel(this); | ||
|
|
||
| ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); | ||
| insertCommand->setStartRow(0); | ||
| insertCommand->setEndRow(2); | ||
| insertCommand->doCommand(); | ||
|
|
||
| QPersistentModelIndex persistent = model->index(0, 0); | ||
|
|
||
| AccessibleProxyModel *proxy = new AccessibleProxyModel(this); | ||
| proxy->setSourceModel(model); | ||
|
|
||
| ObservingObject observer(proxy); | ||
| observer.storePersistent(); | ||
|
|
||
| ModelResetCommand *resetCommand = new ModelResetCommand(model, this); | ||
| resetCommand->setNumCols(0); | ||
| resetCommand->doCommand(); | ||
| } | ||
|
|
||
|
|
||
| QTEST_MAIN(tst_ModelTest) | ||
| #include "tst_modeltest.moc" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /*************************************************************************** | ||
| test_template.cpp | ||
| -------------------------------------- | ||
| Date : Sun Sep 16 12:22:23 AKDT 2007 | ||
| Copyright : (C) 2007 by Gary E. Sherman | ||
| Email : sherman at mrcc dot com | ||
| *************************************************************************** | ||
| * * | ||
| * This program is free software; you can redistribute it and/or modify * | ||
| * it under the terms of the GNU General Public License as published by * | ||
| * the Free Software Foundation; either version 2 of the License, or * | ||
| * (at your option) any later version. * | ||
| * * | ||
| ***************************************************************************/ | ||
| #include <QtTest> | ||
| #include <QDomDocument> | ||
| #include <QFile> | ||
| //header for class being tested | ||
| #include <qgsrulebasedrendererv2.h> | ||
|
|
||
| #if QT_VERSION < 0x40701 | ||
| // See http://hub.qgis.org/issues/4284 | ||
| Q_DECLARE_METATYPE( QVariant ) | ||
| #endif | ||
|
|
||
|
|
||
| class TestQgsRuleBasedRenderer: public QObject | ||
| { | ||
| Q_OBJECT | ||
| private slots: | ||
|
|
||
| void test_load_xml() | ||
| { | ||
| QDomDocument doc; | ||
| xml2domElement( "rulebasedrenderer_simple.xml", doc ); | ||
| QDomElement elem = doc.documentElement(); | ||
|
|
||
| QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); | ||
| QVERIFY( r ); | ||
| check_tree_valid( r->rootRule() ); | ||
| delete r; | ||
| } | ||
|
|
||
| void test_load_invalid_xml() | ||
| { | ||
| QDomDocument doc; | ||
| xml2domElement( "rulebasedrenderer_invalid.xml", doc ); | ||
| QDomElement elem = doc.documentElement(); | ||
|
|
||
| QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); | ||
| QVERIFY( r == NULL ); | ||
| } | ||
|
|
||
| private: | ||
| void xml2domElement( QString testFile, QDomDocument& doc ) | ||
| { | ||
| QString fileName = QString( TEST_DATA_DIR ) + QDir::separator() + testFile; | ||
| QFile f(fileName); | ||
| bool fileOpen = f.open(QIODevice::ReadOnly); | ||
| QVERIFY( fileOpen ); | ||
|
|
||
| QString msg; | ||
| int line, col; | ||
| bool parse = doc.setContent( &f, &msg, &line, &col ); | ||
| QVERIFY( parse ); | ||
| } | ||
|
|
||
| void check_tree_valid( QgsRuleBasedRendererV2::Rule* root ) | ||
| { | ||
| // root must always exist (although it does not have children) | ||
| QVERIFY( root ); | ||
| // and does not have a parent | ||
| QVERIFY( root->parent() == NULL ); | ||
|
|
||
| foreach ( QgsRuleBasedRendererV2::Rule* node, root->children() ) | ||
| check_non_root_rule( node ); | ||
| } | ||
|
|
||
| void check_non_root_rule( QgsRuleBasedRendererV2::Rule* node ) | ||
| { | ||
| qDebug() << node->dump(); | ||
| // children must not be NULL | ||
| QVERIFY( node ); | ||
| // and must have a parent | ||
| QVERIFY( node->parent() ); | ||
| // check that all children are okay | ||
| foreach ( QgsRuleBasedRendererV2::Rule* child, node->children() ) | ||
| check_non_root_rule( child ); | ||
| } | ||
|
|
||
| }; | ||
|
|
||
| QTEST_MAIN( TestQgsRuleBasedRenderer ) | ||
|
|
||
| #include "moc_testqgsrulebasedrenderer.cxx" | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| #include "testrendererv2gui.h" | ||
|
|
||
| #include <qgsapplication.h> | ||
| #include <qgsmapcanvas.h> | ||
| #include <qgsvectorlayer.h> | ||
| #include <qgsmaplayerregistry.h> | ||
| #include <qgsproject.h> | ||
| #include <qgsrendererv2propertiesdialog.h> | ||
| #include <qgsstylev2.h> | ||
|
|
||
| #include <QApplication> | ||
| #include <QToolBar> | ||
|
|
||
| TestRendererV2GUI::TestRendererV2GUI(QWidget *parent) : | ||
| QMainWindow(parent) | ||
| { | ||
| resize(640,480); | ||
|
|
||
| QToolBar* toolBar = addToolBar("Actions"); | ||
| toolBar->addAction( "set renderer", this, SLOT(setRenderer()) ); | ||
|
|
||
| mMapCanvas = new QgsMapCanvas(this); | ||
| mMapCanvas->setCanvasColor( Qt::white ); | ||
| setCentralWidget(mMapCanvas); | ||
|
|
||
| connect( QgsProject::instance(), SIGNAL(readProject(QDomDocument)), mMapCanvas, SLOT(readProject(QDomDocument))); | ||
| } | ||
|
|
||
| void TestRendererV2GUI::loadLayers() | ||
| { | ||
| // load just first vector layer | ||
| QList<QgsMapCanvasLayer> canvasLayers; | ||
| foreach (QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values()) | ||
| { | ||
| if ( layer->type() == QgsMapLayer::VectorLayer ) | ||
| canvasLayers << QgsMapCanvasLayer( layer ); | ||
| } | ||
|
|
||
| mMapCanvas->setLayerSet(canvasLayers); | ||
| } | ||
|
|
||
| void TestRendererV2GUI::setRenderer() | ||
| { | ||
| QgsMapLayer* layer = mMapCanvas->layer(0); | ||
| Q_ASSERT( layer ); | ||
| Q_ASSERT( layer->type() == QgsMapLayer::VectorLayer ); | ||
| QgsVectorLayer* vlayer = static_cast<QgsVectorLayer*>(layer); | ||
|
|
||
| QgsRendererV2PropertiesDialog dlg( vlayer, QgsStyleV2::defaultStyle() ); | ||
| dlg.exec(); | ||
|
|
||
| mMapCanvas->refresh(); | ||
| } | ||
|
|
||
| int main(int argc, char* argv[]) | ||
| { | ||
| QApplication app(argc, argv); | ||
|
|
||
| if ( argc < 2 ) | ||
| { | ||
| qDebug( "Provide a project file name with at least one vector layer!" ); | ||
| return 1; | ||
| } | ||
|
|
||
| QgsApplication::init(); | ||
| QgsApplication::initQgis(); | ||
|
|
||
| TestRendererV2GUI gui; | ||
|
|
||
| QString projectFileName( argv[1] ); | ||
| QgsProject::instance()->setFileName( projectFileName ); | ||
| bool res = QgsProject::instance()->read(); | ||
| if ( !res ) | ||
| { | ||
| qDebug("Failed to open project!"); | ||
| return 1; | ||
| } | ||
|
|
||
| // the layers are in the registry - now load them! | ||
| gui.loadLayers(); | ||
|
|
||
| gui.show(); | ||
| return app.exec(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| #ifndef TESTRENDERERV2GUI_H | ||
| #define TESTRENDERERV2GUI_H | ||
|
|
||
| #include <QMainWindow> | ||
|
|
||
| class QgsMapCanvas; | ||
|
|
||
| class TestRendererV2GUI : public QMainWindow | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| explicit TestRendererV2GUI(QWidget *parent = 0); | ||
| void loadLayers(); | ||
|
|
||
| signals: | ||
|
|
||
| public slots: | ||
| void setRenderer(); | ||
|
|
||
| protected: | ||
| QgsMapCanvas* mMapCanvas; | ||
| }; | ||
|
|
||
| #endif // TESTRENDERERV2GUI_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| <renderer-v2 symbollevels="0" type="RuleRenderer"> | ||
| </renderer-v2> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <renderer-v2 symbollevels="0" type="RuleRenderer"> | ||
| <rules> | ||
| <rule filter="type=3" symbol="0" label="type3"/> | ||
| <rule filter="type=1" symbol="1" label="type1"/> | ||
| </rules> | ||
| <symbols> | ||
| <symbol outputUnit="MM" alpha="1" type="line" name="0"> | ||
| <layer pass="0" class="SimpleLine" locked="0"> | ||
| <prop k="capstyle" v="square"/> | ||
| <prop k="color" v="220,0,0,255"/> | ||
| <prop k="customdash" v="5;2"/> | ||
| <prop k="joinstyle" v="bevel"/> | ||
| <prop k="offset" v="0"/> | ||
| <prop k="penstyle" v="solid"/> | ||
| <prop k="use_custom_dash" v="0"/> | ||
| <prop k="width" v="0.26"/> | ||
| </layer> | ||
| </symbol> | ||
| <symbol outputUnit="MM" alpha="1" type="line" name="1"> | ||
| <layer pass="0" class="SimpleLine" locked="0"> | ||
| <prop k="capstyle" v="square"/> | ||
| <prop k="color" v="94,116,254,255"/> | ||
| <prop k="customdash" v="5;2"/> | ||
| <prop k="joinstyle" v="bevel"/> | ||
| <prop k="offset" v="0"/> | ||
| <prop k="penstyle" v="solid"/> | ||
| <prop k="use_custom_dash" v="0"/> | ||
| <prop k="width" v="0.26"/> | ||
| </layer> | ||
| </symbol> | ||
| </symbols> | ||
| </renderer-v2> |