[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.
|