Commit 1619b3e5 authored by Sandro Knauß's avatar Sandro Knauß
Browse files

Make it possible to update personnodes for the reparenting model

* show subcols again
* add tests to make sure that update works

KOLAB: #3743
parent 6719639a
......@@ -351,7 +351,7 @@ class CollectionFilter : public QSortFilterProxyModel
const Akonadi::Collection &col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
//We filter the user folders because we insert person nodes for user folders.
if ( (attr && attr->collectionNamespace().startsWith("user"))
if ( (attr && attr->collectionNamespace().startsWith("usertoplevel"))
|| col.name().contains(QLatin1String("Other Users"))) {
return false;
}
......
......@@ -110,7 +110,6 @@ bool CollectionNode::isDuplicateOf(const QModelIndex& sourceIndex)
return (sourceIndex.data(Akonadi::EntityTreeModel::CollectionIdRole).value<Akonadi::Collection::Id>() == mCollection.id());
}
PersonNode::PersonNode(ReparentingModel& personModel, const Person& person, const QModelIndex &colIndex)
: Node(personModel),
mPerson(person),
......@@ -121,7 +120,6 @@ PersonNode::PersonNode(ReparentingModel& personModel, const Person& person, cons
}
PersonNode::~PersonNode()
{
......@@ -223,46 +221,48 @@ bool PersonNode::isDuplicateOf(const QModelIndex& sourceIndex)
return (sourceIndex.data(PersonRole).value<Person>().uid == mPerson.uid);
}
void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
Person PersonNodeManager::person(const QModelIndex &sourceIndex)
{
Person person;
const Akonadi::Collection col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
// kDebug() << col.displayName() << col.enabled();
if (col.isValid()) {
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
if (attr && attr->collectionNamespace() == "usertoplevel") {
kDebug() << "Found user folder, creating person node " << col.displayName();
Person person;
person.name = col.displayName();
person.mail = QString::fromUtf8(attr->mail());
person.ou = QString::fromUtf8(attr->ou());
person.uid = col.name();
person.rootCollection = col.id();
model.addNode(ReparentingModel::Node::Ptr(new PersonNode(model, person, sourceIndex)));
}
}
return person;
}
void PersonNodeManager::checkSourceIndexRemoval(const QModelIndex &sourceIndex)
void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
{
const Akonadi::Collection col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
// kDebug() << col.displayName() << col.enabled();
if (col.isValid()) {
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
if (attr && attr->collectionNamespace() == "usertoplevel") {
kDebug() << "Found user folder, removing person node " << col.displayName();
Person person;
person.name = col.displayName();
person.mail = QString::fromUtf8(attr->mail());
person.ou = QString::fromUtf8(attr->ou());
person.uid = col.name();
person.rootCollection = col.id();
model.removeNode(PersonNode(model, person, sourceIndex));
}
const Person &p = person(sourceIndex);
if (p.rootCollection > -1) {
model.addNode(ReparentingModel::Node::Ptr(new PersonNode(model, p, sourceIndex)));
}
}
void PersonNodeManager::updateSourceIndex(const QModelIndex &sourceIndex)
{
const Person &p = person(sourceIndex);
if (p.rootCollection > -1) {
model.updateNode(ReparentingModel::Node::Ptr(new PersonNode(model, p, sourceIndex)));
}
}
void PersonNodeManager::checkSourceIndexRemoval(const QModelIndex &sourceIndex)
{
const Person &p = person(sourceIndex);
if (p.rootCollection > -1) {
model.removeNode(PersonNode(model, p, sourceIndex));
}
}
CollectionSearchJob::CollectionSearchJob(const QString& searchString, QObject* parent)
: KJob(parent),
mSearchString(searchString)
......
......@@ -133,8 +133,10 @@ class PersonNodeManager : public ReparentingModel::NodeManager
public:
PersonNodeManager(ReparentingModel &personModel) : ReparentingModel::NodeManager(personModel){};
private:
Person person(const QModelIndex &sourceIndex);
void checkSourceIndex(const QModelIndex &sourceIndex);
void checkSourceIndexRemoval(const QModelIndex &sourceIndex);
void updateSourceIndex(const QModelIndex &sourceIndex);
};
class CollectionSearchJob : public KJob
......
......@@ -279,7 +279,13 @@ void ReparentingModel::updateNode(const ReparentingModel::Node::Ptr &node)
Q_FOREACH(const ReparentingModel::Node::Ptr &existing, mProxyNodes) {
if (*existing == *node) {
node->parent = existing->parent;
node->children = existing->children;
Q_FOREACH(ReparentingModel::Node::Ptr child, existing->children) {
child->parent = node.data();
}
node->sourceIndex = existing->sourceIndex;
int r = row(existing.data());
mProxyNodes.replace(mProxyNodes.indexOf(existing), node);
existing->parent->children.replace(r, node);
const QModelIndex i = index(node.data());
Q_ASSERT(i.row() == r);
......@@ -468,7 +474,7 @@ void ReparentingModel::onSourceRowsInserted(QModelIndex parent, int start, int e
Q_ASSERT(validateNode(parentNode));
}
Q_ASSERT(parentNode);
//Remove any duplicates that we are going to replace
removeDuplicates(sourceIndex);
......@@ -588,9 +594,8 @@ void ReparentingModel::onSourceLayoutChanged()
void ReparentingModel::onSourceDataChanged(QModelIndex begin, QModelIndex end)
{
for (int row = begin.row(); row <= end.row(); row++) {
mNodeManager->checkSourceIndex(sourceModel()->index(row, begin.column(), begin.parent()));
mNodeManager->updateSourceIndex(sourceModel()->index(row, begin.column(), begin.parent()));
}
emit dataChanged(mapFromSource(begin), mapFromSource(end));
}
void ReparentingModel::onSourceModelAboutToBeReset()
......@@ -614,6 +619,9 @@ ReparentingModel::Node *ReparentingModel::extractNode(const QModelIndex &index)
QModelIndex ReparentingModel::index(int row, int column, const QModelIndex& parent) const
{
if (row < 0 || column != 0) {
return QModelIndex();
}
// kDebug() << parent << row;
const Node *parentNode;
if (parent.isValid()) {
......@@ -623,7 +631,7 @@ QModelIndex ReparentingModel::index(int row, int column, const QModelIndex& pare
}
//At least QAbstractItemView expects that we deal with this properly (see rowsAboutToBeRemoved "find the next visible and enabled item")
//Also QAbstractItemModel::match does all kinds of weird shit including passing row=-1
if (parentNode->children.size() <= row || row < 0) {
if (parentNode->children.size() <= row) {
return QModelIndex();
}
Node *node = parentNode->children.at(row).data();
......@@ -709,15 +717,14 @@ void ReparentingModel::reparentSourceNodes(const Node::Ptr &proxyNode)
//Reparent source nodes according to the provided rules
Q_FOREACH(Node *n, mSourceNodes) {
if (proxyNode->adopts(n->sourceIndex)) {
const int oldRow = n->sourceIndex.row();
beginRemoveRows(index(n->parent), oldRow, oldRow);
//We lie about the row being removed already, but the view can deal with that better than if we call endRemoveRows after beginInsertRows
endRemoveRows();
//kDebug() << "reparenting" << n->data(Qt::DisplayRole).toString() << "from" << n->parent->data(Qt::DisplayRole).toString()
// << "to" << proxyNode->data(Qt::DisplayRole).toString();
const int oldRow = row(n);
const int newRow = proxyNode->children.size();
beginInsertRows(index(proxyNode.data()), newRow, newRow);
beginMoveRows(index(n->parent), oldRow, oldRow,
index(proxyNode.data()), newRow);
proxyNode->reparent(n);
endInsertRows();
endMoveRows();
Q_ASSERT(validateNode(n));
}
}
......@@ -745,7 +752,9 @@ void ReparentingModel::rebuildAll()
QVariant ReparentingModel::data(const QModelIndex& proxyIndex, int role) const
{
Q_ASSERT(proxyIndex.isValid());
if (!proxyIndex.isValid()) {
return QVariant();
}
const Node *node = extractNode(proxyIndex);
if (node->isSourceNode()) {
return sourceModel()->data(mapToSource(proxyIndex), role);
......@@ -755,6 +764,9 @@ QVariant ReparentingModel::data(const QModelIndex& proxyIndex, int role) const
bool ReparentingModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid()) {
return false;
}
Q_ASSERT(index.isValid());
if (!sourceModel()) {
return false;
......@@ -831,6 +843,11 @@ int ReparentingModel::rowCount(const QModelIndex& parent) const
if (!parent.isValid()) {
return mRootNode.children.size();
}
if (parent.column() != 0) {
return 0;
}
Node *node = extractNode(parent);
return node->children.size();
}
......
......@@ -79,6 +79,7 @@ public:
//Allows the implementation to create proxy nodes as necessary
virtual void checkSourceIndex(const QModelIndex &/* sourceIndex */){};
virtual void checkSourceIndexRemoval(const QModelIndex &/* sourceIndex */){};
virtual void updateSourceIndex(const QModelIndex & sourceIndex ){checkSourceIndex(sourceIndex);};
};
public:
......
......@@ -5,6 +5,7 @@ include_directories(
set(reparentingmodeltest_SRCS
reparentingmodeltest.cpp
../reparentingmodel.cpp
modeltest.cpp
)
kde4_add_unit_test(reparentingmodeltest NOGUI ${reparentingmodeltest_SRCS})
......
This diff is collapsed.
/****************************************************************************
**
** Copyright (C) 2007 Trolltech ASA. All rights reserved.
**
** This file is part of the Qt Concurrent project on Trolltech Labs.
**
** This file may be used under the terms of the GNU General Public
** License version 2.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 GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
//krazy:excludeall=style
#ifndef MODELTEST_H
#define MODELTEST_H
#include <QtCore/QObject>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStack>
class ModelTest : public QObject
{
Q_OBJECT
public:
explicit 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 modelAboutToBeReset();
void modelReset();
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 );
void rowsAboutToBeMoved ( const QModelIndex &, int, int, const QModelIndex &, int);
void rowsMoved ( const QModelIndex &, int, int, const QModelIndex &, int );
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
......@@ -33,7 +33,9 @@ public:
DummyNode(ReparentingModel &personModel, const QString &name, const QString &data=QString())
: ReparentingModel::Node(personModel),
mName(name),
mData(data)
mData(data),
mUid(name),
mParent(QLatin1String("orphan"))
{}
virtual ~DummyNode(){};
......@@ -41,15 +43,21 @@ public:
virtual bool operator==(const Node &node) const {
const DummyNode *dummyNode = dynamic_cast<const DummyNode*>(&node);
if (dummyNode) {
return (dummyNode->mName == mName);
return (dummyNode->mUid == mUid);
}
return false;
}
QString mUid;
QString mParent;
private:
virtual QVariant data(int role) const {
if (role == Qt::DisplayRole) {
return mName;
if (mName != mUid) {
return QString(mUid+QLatin1Char('-')+mName);
} else {
return mName;
}
} else if (role == Qt::UserRole) {
return mData;
}
......@@ -61,11 +69,11 @@ private:
return false;
}
virtual bool isDuplicateOf(const QModelIndex& sourceIndex) {
return (sourceIndex.data().toString() == mName);
return (sourceIndex.data().toString() == mUid);
}
virtual bool adopts(const QModelIndex& sourceIndex) {
return sourceIndex.data().toString().contains(QLatin1String("orphan"));
return sourceIndex.data().toString().contains(mParent);
}
QString mName;
......@@ -148,6 +156,7 @@ private Q_SLOTS:
void testNestedDeduplicateProxyNodeFirst();
void testUpdateNode();
void testReparent();
void testReparentSubcollections();
void testReparentResetWithoutCrash();
void testAddReparentedSourceItem();
void testRemoveReparentedSourceItem();
......@@ -413,6 +422,63 @@ void ReparentingModelTest::testReparent()
QCOMPARE(reparentingModel.rowCount(getIndex("proxy1", reparentingModel)), 1);
}
void ReparentingModelTest::testReparentSubcollections()
{
QStandardItemModel sourceModel;
ReparentingModel reparentingModel;
reparentingModel.setSourceModel(&sourceModel);
/* Source structure
-- +
-- + orphan
-- + col1
-- sub1
-- sub2
-- col2
*/
sourceModel.appendRow(new QStandardItem(QLatin1String("orphan")));
sourceModel.item(0,0)->appendRow(new QStandardItem(QLatin1String("col1")));
sourceModel.item(0,0)->child(0,0)->appendRow(new QStandardItem(QLatin1String("sub1")));
sourceModel.item(0,0)->child(0,0)->appendRow(new QStandardItem(QLatin1String("sub2")));
sourceModel.item(0,0)->appendRow(new QStandardItem(QLatin1String("col2")));
DummyNode *node = new DummyNode(reparentingModel, QLatin1String("col1"));
node->mUid = QLatin1String("uid");
node->mParent = QLatin1String("col");
/* new srutcure:
-- +
-- orphan
-- + uid-col1
-- + col1
-- sub1
-- sub2
-- col2
*/
reparentingModel.addNode(ReparentingModel::Node::Ptr(node));
QTest::qWait(0);
QCOMPARE(reparentingModel.rowCount(QModelIndex()), 2);
QVERIFY(getIndex("col1", reparentingModel).isValid());
QCOMPARE(getIndex("col1", reparentingModel).parent(), getIndex("uid-col1", reparentingModel));
QCOMPARE(reparentingModel.rowCount(getIndex("col1", reparentingModel)), 2);
QCOMPARE(reparentingModel.rowCount(getIndex("uid-col1", reparentingModel)), 2);
node = new DummyNode(reparentingModel, QLatin1String("xxx"));
node->mUid = QLatin1String("uid");
node->mParent = QLatin1String("col");
// same structure but new data
reparentingModel.updateNode(ReparentingModel::Node::Ptr(node));
QTest::qWait(0);
QCOMPARE(getIndex("col1", reparentingModel).parent(), getIndex("uid-xxx", reparentingModel));
QCOMPARE(reparentingModel.rowCount(getIndex("col1", reparentingModel)), 2);
QCOMPARE(reparentingModel.rowCount(getIndex("uid-xxx", reparentingModel)), 2);
}
/*
* This test ensures we properly deal with reparented source nodes if the model is reset.
* This is important since source nodes are removed during the model reset while the proxy nodes (to which the source nodes have been reparented) remain.
......@@ -677,3 +743,4 @@ void ReparentingModelTest::testRemoveNodeByNodeManagerWithDataChanged()
QTEST_MAIN(ReparentingModelTest)
#include "reparentingmodeltest.moc"
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment