diff --git a/akonadi/src/core/models/tagmodel.cpp b/akonadi/src/core/models/tagmodel.cpp index d5e4df344..ebc41d5f7 100644 --- a/akonadi/src/core/models/tagmodel.cpp +++ b/akonadi/src/core/models/tagmodel.cpp @@ -1,178 +1,178 @@ /* Copyright (c) 2014 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tagmodel.h" #include "tagmodel_p.h" #include "tagattribute.h" #include #include using namespace Akonadi; TagModel::TagModel(Monitor *recorder, QObject *parent) : QAbstractItemModel(parent) , d_ptr(new TagModelPrivate(this)) { Q_D(TagModel); d->init(recorder); } TagModel::TagModel(Monitor *recorder, TagModelPrivate *dd, QObject *parent) : QAbstractItemModel(parent) , d_ptr(dd) { Q_D(TagModel); d->init(recorder); } TagModel::~TagModel() { delete d_ptr; } int TagModel::columnCount(const QModelIndex &parent) const { if (parent.isValid() && parent.column() != 0) { return 0; } return 1; } int TagModel::rowCount(const QModelIndex &parent) const { Q_D(const TagModel); - Tag::Id parentTagId = 0; + Tag::Id parentTagId = -1; if (parent.isValid()) { parentTagId = d->mChildTags[parent.internalId()].at(parent.row()).id(); } return d->mChildTags[parentTagId].count(); } QVariant TagModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical) { return QVariant(); } if (role == Qt::DisplayRole) { switch (section) { case 0: return i18n("Tag"); } } return QAbstractItemModel::headerData(section, orientation, role); } QVariant TagModel::data(const QModelIndex &index, int role) const { Q_D(const TagModel); const Tag tag = d->tagForIndex(index); if (!tag.isValid()) { return QVariant(); } switch (role) { case Qt::DisplayRole: // fall-through case NameRole: return tag.name(); case IdRole: return tag.id(); case GIDRole: return tag.gid(); case ParentRole: return QVariant::fromValue(tag.parent()); case TagRole: return QVariant::fromValue(tag); case Qt::DecorationRole: { TagAttribute *attr = tag.attribute(); if (attr) { return QIcon::fromTheme(attr->iconName()); } else { return QVariant(); } } } return QVariant(); } QModelIndex TagModel::index(int row, int column, const QModelIndex &parent) const { Q_D(const TagModel); - qint64 parentId = 0; + qint64 parentId = -1; if (parent.isValid()) { const Tag parentTag = d->tagForIndex(parent); parentId = parentTag.id(); } const Tag::List &children = d->mChildTags.value(parentId); if (row >= children.count()) { return QModelIndex(); } return createIndex(row, column, (int) parentId); } QModelIndex TagModel::parent(const QModelIndex &child) const { Q_D(const TagModel); if (!child.isValid()) { return QModelIndex(); } const qint64 parentId = child.internalId(); return d->indexForTag(parentId); } Qt::ItemFlags TagModel::flags(const QModelIndex &index) const { Q_UNUSED(index); return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; } bool TagModel::insertColumns(int, int, const QModelIndex &) { return false; } bool TagModel::insertRows(int, int, const QModelIndex &) { return false; } bool TagModel::removeColumns(int, int, const QModelIndex &) { return false; } bool TagModel::removeRows(int, int, const QModelIndex &) { return false; } #include "moc_tagmodel.cpp" diff --git a/akonadi/src/core/models/tagmodel_p.cpp b/akonadi/src/core/models/tagmodel_p.cpp index b4266f6ea..2379b8230 100644 --- a/akonadi/src/core/models/tagmodel_p.cpp +++ b/akonadi/src/core/models/tagmodel_p.cpp @@ -1,243 +1,243 @@ /* Copyright (c) 2014 Daniel Vr??til This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tagmodel_p.h" #include "tagmodel.h" #include "monitor.h" #include "session.h" #include "tagfetchjob.h" #include #include using namespace Akonadi; TagModelPrivate::TagModelPrivate(TagModel *parent) : mMonitor(0) , mSession(0) , q_ptr(parent) { // Root tag - mTags.insert(0, Tag(0)); + mTags.insert(-1, Tag()); } TagModelPrivate::~TagModelPrivate() { } void TagModelPrivate::init(Monitor *monitor) { Q_Q(TagModel); mMonitor = monitor; mSession = mMonitor->session(); q->connect(mMonitor, SIGNAL(tagAdded(Akonadi::Tag)), q, SLOT(monitoredTagAdded(Akonadi::Tag))); q->connect(mMonitor, SIGNAL(tagChanged(Akonadi::Tag)), q, SLOT(monitoredTagChanged(Akonadi::Tag))); q->connect(mMonitor, SIGNAL(tagRemoved(Akonadi::Tag)), q, SLOT(monitoredTagRemoved(Akonadi::Tag))); // Delay starting the job to allow unit-tests to set up fake stuff QTimer::singleShot(0, q, SLOT(fillModel())); } void TagModelPrivate::fillModel() { Q_Q(TagModel); TagFetchJob *fetchJob = new TagFetchJob(mSession); fetchJob->setFetchScope(mMonitor->tagFetchScope()); q->connect(fetchJob, SIGNAL(tagsReceived(Akonadi::Tag::List)), q, SLOT(tagsFetched(Akonadi::Tag::List))); q->connect(fetchJob, SIGNAL(finished(KJob*)), q, SLOT(tagsFetchDone(KJob*))); } QModelIndex TagModelPrivate::indexForTag(const qint64 tagId) const { Q_Q(const TagModel); if (!mTags.contains(tagId)) { return QModelIndex(); } const Tag tag = mTags.value(tagId); if (!tag.isValid()) { return QModelIndex(); } const Tag::Id parentId = tag.parent().id(); const int row = mChildTags.value(parentId).indexOf(tag); if (row != -1) { return q->createIndex(row, 0, (int) parentId); } return QModelIndex(); } Tag TagModelPrivate::tagForIndex(const QModelIndex &index) const { if (!index.isValid()) { return Tag(); } const Tag::Id parentId = index.internalId(); const Tag::List &children = mChildTags.value(parentId); return children.at(index.row()); } void TagModelPrivate::monitoredTagAdded(const Tag &tag) { Q_Q(TagModel); const qint64 parentId = tag.parent().id(); // Parent not yet in model, defer for later if (!mTags.contains(parentId)) { Tag::List &list = mPendingTags[parentId]; list.append(tag); return; } Tag::List &children = mChildTags[parentId]; q->beginInsertRows(indexForTag(parentId), children.count(), children.count()); mTags.insert(tag.id(), tag); children.append(tag); q->endInsertRows(); // If there are any child tags waiting for this parent, insert them if (mPendingTags.contains(tag.id())) { const Tag::List pendingChildren = mPendingTags.take(tag.id()); Tag::List &children = mChildTags[tag.id()]; q->beginInsertRows(indexForTag(tag.id()), 0, pendingChildren.count() - 1); Q_FOREACH (const Tag &child, pendingChildren) { mTags.insert(child.id(), child); children.append(child); } q->endInsertRows(); } } void TagModelPrivate::removeTagsRecursively(qint64 tagId) { const Tag tag = mTags.value(tagId); // Remove all children first const Tag::List childTags = mChildTags.take(tagId); Q_FOREACH (const Tag &child, childTags) { removeTagsRecursively(child.id()); } // Remove the actual tag Tag::List &siblings = mChildTags[tag.parent().id()]; siblings.removeOne(tag); mTags.remove(tag.id()); } void TagModelPrivate::monitoredTagRemoved(const Tag &tag) { Q_Q(TagModel); // Better lookup parent in our cache qint64 parentId = mTags.value(tag.id()).parent().id(); if (parentId == -1) { qWarning() << "Got removal notification for unknown tag" << tag.id(); return; } Tag::List &siblings = mChildTags[parentId]; const int pos = siblings.indexOf(tag); Q_ASSERT(pos != -1); q->beginRemoveRows(indexForTag(parentId), pos, pos); removeTagsRecursively(tag.id()); q->endRemoveRows(); } void TagModelPrivate::monitoredTagChanged(const Tag &tag) { Q_Q(TagModel); if (!mTags.contains(tag.id())) { qWarning() << "Got change notifications for unknown tag" << tag.id(); return; } const Tag oldTag = mTags.value(tag.id()); // Replace existing tag in cache mTags.insert(tag.id(), tag); // Check whether the tag has been reparented const qint64 oldParent = oldTag.parent().id(); const qint64 newParent = tag.parent().id(); if (oldParent != newParent) { const QModelIndex sourceParent = indexForTag(oldParent); const int sourcePos = mChildTags.value(oldParent).indexOf(oldTag); const QModelIndex destParent = indexForTag(newParent); const int destPos = mChildTags.value(newParent).count(); q->beginMoveRows(sourceParent, sourcePos, sourcePos, destParent, destPos); Tag::List &oldSiblings = mChildTags[oldParent]; oldSiblings.removeAt(sourcePos); Tag::List &newSiblings = mChildTags[newParent]; newSiblings.append(tag); q->endMoveRows(); } else { Tag::List &children = mChildTags[oldParent]; const int sourcePos = children.indexOf(oldTag); if (sourcePos != -1) { children[sourcePos] = tag; } const QModelIndex index = indexForTag(tag.id()); q->dataChanged(index, index); } } void TagModelPrivate::tagsFetched(const Tag::List &tags) { Q_FOREACH (const Tag &tag, tags) { monitoredTagAdded(tag); } } void TagModelPrivate::tagsFetchDone(KJob *job) { Q_Q(TagModel); if (job->error()) { qWarning() << job->errorString(); return; } if (!mPendingTags.isEmpty()) { qWarning() << "Fetched all tags from server, but there are still" << mPendingTags.count() << "orphan tags:"; for (auto it = mPendingTags.cbegin(), e = mPendingTags.cend(); it != e; ++it) { qWarning() << "tagId = " << it.key() << "; with list count =" << it.value().count(); } return; } emit q->populated(); }