[Ktutorial-commits] SF.net SVN: ktutorial:[113] trunk/ktutorial/ktutorial-editor
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2010-03-06 17:12:55
|
Revision: 113 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=113&view=rev Author: danxuliu Date: 2010-03-06 17:12:46 +0000 (Sat, 06 Mar 2010) Log Message: ----------- Watch the signals emitted by the TreeItems added to a TreeModel and notify the views to be updated as needed. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/TreeModel.cpp trunk/ktutorial/ktutorial-editor/src/view/TreeModel.h trunk/ktutorial/ktutorial-editor/tests/unit/view/TreeModelTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/TreeModel.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TreeModel.cpp 2010-03-06 17:11:26 UTC (rev 112) +++ trunk/ktutorial/ktutorial-editor/src/view/TreeModel.cpp 2010-03-06 17:12:46 UTC (rev 113) @@ -26,6 +26,8 @@ QAbstractItemModel(parent), mRootItem(rootItem) { Q_ASSERT(rootItem); + + registerTreeItemForUpdates(rootItem); } TreeModel::~TreeModel() { @@ -114,3 +116,79 @@ return 1; } + +//private: + +void TreeModel::registerTreeItemForUpdates(TreeItem* item) { + connect(item, SIGNAL(childAboutToBeInserted(TreeItem*, int)), + this, SLOT(treeItemAboutToBeInserted(TreeItem*, int))); + connect(item, SIGNAL(childInserted(TreeItem*)), + this, SLOT(treeItemInserted(TreeItem*))); + connect(item, SIGNAL(childAboutToBeRemoved(TreeItem*)), + this, SLOT(treeItemAboutToBeRemoved(TreeItem*))); + connect(item, SIGNAL(childRemoved(TreeItem*)), + this, SLOT(treeItemRemoved(TreeItem*))); + connect(item, SIGNAL(dataChanged(TreeItem*)), + this, SLOT(treeItemDataChanged(TreeItem*))); + + for (int i=0; i<item->childCount(); ++i) { + registerTreeItemForUpdates(item->child(i)); + } +} + +void TreeModel::deregisterTreeItemFromUpdates(TreeItem* item) { + disconnect(item, SIGNAL(childAboutToBeInserted(TreeItem*, int)), + this, SLOT(treeItemAboutToBeInserted(TreeItem*, int))); + disconnect(item, SIGNAL(childInserted(TreeItem*)), + this, SLOT(treeItemInserted(TreeItem*))); + disconnect(item, SIGNAL(childAboutToBeRemoved(TreeItem*)), + this, SLOT(treeItemAboutToBeRemoved(TreeItem*))); + disconnect(item, SIGNAL(childRemoved(TreeItem*)), + this, SLOT(treeItemRemoved(TreeItem*))); + disconnect(item, SIGNAL(dataChanged(TreeItem*)), + this, SLOT(treeItemDataChanged(TreeItem*))); + + for (int i=0; i<item->childCount(); ++i) { + deregisterTreeItemFromUpdates(item->child(i)); + } +} + +QModelIndex TreeModel::getParentIndex(TreeItem* item) { + QModelIndex parent; + + if (item->parent() != mRootItem) { + parent = createIndex(item->parent()->childIndex(), 0, item->parent()); + } + + return parent; +} + +//private slots: + +void TreeModel::treeItemAboutToBeInserted(TreeItem* item, int treeItemIndex) { + beginInsertRows(getParentIndex(item), treeItemIndex, treeItemIndex); +} + +void TreeModel::treeItemInserted(TreeItem* item) { + endInsertRows(); + + registerTreeItemForUpdates(item); +} + +void TreeModel::treeItemAboutToBeRemoved(TreeItem* item) { + int treeItemIndex = item->childIndex(); + + beginRemoveRows(getParentIndex(item), treeItemIndex, treeItemIndex); +} + +void TreeModel::treeItemRemoved(TreeItem* item) { + deregisterTreeItemFromUpdates(item); + + endRemoveRows(); +} + +void TreeModel::treeItemDataChanged(TreeItem* item) { + QModelIndex index = createIndex(item->childIndex(), 0, item); + + emit dataChanged(index, index); +} Modified: trunk/ktutorial/ktutorial-editor/src/view/TreeModel.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/TreeModel.h 2010-03-06 17:11:26 UTC (rev 112) +++ trunk/ktutorial/ktutorial-editor/src/view/TreeModel.h 2010-03-06 17:12:46 UTC (rev 113) @@ -40,6 +40,9 @@ * Each TreeItem provides the data to be shown by its associated model item. * Display role shows the text of the TreeItem. * + * Changes in the tree layout or data provided by the TreeItems are notified to + * the views, so they can be updated as needed. + * * @see TreeItem */ class TreeModel: public QAbstractItemModel { @@ -145,6 +148,70 @@ */ TreeItem* mRootItem; + /** + * Starts watching changes in the given item and its children. + * When something change in a registered item (the data is modified, a child + * is added or a child is removed), the views are notified so they can be + * updated as needed. + * + * @param item The item to register. + */ + void registerTreeItemForUpdates(TreeItem* item); + + /** + * Stops watching changes in the given item and its children. + * + * @param item The item to deregister. + */ + void deregisterTreeItemFromUpdates(TreeItem* item); + + /** + * Returns the index for the parent of the given item. + * + * @param item The item to get the index of its parent. + * @return The parent index. + */ + QModelIndex getParentIndex(TreeItem* item); + +private Q_SLOTS: + + /** + * Notifies the views that the given item is going to be added. + * + * @param item The item that is going to be added. + * @param treeItemIndex The index in the parent item where the item is going + * to be added. + */ + void treeItemAboutToBeInserted(TreeItem* item, int treeItemIndex); + + /** + * Notifies the views that the given item has been added, + * + * @param item The item added. + */ + void treeItemInserted(TreeItem* item); + + /** + * Notifies the views that the given item is going to be removed. + * + * @param item The item that is going to be removed. + */ + void treeItemAboutToBeRemoved(TreeItem* item); + + /** + * Notifies the views that the given item has been removed. + * + * @param item The item removed. + */ + void treeItemRemoved(TreeItem* item); + + /** + * Notifies the views about a change in the data of the given item. + * + * @param item The item that changed. + */ + void treeItemDataChanged(TreeItem* item); + }; #endif Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/TreeModelTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/TreeModelTest.cpp 2010-03-06 17:11:26 UTC (rev 112) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/TreeModelTest.cpp 2010-03-06 17:12:46 UTC (rev 113) @@ -27,6 +27,8 @@ private slots: + void initTestCase(); + void init(); void cleanup(); @@ -36,6 +38,18 @@ void testConstructorSingleNestedItem(); void testConstructorSeveralNestedItems(); + void testAppendTopLevelItem(); + void testInsertTopLevelItem(); + void testRemoveTopLevelItem(); + + void testChangeChildrenOfNestedItemAddedBeforeConstructor(); + void testChangeChildrenOfNestedItemAddedAfterConstructor(); + + void testChangeChildrenOfRemovedItem(); + void testChangeChildrenOfRemovedNestedItem(); + + void testChangeItemData(); + void testDataWithInvalidIndex(); void testDataWithInvalidRole(); @@ -51,6 +65,8 @@ private: + int mModelIndexType; + TreeItem* mEmptyRootItem; TreeItem* mSingleItem; TreeItem* mSeveralFlatItems; @@ -60,9 +76,13 @@ void assertItem(const QModelIndex& index, const QString& displayRoleData, int childrenCount, const QModelIndex& parent) const; + void assertSignal(const QSignalSpy& spy, int signalIndex, + const QModelIndex& index, int row) const; + }; class StubTreeItem: public TreeItem { +Q_OBJECT public: QString mText; @@ -76,8 +96,19 @@ return mText; } + void setText(const QString& text) { + mText = text; + + emit dataChanged(this); + } + }; +void TreeModelTest::initTestCase() { + //QModelIndex must be registered in order to be used with QSignalSpy + mModelIndexType = qRegisterMetaType<QModelIndex>("QModelIndex"); +} + void TreeModelTest::init() { mEmptyRootItem = new StubTreeItem("root"); @@ -202,6 +233,289 @@ assertItem(index, "root-3-2", 0, parent); } +//QModelIndex must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(QModelIndex); + +void TreeModelTest::testAppendTopLevelItem() { + TreeItem* rootItem = mEmptyRootItem; + mEmptyRootItem = 0; + + TreeModel model(rootItem); + + QSignalSpy aboutToSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + + rootItem->appendChild(new StubTreeItem("root-1", rootItem)); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1", 0, QModelIndex()); + + //The proper way to check this would be asserting not only that the signals + //were emitted, but that when they were emitted the item wasn't and was + //already added (depending on the signal). However, that is too much effort + //for little gain. + QCOMPARE(aboutToSpy.count(), 1); + assertSignal(aboutToSpy, 0, QModelIndex(), 0); + QCOMPARE(insertedSpy.count(), 1); + assertSignal(insertedSpy, 0, QModelIndex(), 0); +} + +void TreeModelTest::testInsertTopLevelItem() { + TreeItem* rootItem = mSeveralFlatItems; + mSeveralFlatItems = 0; + + TreeModel model(rootItem); + + QSignalSpy aboutToSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + + rootItem->insertChild(new StubTreeItem("root-2new", rootItem), 1); + + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1", 0, QModelIndex()); + + index = model.index(1, 0); + assertItem(index, "root-2new", 0, QModelIndex()); + + index = model.index(2, 0); + assertItem(index, "root-2", 0, QModelIndex()); + + index = model.index(3, 0); + assertItem(index, "root-3", 0, QModelIndex()); + + QCOMPARE(aboutToSpy.count(), 1); + assertSignal(aboutToSpy, 0, QModelIndex(), 1); + QCOMPARE(insertedSpy.count(), 1); + assertSignal(insertedSpy, 0, QModelIndex(), 1); +} + +void TreeModelTest::testRemoveTopLevelItem() { + TreeItem* rootItem = mSeveralFlatItems; + mSeveralFlatItems = 0; + + TreeModel model(rootItem); + + QSignalSpy aboutToSpy(&model, + SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); + QSignalSpy removedSpy(&model, SIGNAL(rowsRemoved(QModelIndex, int, int))); + + TreeItem* itemToBeRemoved = rootItem->child(1); + rootItem->removeChild(itemToBeRemoved); + delete itemToBeRemoved; + + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1", 0, QModelIndex()); + + index = model.index(1, 0); + assertItem(index, "root-3", 0, QModelIndex()); + + QCOMPARE(aboutToSpy.count(), 1); + assertSignal(aboutToSpy, 0, QModelIndex(), 1); + QCOMPARE(removedSpy.count(), 1); + assertSignal(removedSpy, 0, QModelIndex(), 1); +} + + +void TreeModelTest::testChangeChildrenOfNestedItemAddedBeforeConstructor() { + TreeItem* rootItem = mSingleNestedItem; + mSingleNestedItem = 0; + + TreeModel model(rootItem); + TreeItem* item = rootItem->child(0)->child(0); + + QSignalSpy aboutToInsertSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + QSignalSpy aboutToRemoveSpy(&model, + SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); + QSignalSpy removedSpy(&model, SIGNAL(rowsRemoved(QModelIndex, int, int))); + + StubTreeItem childToBeRemoved("root-1-1-3", item); + item->appendChild(new StubTreeItem("root-1-1-1", item)); + item->appendChild(&childToBeRemoved); + item->insertChild(new StubTreeItem("root-1-1-2", item), 1); + item->removeChild(&childToBeRemoved); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1", 1, QModelIndex()); + + QModelIndex parent = index; + index = model.index(0, 0, parent); + assertItem(index, "root-1-1", 2, parent); + + parent = index; + index = model.index(0, 0, parent); + assertItem(index, "root-1-1-1", 0, parent); + + index = model.index(1, 0, parent); + assertItem(index, "root-1-1-2", 0, parent); + + QCOMPARE(aboutToInsertSpy.count(), 3); + assertSignal(aboutToInsertSpy, 0, parent, 0); + assertSignal(aboutToInsertSpy, 1, parent, 1); + assertSignal(aboutToInsertSpy, 2, parent, 1); + QCOMPARE(insertedSpy.count(), 3); + assertSignal(insertedSpy, 0, parent, 0); + assertSignal(insertedSpy, 1, parent, 1); + assertSignal(insertedSpy, 2, parent, 1); + QCOMPARE(aboutToRemoveSpy.count(), 1); + assertSignal(aboutToRemoveSpy, 0, parent, 2); + QCOMPARE(removedSpy.count(), 1); + assertSignal(removedSpy, 0, parent, 2); +} + +void TreeModelTest::testChangeChildrenOfNestedItemAddedAfterConstructor() { + TreeItem* rootItem = mEmptyRootItem; + mEmptyRootItem = 0; + + TreeModel model(rootItem); + rootItem->appendChild(new StubTreeItem("root-1", rootItem)); + + TreeItem* item = new StubTreeItem("root-1-1", rootItem->child(0)); + rootItem->child(0)->appendChild(item); + + QSignalSpy aboutToInsertSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + QSignalSpy aboutToRemoveSpy(&model, + SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); + QSignalSpy removedSpy(&model, SIGNAL(rowsRemoved(QModelIndex, int, int))); + + StubTreeItem childToBeRemoved("root-1-1-3", item); + item->appendChild(new StubTreeItem("root-1-1-1", item)); + item->appendChild(&childToBeRemoved); + item->insertChild(new StubTreeItem("root-1-1-2", item), 1); + item->removeChild(&childToBeRemoved); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1", 1, QModelIndex()); + + QModelIndex parent = index; + index = model.index(0, 0, parent); + assertItem(index, "root-1-1", 2, parent); + + parent = index; + index = model.index(0, 0, parent); + assertItem(index, "root-1-1-1", 0, parent); + + index = model.index(1, 0, parent); + assertItem(index, "root-1-1-2", 0, parent); + + QCOMPARE(aboutToInsertSpy.count(), 3); + assertSignal(aboutToInsertSpy, 0, parent, 0); + assertSignal(aboutToInsertSpy, 1, parent, 1); + assertSignal(aboutToInsertSpy, 2, parent, 1); + QCOMPARE(insertedSpy.count(), 3); + assertSignal(insertedSpy, 0, parent, 0); + assertSignal(insertedSpy, 1, parent, 1); + assertSignal(insertedSpy, 2, parent, 1); + QCOMPARE(aboutToRemoveSpy.count(), 1); + assertSignal(aboutToRemoveSpy, 0, parent, 2); + QCOMPARE(removedSpy.count(), 1); + assertSignal(removedSpy, 0, parent, 2); +} + +void TreeModelTest::testChangeChildrenOfRemovedItem() { + TreeItem* rootItem = mEmptyRootItem; + mEmptyRootItem = 0; + + TreeModel model(rootItem); + + StubTreeItem item("root-1", rootItem); + rootItem->appendChild(&item); + rootItem->removeChild(&item); + + QSignalSpy aboutToSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + + item.appendChild(new StubTreeItem("root-1-1", &item)); + + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QCOMPARE(aboutToSpy.count(), 0); + QCOMPARE(insertedSpy.count(), 0); +} + +void TreeModelTest::testChangeChildrenOfRemovedNestedItem() { + TreeItem* rootItem = mEmptyRootItem; + mEmptyRootItem = 0; + + TreeModel model(rootItem); + + StubTreeItem item("root-1", rootItem); + TreeItem* nestedItem = new StubTreeItem("root-1-1", &item); + item.appendChild(nestedItem); + rootItem->appendChild(&item); + rootItem->removeChild(&item); + + QSignalSpy aboutToSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + + nestedItem->appendChild(new StubTreeItem("root-1-1-1", nestedItem)); + + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QCOMPARE(aboutToSpy.count(), 0); + QCOMPARE(insertedSpy.count(), 0); +} + +void TreeModelTest::testChangeItemData() { + TreeItem* rootItem = mEmptyRootItem; + mEmptyRootItem = 0; + + StubTreeItem* item = new StubTreeItem("root-1", rootItem); + rootItem->appendChild(item); + TreeModel model(rootItem); + + QSignalSpy dataChangedSpy(&model, + SIGNAL(dataChanged(QModelIndex, QModelIndex))); + + item->setText("root-1modified"); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("root")); + + QModelIndex index = model.index(0, 0); + assertItem(index, "root-1modified", 0, QModelIndex()); + + QCOMPARE(dataChangedSpy.count(), 1); + QVariant argument = dataChangedSpy.at(0).at(0); + QCOMPARE(argument.userType(), mModelIndexType); + QCOMPARE(qvariant_cast<QModelIndex>(argument), index); + argument = dataChangedSpy.at(0).at(1); + QCOMPARE(argument.userType(), mModelIndexType); + QCOMPARE(qvariant_cast<QModelIndex>(argument), index); +} + void TreeModelTest::testDataWithInvalidIndex() { TreeModel model(mSingleItem); mSingleItem = 0; @@ -290,6 +604,23 @@ QCOMPARE(model->columnCount(index), 1); } +void TreeModelTest::assertSignal(const QSignalSpy& spy, int signalIndex, + const QModelIndex& index, int row) const { + QCOMPARE(spy.at(signalIndex).count(), 3); + + QVariant argument = spy.at(signalIndex).at(0); + QCOMPARE(argument.userType(), mModelIndexType); + QCOMPARE(qvariant_cast<QModelIndex>(argument), index); + + argument = spy.at(signalIndex).at(1); + QCOMPARE(argument.type(), QVariant::Int); + QCOMPARE(argument.toInt(), row); + + argument = spy.at(signalIndex).at(2); + QCOMPARE(argument.type(), QVariant::Int); + QCOMPARE(argument.toInt(), row); +} + QTEST_MAIN(TreeModelTest) #include "TreeModelTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |