Branch 'kolab/integration/4.13.0' - korganizer/akonadicollectionview.cpp korganizer/views
Sandro Knauß
knauss at kolabsys.com
Thu Oct 2 20:00:14 CEST 2014
korganizer/akonadicollectionview.cpp | 10
korganizer/views/collectionview/controller.cpp | 276 +++++++++-
korganizer/views/collectionview/controller.h | 28 -
korganizer/views/collectionview/reparentingmodel.cpp | 34 +
korganizer/views/collectionview/reparentingmodel.h | 2
korganizer/views/collectionview/tests/reparentingmodeltest.cpp | 50 +
6 files changed, 367 insertions(+), 33 deletions(-)
New commits:
commit bf819e0eb26fba226b79fbcb0f38a0f977b4210c
Author: Sandro Knauà <knauss at kolabsys.com>
Date: Thu Oct 2 19:03:26 2014 +0200
Implement ldap search in calendar selection
KOLAB: #3543
diff --git a/korganizer/akonadicollectionview.cpp b/korganizer/akonadicollectionview.cpp
index 3b0a808..c562255 100644
--- a/korganizer/akonadicollectionview.cpp
+++ b/korganizer/akonadicollectionview.cpp
@@ -581,6 +581,16 @@ AkonadiCollectionView::AkonadiCollectionView( CalendarView *view, bool hasContex
topLayout->addWidget( mStackedWidget );
+ KMessageWidget *msgWidget = new KMessageWidget(this);
+ msgWidget->setCloseButtonVisible(false);
+ msgWidget->setMessageType(KMessageWidget::Positive);
+ msgWidget->setObjectName(QLatin1String("msgwidget"));
+ msgWidget->setVisible(false);
+ msgWidget->setText(i18n("searching..."));
+ connect(mController, SIGNAL(searching(bool)),
+ msgWidget, SLOT(setVisible(bool)));
+ topLayout->addWidget(msgWidget);
+
connect( mBaseModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)) );
diff --git a/korganizer/views/collectionview/controller.cpp b/korganizer/views/collectionview/controller.cpp
index 03f8949..8f7808c 100644
--- a/korganizer/views/collectionview/controller.cpp
+++ b/korganizer/views/collectionview/controller.cpp
@@ -28,6 +28,7 @@
#include <Akonadi/CollectionFetchScope>
#include <Akonadi/AttributeFactory>
#include <KIcon>
+#include <KLocale>
#include <KCalCore/Event>
#include <KCalCore/Journal>
#include <KCalCore/Todo>
@@ -80,7 +81,7 @@ QVariant CollectionNode::data(int role) const
return mCheckState;
}
if (role == Qt::ToolTipRole) {
- return QString(QLatin1String("Collection: ") + mCollection.name() + QString::number(mCollection.id()));
+ return QString(i18n("Collection: ") + mCollection.name() + QLatin1String(" ") + QString::number(mCollection.id()));
}
if (role == IsSearchResultRole) {
return isSearchNode;
@@ -128,7 +129,7 @@ bool PersonNode::operator==(const Node &node) const
{
const PersonNode *personNode = dynamic_cast<const PersonNode*>(&node);
if (personNode) {
- return (personNode->mPerson.name == mPerson.name);
+ return (personNode->mPerson.uid == mPerson.uid);
}
return false;
}
@@ -145,7 +146,11 @@ void PersonNode::setChecked(bool enabled)
QVariant PersonNode::data(int role) const
{
if (role == Qt::DisplayRole) {
- return mPerson.name;
+ QString name = mPerson.name;
+ if (!mPerson.ou.isEmpty()) {
+ name += QLatin1String(" (") + mPerson.ou + QLatin1String(")");
+ }
+ return name;
}
if (role == Qt::DecorationRole) {
return KIcon(QLatin1String("meeting-participant"));
@@ -157,7 +162,14 @@ QVariant PersonNode::data(int role) const
return mCheckState;
}
if (role == Qt::ToolTipRole) {
- return QString(QLatin1String("Person: ") + mPerson.name);
+ QString tooltip = i18n("Person: ") + mPerson.name;
+ if (!mPerson.mail.isEmpty()) {
+ tooltip += QLatin1String("\n") + i18n("Mail: ") + mPerson.mail;
+ }
+ if (!mPerson.ou.isEmpty()) {
+ tooltip += QLatin1String("\n") + i18n("Organization Unit: ") + mPerson.ou;
+ }
+ return tooltip;
}
if (role == PersonRole) {
return QVariant::fromValue(mPerson);
@@ -214,6 +226,9 @@ void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
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)));
@@ -231,6 +246,9 @@ void PersonNodeManager::checkSourceIndexRemoval(const QModelIndex &sourceIndex)
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));
}
@@ -312,6 +330,7 @@ static Akonadi::Collection replaceParent(Akonadi::Collection col, const Akonadi:
Q_FOREACH (const Akonadi::Collection &c, ancestors) {
if (col == c) {
col = c;
+ break;
}
}
col.setParentCollection(parent);
@@ -343,6 +362,22 @@ PersonSearchJob::PersonSearchJob(const QString& searchString, QObject* parent)
: KJob(parent),
mSearchString(searchString)
{
+ connect(&mLdapSearch, SIGNAL(searchData(const QList<KLDAP::LdapResultObject> &)),
+ SLOT(onLDAPSearchData(const QList<KLDAP::LdapResultObject> &)));
+
+ connect(&mLdapSearch, SIGNAL(searchDone()),
+ SLOT(onLDAPSearchDone()));
+}
+
+PersonSearchJob::~PersonSearchJob()
+{
+ mLdapSearch.cancelSearch();
+}
+
+bool PersonSearchJob::kill(KJob::KillVerbosity verbosity)
+{
+ mLdapSearch.cancelSearch();
+ return KJob::kill(verbosity);
}
void PersonSearchJob::start()
@@ -358,9 +393,14 @@ void PersonSearchJob::start()
}
kDebug() << "Found persons " << collections.size();
+ mCollectionSearchDone = false;
+ mLdapSearchDone = false;
+
+ mLdapSearch.startSearch(QLatin1String("*") + mSearchString);
+
if (collections.isEmpty()) {
//We didn't find anything
- emitResult();
+ mCollectionSearchDone = true;
return;
}
@@ -369,34 +409,176 @@ void PersonSearchJob::start()
fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
connect(fetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(onCollectionsReceived(Akonadi::Collection::List)));
connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFetched(KJob*)));
- //TODO query ldap for available persons and their folders.
- //TODO identify imap folders as person folders and list them here (after indexing them in baloo).
- //
+
//The IMAP resource should add a "Person" attribute to the collections in the person namespace,
//the ldap query can then be used to update the name (entitydisplayattribute) for the person.
}
+void PersonSearchJob::onLDAPSearchData(const QList< KLDAP::LdapResultObject > &list)
+{
+ QList<Person> persons;
+ Q_FOREACH(const KLDAP::LdapResultObject &item, list) {
+ Person person;
+ person.name = QString::fromUtf8(item.object.value(QLatin1String("cn")));
+ person.mail = QString::fromUtf8(item.object.value(QLatin1String("mail")));
+
+ const int depth = item.object.dn().depth();
+ for ( int i = 0; i < depth; ++i ) {
+ const QString rdnStr = item.object.dn().rdnString(i);
+ if ( rdnStr.startsWith(QLatin1String("ou="), Qt::CaseInsensitive) ) {
+ person.ou = rdnStr.mid(3);
+ break;
+ }
+ }
+ const QStringList &parts = person.mail.split(QLatin1Char('@'));
+ if (parts.count() == 2) {
+ const QString &uid = parts.at(0);
+ person.uid = uid;
+ if (mMatches.contains(uid)) {
+ const Person &p = mMatches.value(uid);
+ if (p.mail != person.mail ) {
+ if (p.rootCollection > -1) {
+ person.rootCollection = p.rootCollection;
+ person.updateDisplayName = p.updateDisplayName;
+ updatePersonCollection(person);
+ mMatches.insert(uid, person);
+ } else {
+ kWarning() << "That should not happen: we found two times persons with the same uid ("<< uid << "), but differnet name:" << p.name << "vs" << person.name;
+ }
+ }
+ } else { //New person found
+ mMatches.insert(uid, person);
+ persons << person;
+ }
+ } else {
+ kWarning() << item.object.dn().toString() << ": invalid email address" << person.mail;
+ }
+ }
+ if (persons.count() > 0) {
+ emit personsFound(persons);
+ }
+}
+
+void PersonSearchJob::onLDAPSearchDone()
+{
+ mLdapSearchDone = true;
+ if (mCollectionSearchDone) {
+ emitResult();
+ }
+}
+
void PersonSearchJob::onCollectionsReceived(const Akonadi::Collection::List &list)
{
+ QList<Person> persons;
Q_FOREACH(const Akonadi::Collection &col, list) {
Person person;
- person.name = col.displayName();
+ const QString &uid = col.name();
+ const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
+ const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
person.rootCollection = col.id();
- mMatches << person;
+ person.uid = uid;
+ if (attr) {
+ person.ou = QString::fromUtf8(attr->ou());
+ person.mail = QString::fromUtf8(attr->mail());
+ person.name = QString::fromUtf8(attr->identifier());
+ if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
+ person.updateDisplayName = true;
+ }
+ } else {
+ person.name = col.displayName();
+ if (!displayname || displayname->displayName().isEmpty()) {
+ person.updateDisplayName = true;
+ }
+ }
+ if (mMatches.contains(uid)) {
+ Person p = mMatches.value(uid);
+ if (p.rootCollection > -1) {
+ //two collection with the same uid ?!
+ kWarning() << "Two collections match to same person" << p.rootCollection << person.rootCollection;
+ } else if (p.mail != person.mail) {
+ p.rootCollection = person.rootCollection;
+ p.updateDisplayName = person.updateDisplayName;
+ updatePersonCollection(p);
+ } else {
+ mMatches.insert(uid, person);
+ emit personUpdate(person);
+ }
+ } else {
+ mMatches.insert(uid, person);
+ persons << person;
+ }
+ }
+
+ if (persons.count() > 0) {
+ emit personsFound(persons);
}
}
+void PersonSearchJob::updatePersonCollection(const Person &person)
+{
+ Akonadi::Collection c(person.rootCollection);
+ CollectionIdentificationAttribute *identification = c.attribute<CollectionIdentificationAttribute>(Akonadi::Entity::AddIfMissing);
+
+ if (person.updateDisplayName) {
+ Akonadi::EntityDisplayAttribute *displayname = c.attribute<Akonadi::EntityDisplayAttribute >(Akonadi::Entity::AddIfMissing);
+ displayname->setDisplayName(person.name);
+ }
+
+ //identification->setIdentifier("Other Users/" + person.uid);
+ identification->setIdentifier(person.name.toUtf8());
+ identification->setName(person.name.toUtf8());
+ identification->setCollectionNamespace("usertoplevel");
+ identification->setMail(person.mail.toUtf8());
+ identification->setOu(person.ou.toUtf8());
+
+ Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( c, this );
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(modifyResult(KJob*)));
+}
+
void PersonSearchJob::onCollectionsFetched(KJob *job)
{
if (job->error()) {
kWarning() << job->errorString();
}
- emitResult();
+ mCollectionSearchDone = true;
+ if (mLdapSearchDone) {
+ emitResult();
+ }
}
QList<Person> PersonSearchJob::matches() const
{
- return mMatches;
+ return mMatches.values();
+}
+
+void PersonSearchJob::modifyResult(KJob *job)
+{
+ if (job->error()) {
+ kWarning() << job->errorString();
+ return;
+ }
+
+ const Akonadi::CollectionModifyJob *modifyJob = static_cast<Akonadi::CollectionModifyJob*>(job);
+ const Akonadi::Collection &col = modifyJob->collection();
+
+ const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
+ const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
+ const QString &uid = col.name();
+ Person &person = mMatches[col.name()];
+ person.rootCollection = col.id();
+ person.uid = uid;
+ if (attr) {
+ person.ou = QString::fromUtf8(attr->ou());
+ person.mail = QString::fromUtf8(attr->mail());
+ person.name = QString::fromUtf8(attr->identifier());
+ if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
+ person.updateDisplayName = true;
+ }
+ }
+ kDebug() << "modified person to" << person.uid << person.name << person.rootCollection;
+
+ mMatches.insert(person.uid, person);
+ emit personUpdate(person);
}
@@ -426,10 +608,15 @@ void Controller::setSearchString(const QString &searchString)
mSearchModel->clear();
emit searchIsActive(!searchString.isEmpty());
if (searchString.size() < 2) {
+ emit searching(false);
return;
}
+ emit searching(true);
+
mPersonSearchJob = new PersonSearchJob(searchString, this);
+ connect(mPersonSearchJob, SIGNAL(personsFound(QList<Person>)), this, SLOT(onPersonsFound(QList<Person>)));
+ connect(mPersonSearchJob, SIGNAL(personUpdate(Person)), this, SLOT(onPersonUpdate(Person)));
connect(mPersonSearchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonsFound(KJob*)));
mPersonSearchJob->start();
@@ -440,6 +627,9 @@ void Controller::setSearchString(const QString &searchString)
void Controller::onCollectionsFound(KJob* job)
{
+ if (!mPersonSearchJob) {
+ emit searching(false);
+ }
if (job->error()) {
kWarning() << job->errorString();
mCollectionSearchJob = 0;
@@ -456,21 +646,34 @@ void Controller::onCollectionsFound(KJob* job)
mCollectionSearchJob = 0;
}
-void Controller::onPersonsFound(KJob* job)
+void Controller::onPersonsFound(const QList<Person> &persons)
{
- if (job->error()) {
- kWarning() << job->errorString();
- mPersonSearchJob = 0;
- return;
- }
- Q_ASSERT(mPersonSearchJob == static_cast<PersonSearchJob*>(job));
- Q_FOREACH(const Person &p, mPersonSearchJob->matches()) {
+ Q_FOREACH(const Person &p, persons) {
PersonNode *personNode = new PersonNode(*mSearchModel, p);
personNode->isSearchNode = true;
//toggled by the checkbox, results in person getting added to main model
// connect(&personNode->emitter, SIGNAL(enabled(bool, Person)), this, SLOT(onPersonEnabled(bool, Person)));
mSearchModel->addNode(ReparentingModel::Node::Ptr(personNode));
}
+}
+
+void Controller::onPersonUpdate(const Person &person)
+{
+ PersonNode *personNode = new PersonNode(*mSearchModel, person);
+ personNode->isSearchNode = true;
+ mSearchModel->updateNode(ReparentingModel::Node::Ptr(personNode));
+}
+
+void Controller::onPersonsFound(KJob* job)
+{
+ if (!mCollectionSearchJob) {
+ emit searching(false);
+ }
+ if (job->error()) {
+ kWarning() << job->errorString();
+ mPersonSearchJob = 0;
+ return;
+ }
mPersonSearchJob = 0;
}
@@ -536,19 +739,44 @@ void Controller::onPersonCollectionsFetched(KJob* job)
void Controller::addPerson(const Person &person)
{
- kDebug() << person.name;
+ kDebug() << person.uid << person.name << person.rootCollection;
+
+ if (person.rootCollection == -1) {
+ Baloo::PIM::CollectionQuery query;
+ query.setNamespace(QStringList() << QLatin1String("usertoplevel"));
+ query.pathMatches(QLatin1String("/Other Users/")+person.uid);
+ query.setLimit(200);
+ Baloo::PIM::ResultIterator it = query.exec();
+ Akonadi::Collection::List collections;
+ while (it.next()) {
+ collections << Akonadi::Collection(it.id());
+ }
+ kDebug() << "Found collections " << collections.size() << "for" << person.name;
+ //TODO: use the found collection and update attribute
+ }
+
PersonNode *personNode = new PersonNode(*mPersonModel, person);
personNode->setChecked(true);
mPersonModel->addNode(ReparentingModel::Node::Ptr(personNode));
- setCollectionState(Akonadi::Collection(person.rootCollection), Referenced, true);
+ if (person.rootCollection > -1) {
+ setCollectionState(Akonadi::Collection(person.rootCollection), Referenced, true);
+ } else {
+ kDebug() << "well this only a ldap search object without a collection";
+ //TODO: use freebusy data into calendar
+ }
}
void Controller::removePerson(const Person &person)
{
- kDebug() << person.name;
+ kDebug() << person.uid << person.name << person.rootCollection;
mPersonModel->removeNode(PersonNode(*mPersonModel, person));
- setCollectionState(Akonadi::Collection(person.rootCollection), Disabled, true);
+ if (person.rootCollection > -1) {
+ setCollectionState(Akonadi::Collection(person.rootCollection), Disabled, true);
+ } else {
+ kDebug() << "well this only a ldap search object without a collection";
+ //TODO: delete freebusy data from calendar
+ }
}
diff --git a/korganizer/views/collectionview/controller.h b/korganizer/views/collectionview/controller.h
index a90a851..916078e 100644
--- a/korganizer/views/collectionview/controller.h
+++ b/korganizer/views/collectionview/controller.h
@@ -29,6 +29,8 @@
#include <Akonadi/Collection>
#include "reparentingmodel.h"
+#include <libkdepim/ldap/ldapclientsearch.h>
+
enum DataRoles {
PersonRole = Akonadi::EntityTreeModel::UserRole + 1,
IsSearchResultRole,
@@ -45,8 +47,12 @@ enum NodeTypeRoles {
struct Person
{
- Person(): rootCollection(-1){};
+ Person(): rootCollection(-1), updateDisplayName(false) {};
QString name;
+ QString uid;
+ QString ou;
+ QString mail;
+ bool updateDisplayName;
Akonadi::Collection::Id rootCollection;
//FIXME not sure we actually require those two
@@ -156,18 +162,33 @@ class PersonSearchJob : public KJob
Q_OBJECT
public:
explicit PersonSearchJob(const QString &searchString, QObject* parent = 0);
+ virtual ~PersonSearchJob();
virtual void start();
QList<Person> matches() const;
+Q_SIGNALS:
+ void personsFound(const QList<Person> &persons);
+ void personUpdate(const Person &person);
+
+public Q_SLOTS:
+ bool kill(KillVerbosity verbosity=Quietly);
+
private Q_SLOTS:
void onCollectionsReceived(const Akonadi::Collection::List &);
void onCollectionsFetched(KJob *);
+ void onLDAPSearchData(const QList<KLDAP::LdapResultObject> &);
+ void onLDAPSearchDone();
+ void updatePersonCollection(const Person &person);
+ void modifyResult(KJob *job);
private:
QString mSearchString;
- QList<Person> mMatches;
+ QHash<QString, Person> mMatches;
+ KLDAP::LdapClientSearch mLdapSearch;
+ bool mCollectionSearchDone;
+ bool mLdapSearchDone;
};
/**
@@ -195,12 +216,15 @@ public:
Q_SIGNALS:
void searchIsActive(bool);
+ void searching(bool);
public Q_SLOTS:
void setSearchString(const QString &);
private Q_SLOTS:
void onCollectionsFound(KJob *job);
+ void onPersonsFound(const QList<Person> &persons);
+ void onPersonUpdate(const Person &person);
void onPersonsFound(KJob *job);
void onPersonCollectionsFetched(KJob *job);
diff --git a/korganizer/views/collectionview/reparentingmodel.cpp b/korganizer/views/collectionview/reparentingmodel.cpp
index 6e73df0..824f31c 100644
--- a/korganizer/views/collectionview/reparentingmodel.cpp
+++ b/korganizer/views/collectionview/reparentingmodel.cpp
@@ -274,6 +274,23 @@ void ReparentingModel::doAddNode(const Node::Ptr &node)
}
}
+void ReparentingModel::updateNode(const ReparentingModel::Node::Ptr &node)
+{
+ Q_FOREACH(const ReparentingModel::Node::Ptr &existing, mProxyNodes) {
+ if (*existing == *node) {
+ node->parent = existing->parent;
+ int r = row(existing.data());
+ existing->parent->children.replace(r, node);
+ const QModelIndex i = index(node.data());
+ Q_ASSERT(i.row() == r);
+ emit dataChanged(i, i);
+ return;
+ }
+ }
+
+ kWarning() << "no node to update";
+}
+
void ReparentingModel::removeNode(const ReparentingModel::Node& node)
{
//If there is an addNode in progress for that node, abort it.
@@ -761,21 +778,30 @@ Qt::ItemFlags ReparentingModel::flags(const QModelIndex& index) const
return sourceModel()->flags(mapToSource(index));
}
-QModelIndex ReparentingModel::index(Node *node) const
+int ReparentingModel::row(ReparentingModel::Node *node) const
{
Q_ASSERT(node);
if (node == &mRootNode) {
- return QModelIndex();
+ return -1;
}
Q_ASSERT(validateNode(node));
int row = 0;
Q_FOREACH(const Node::Ptr &c, node->parent->children) {
if (c.data() == node) {
- break;
+ return row;
}
row++;
}
- return createIndex(row, 0, node);
+ return -1;
+}
+
+QModelIndex ReparentingModel::index(Node *node) const
+{
+ const int r = row(node);
+ if (r < 0) {
+ return QModelIndex();
+ }
+ return createIndex(r, 0, node);
}
QModelIndex ReparentingModel::parent(const QModelIndex& child) const
diff --git a/korganizer/views/collectionview/reparentingmodel.h b/korganizer/views/collectionview/reparentingmodel.h
index ad73ba8..4f88c84 100644
--- a/korganizer/views/collectionview/reparentingmodel.h
+++ b/korganizer/views/collectionview/reparentingmodel.h
@@ -87,6 +87,7 @@ public:
void setNodeManager(const NodeManager::Ptr &nodeManager);
void addNode(const Node::Ptr &node);
+ void updateNode(const Node::Ptr &node);
void removeNode(const Node &node);
void setNodes(const QList<Node::Ptr> &nodes);
void clear();
@@ -126,6 +127,7 @@ private:
void reparentSourceNodes(const Node::Ptr &proxyNode);
void rebuildAll();
QModelIndex index(Node *node) const;
+ int row(Node *node) const;
Node *getReparentNode(const QModelIndex &sourceIndex);
Node *getParentNode(const QModelIndex &sourceIndex);
bool validateNode(const Node *node) const;
diff --git a/korganizer/views/collectionview/tests/reparentingmodeltest.cpp b/korganizer/views/collectionview/tests/reparentingmodeltest.cpp
index 4fe7dbb..f7ddd70 100644
--- a/korganizer/views/collectionview/tests/reparentingmodeltest.cpp
+++ b/korganizer/views/collectionview/tests/reparentingmodeltest.cpp
@@ -30,9 +30,10 @@
class DummyNode : public ReparentingModel::Node
{
public:
- DummyNode(ReparentingModel &personModel, const QString &name)
+ DummyNode(ReparentingModel &personModel, const QString &name, const QString &data=QString())
: ReparentingModel::Node(personModel),
- mName(name)
+ mName(name),
+ mData(data)
{}
virtual ~DummyNode(){};
@@ -49,10 +50,14 @@ private:
virtual QVariant data(int role) const {
if (role == Qt::DisplayRole) {
return mName;
+ } else if (role == Qt::UserRole) {
+ return mData;
}
return QVariant();
}
virtual bool setData(const QVariant& variant, int role){
+ Q_UNUSED(variant);
+ Q_UNUSED(role);
return false;
}
virtual bool isDuplicateOf(const QModelIndex& sourceIndex) {
@@ -64,6 +69,7 @@ private:
}
QString mName;
+ QString mData;
};
class ModelSignalSpy : public QObject {
@@ -80,6 +86,7 @@ public:
QStringList mSignals;
QModelIndex parent;
+ QModelIndex topLeft, bottomRight;
int start;
int end;
@@ -99,8 +106,10 @@ public Q_SLOTS:
void onRowsMoved(QModelIndex,int,int,QModelIndex,int) {
mSignals << QLatin1String("rowsMoved");
}
- void onDataChanged(QModelIndex,QModelIndex) {
+ void onDataChanged(QModelIndex t,QModelIndex b) {
mSignals << QLatin1String("dataChanged");
+ topLeft = t;
+ bottomRight = b;
}
void onLayoutChanged() {
mSignals << QLatin1String("layoutChanged");
@@ -137,6 +146,7 @@ private Q_SLOTS:
void testDeduplicateNested();
void testDeduplicateProxyNodeFirst();
void testNestedDeduplicateProxyNodeFirst();
+ void testUpdateNode();
void testReparent();
void testReparentResetWithoutCrash();
void testAddReparentedSourceItem();
@@ -352,6 +362,40 @@ void ReparentingModelTest::testNestedDeduplicateProxyNodeFirst()
//TODO ensure we actually have the source index and not the proxy index
}
+/**
+ * updateNode should update the node datas
+ */
+void ReparentingModelTest::testUpdateNode()
+{
+ QStandardItemModel sourceModel;
+ ReparentingModel reparentingModel;
+ reparentingModel.setSourceModel(&sourceModel);
+ reparentingModel.addNode(ReparentingModel::Node::Ptr(new DummyNode(reparentingModel, QLatin1String("proxy1"), QLatin1String("blub"))));
+
+ QTest::qWait(0);
+
+ QModelIndex index = getIndex("proxy1", reparentingModel);
+ QCOMPARE(reparentingModel.rowCount(QModelIndex()), 1);
+ QVERIFY(index.isValid());
+ QCOMPARE(reparentingModel.data(index,Qt::UserRole).toString(), QLatin1String("blub"));
+
+ ModelSignalSpy spy(reparentingModel);
+ reparentingModel.updateNode(ReparentingModel::Node::Ptr(new DummyNode(reparentingModel, QLatin1String("proxy1"), QLatin1String("new data"))));
+ QTest::qWait(0);
+
+ QModelIndex i2 = getIndex("proxy1", reparentingModel);
+ QCOMPARE(i2.column(), index.column());
+ QCOMPARE(i2.row(), index.row());
+
+ QCOMPARE(spy.mSignals.count(), 1);
+ QCOMPARE(spy.mSignals.takeLast(),QLatin1String("dataChanged"));
+ QCOMPARE(spy.topLeft, i2);
+ QCOMPARE(spy.bottomRight, i2);
+
+ QCOMPARE(reparentingModel.rowCount(QModelIndex()), 1);
+ QCOMPARE(reparentingModel.data(i2,Qt::UserRole).toString(), QLatin1String("new data"));
+}
+
void ReparentingModelTest::testReparent()
{
QStandardItemModel sourceModel;
More information about the commits
mailing list