diff --git a/autotests/libs/testresource/knutresource.cpp b/autotests/libs/testresource/knutresource.cpp index 9efee0f3e..3312c0b9d 100644 --- a/autotests/libs/testresource/knutresource.cpp +++ b/autotests/libs/testresource/knutresource.cpp @@ -1,388 +1,389 @@ /* Copyright (c) 2006 Tobias Koenig Copyright (c) 2009 Volker Krause 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 "knutresource.h" #include "knutresource_debug.h" #include "settings.h" #include "settingsadaptor.h" #include "xmlwriter.h" #include "xmlreader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; KnutResource::KnutResource(const QString &id) : ResourceBase(id) , mWatcher(new QFileSystemWatcher(this)) , mSettings(new KnutSettings()) { changeRecorder()->itemFetchScope().fetchFullPayload(); changeRecorder()->fetchCollection(true); new SettingsAdaptor(mSettings); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/Settings"), mSettings, QDBusConnection::ExportAdaptors); connect(this, &KnutResource::reloadConfiguration, this, &KnutResource::load); connect(mWatcher, &QFileSystemWatcher::fileChanged, this, &KnutResource::load); load(); } KnutResource::~KnutResource() { delete mSettings; } void KnutResource::load() { if (!mWatcher->files().isEmpty()) { mWatcher->removePaths(mWatcher->files()); } // file loading QString fileName = mSettings->dataFile(); if (fileName.isEmpty()) { emit status(Broken, i18n("No data file selected.")); return; } if (!QFile::exists(fileName)) { fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/akonadi_knut_resource/knut-template.xml")); } if (!mDocument.loadFile(fileName)) { emit status(Broken, mDocument.lastError()); return; } if (mSettings->fileWatchingEnabled()) { mWatcher->addPath(fileName); } emit status(Idle, i18n("File '%1' loaded successfully.", fileName)); synchronize(); } void KnutResource::save() { if (mSettings->readOnly()) { return; } const QString fileName = mSettings->dataFile(); if (!mDocument.writeToFile(fileName)) { emit error(mDocument.lastError()); return; } } void KnutResource::configure(WId windowId) { QString oldFile = mSettings->dataFile(); if (oldFile.isEmpty()) { oldFile = QDir::homePath(); } // TODO: Use windowId Q_UNUSED(windowId); const QString newFile = QFileDialog::getSaveFileName( nullptr, i18n("Select Data File"), QString(), QStringLiteral("*.xml |") + i18nc("Filedialog filter for Akonadi data file", "Akonadi Knut Data File")); if (newFile.isEmpty() || oldFile == newFile) { return; } mSettings->setDataFile(newFile); mSettings->save(); load(); emit configurationDialogAccepted(); } void KnutResource::retrieveCollections() { const Collection::List collections = mDocument.collections(); collectionsRetrieved(collections); const Tag::List tags = mDocument.tags(); Q_FOREACH (const Tag &tag, tags) { TagCreateJob *createjob = new TagCreateJob(tag); createjob->setMergeIfExisting(true); } } void KnutResource::retrieveItems(const Akonadi::Collection &collection) { Item::List items = mDocument.items(collection, false); if (!mDocument.lastError().isEmpty()) { cancelTask(mDocument.lastError()); return; } itemsRetrieved(items); } #ifdef DO_IT_THE_OLD_WAY bool KnutResource::retrieveItem(const Item &item, const QSet &parts) { Q_UNUSED(parts); const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); if (itemElem.isNull()) { cancelTask(i18n("No item found for remoteid %1", item.remoteId())); return false; } Item i = XmlReader::elementToItem(itemElem, true); i.setId(item.id()); itemRetrieved(i); return true; } #endif bool KnutResource::retrieveItems(const Item::List &items, const QSet &parts) { Q_UNUSED(parts); Item::List results; results.reserve(items.size()); for (const auto &item : items) { const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); if (itemElem.isNull()) { cancelTask(i18n("No item found for remoteid %1", item.remoteId())); return false; } Item i = XmlReader::elementToItem(itemElem, true); i.setParentCollection(item.parentCollection()); i.setId(item.id()); results.push_back(i); } itemsRetrieved(results); return true; } void KnutResource::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { QDomElement parentElem = mDocument.collectionElementByRemoteId(parent.remoteId()); if (parentElem.isNull()) { emit error(i18n("Parent collection not found in DOM tree.")); changeProcessed(); return; } Collection c(collection); c.setRemoteId(QUuid::createUuid().toString()); if (XmlWriter::writeCollection(c, parentElem).isNull()) { emit error(i18n("Unable to write collection.")); changeProcessed(); } else { save(); changeCommitted(c); } } void KnutResource::collectionChanged(const Akonadi::Collection &collection) { QDomElement oldElem = mDocument.collectionElementByRemoteId(collection.remoteId()); if (oldElem.isNull()) { emit error(i18n("Modified collection not found in DOM tree.")); changeProcessed(); return; } Collection c(collection); QDomElement newElem; newElem = XmlWriter::collectionToElement(c, mDocument.document()); // move all items/collections over to the new node const QDomNodeList children = oldElem.childNodes(); const int numberOfChildren = children.count(); for (int i = 0; i < numberOfChildren; ++i) { const QDomElement child = children.at(i).toElement(); qCDebug(KNUTRESOURCE_LOG) << "reparenting " << child.tagName() << child.attribute(QStringLiteral("rid")); if (child.isNull()) { continue; } if (child.tagName() == QStringLiteral("item") || child.tagName() == QStringLiteral("collection")) { newElem.appendChild(child); // reparents --i; // children, despite being const is modified by the reparenting } } oldElem.parentNode().replaceChild(newElem, oldElem); save(); changeCommitted(c); } void KnutResource::collectionRemoved(const Akonadi::Collection &collection) { const QDomElement colElem = mDocument.collectionElementByRemoteId(collection.remoteId()); if (colElem.isNull()) { emit error(i18n("Deleted collection not found in DOM tree.")); changeProcessed(); return; } colElem.parentNode().removeChild(colElem); save(); changeProcessed(); } void KnutResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { QDomElement parentElem = mDocument.collectionElementByRemoteId(collection.remoteId()); if (parentElem.isNull()) { emit error(i18n("Parent collection '%1' not found in DOM tree." , collection.remoteId())); changeProcessed(); return; } Item i(item); i.setRemoteId(QUuid::createUuid().toString()); if (XmlWriter::writeItem(i, parentElem).isNull()) { emit error(i18n("Unable to write item.")); changeProcessed(); } else { save(); changeCommitted(i); } } void KnutResource::itemChanged(const Akonadi::Item &item, const QSet &parts) { Q_UNUSED(parts); const QDomElement oldElem = mDocument.itemElementByRemoteId(item.remoteId()); if (oldElem.isNull()) { emit error(i18n("Modified item not found in DOM tree.")); changeProcessed(); return; } Item i(item); const QDomElement newElem = XmlWriter::itemToElement(i, mDocument.document()); oldElem.parentNode().replaceChild(newElem, oldElem); save(); changeCommitted(i); } void KnutResource::itemRemoved(const Akonadi::Item &item) { const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); if (itemElem.isNull()) { emit error(i18n("Deleted item not found in DOM tree.")); changeProcessed(); return; } itemElem.parentNode().removeChild(itemElem); save(); changeProcessed(); } void KnutResource::itemMoved(const Item &item, const Collection &collectionSource, const Collection &collectionDestination) { const QDomElement oldElem = mDocument.itemElementByRemoteId(item.remoteId()); if (oldElem.isNull()) { qCWarning(KNUTRESOURCE_LOG) << "Moved item not found in DOM tree"; changeProcessed(); return; } QDomElement sourceParentElem = mDocument.collectionElementByRemoteId(collectionSource.remoteId()); if (sourceParentElem.isNull()) { emit error(i18n("Parent collection '%1' not found in DOM tree.", collectionSource.remoteId())); changeProcessed(); return; } QDomElement destParentElem = mDocument.collectionElementByRemoteId(collectionDestination.remoteId()); if (destParentElem.isNull()) { emit error(i18n("Parent collection '%1' not found in DOM tree.", collectionDestination.remoteId())); changeProcessed(); return; } QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); if (itemElem.isNull()) { emit error(i18n("No item found for remoteid %1", item.remoteId())); } sourceParentElem.removeChild(itemElem); destParentElem.appendChild(itemElem); if (XmlWriter::writeItem(item, destParentElem).isNull()) { emit error(i18n("Unable to write item.")); } else { save(); } changeProcessed(); } QSet KnutResource::parseQuery(const QString &queryString) { QSet resultSet; Akonadi::SearchQuery query = Akonadi::SearchQuery::fromJSON(queryString.toLatin1()); foreach (const Akonadi::SearchTerm &term, query.term().subTerms()) { if (term.key() == QStringLiteral("resource")) { resultSet << term.value().toInt(); } } return resultSet; } void KnutResource::search(const QString &query, const Collection &collection) { Q_UNUSED(collection); const QVector result = parseQuery(query).toList().toVector(); qCDebug(KNUTRESOURCE_LOG) << "KNUT QUERY:" << query; qCDebug(KNUTRESOURCE_LOG) << "KNUT RESOURCE:" << result; searchFinished(result, Akonadi::AgentSearchInterface::Uid); } void KnutResource::addSearch(const QString &query, const QString &queryLanguage, const Collection &resultCollection) { Q_UNUSED(query); Q_UNUSED(queryLanguage); Q_UNUSED(resultCollection); - qCDebug(KNUTRESOURCE_LOG); + qCDebug(KNUTRESOURCE_LOG) << "addSearch: query=" << query << ", queryLanguage=" + << queryLanguage << ", resultCollection=" << resultCollection.id(); } void KnutResource::removeSearch(const Collection &resultCollection) { Q_UNUSED(resultCollection); - qCDebug(KNUTRESOURCE_LOG); + qCDebug(KNUTRESOURCE_LOG) << "removeSearch:" << resultCollection.id(); } AKONADI_RESOURCE_MAIN(KnutResource) diff --git a/src/core/firstrun.cpp b/src/core/firstrun.cpp index aa2e353ef..fd94d7e8a 100644 --- a/src/core/firstrun.cpp +++ b/src/core/firstrun.cpp @@ -1,229 +1,228 @@ /* Copyright (c) 2008 Volker Krause 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 "firstrun_p.h" #include "KDBusConnectionPool" #include "servermanager.h" #include "agentinstance.h" #include "agentinstancecreatejob.h" #include "agentmanager.h" #include "agenttype.h" #include #include "akonadicore_debug.h" #include #include #include #include #include #include #include #include #include static const char FIRSTRUN_DBUSLOCK[] = "org.kde.Akonadi.Firstrun.lock"; using namespace Akonadi; Firstrun::Firstrun(QObject *parent) : QObject(parent) , mConfig(new KConfig(ServerManager::addNamespace(QStringLiteral("akonadi-firstrunrc")))) , mCurrentDefault(nullptr) { //The code in firstrun is not safe in multi-instance mode Q_ASSERT(!ServerManager::hasInstanceIdentifier()); if (ServerManager::hasInstanceIdentifier()) { deleteLater(); return; } - qCDebug(AKONADICORE_LOG); if (KDBusConnectionPool::threadConnection().registerService(QLatin1String(FIRSTRUN_DBUSLOCK))) { findPendingDefaults(); qCDebug(AKONADICORE_LOG) << mPendingDefaults; setupNext(); } else { qCDebug(AKONADICORE_LOG) << "D-Bus lock found, so someone else does the work for us already."; deleteLater(); } } Firstrun::~Firstrun() { if (qApp) { KDBusConnectionPool::threadConnection().unregisterService(QLatin1String(FIRSTRUN_DBUSLOCK)); } delete mConfig; qCDebug(AKONADICORE_LOG) << "done"; } void Firstrun::findPendingDefaults() { const KConfigGroup cfg(mConfig, "ProcessedDefaults"); const auto paths = StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/firstrun")); for (const QString &dirName : paths) { const QStringList files = QDir(dirName).entryList(QDir::Files | QDir::Readable); for (const QString &fileName : files) { const QString fullName = dirName + QLatin1Char('/') + fileName; KConfig c(fullName); const QString id = KConfigGroup(&c, "Agent").readEntry("Id", QString()); if (id.isEmpty()) { qCWarning(AKONADICORE_LOG) << "Found invalid default configuration in " << fullName; continue; } if (cfg.hasKey(id)) { continue; } mPendingDefaults << fullName; } } } void Firstrun::setupNext() { delete mCurrentDefault; mCurrentDefault = nullptr; if (mPendingDefaults.isEmpty()) { deleteLater(); return; } mCurrentDefault = new KConfig(mPendingDefaults.takeFirst()); const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, "Agent"); AgentType type = AgentManager::self()->type(agentCfg.readEntry("Type", QString())); if (!type.isValid()) { qCCritical(AKONADICORE_LOG) << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name(); setupNext(); return; } if (type.capabilities().contains(QLatin1String("Unique"))) { const Akonadi::AgentInstance::List lstAgents = AgentManager::self()->instances(); for (const AgentInstance &agent : lstAgents) { if (agent.type() == type) { // remember we set this one up already KConfigGroup cfg(mConfig, "ProcessedDefaults"); cfg.writeEntry(agentCfg.readEntry("Id", QString()), agent.identifier()); cfg.sync(); setupNext(); return; } } } AgentInstanceCreateJob *job = new AgentInstanceCreateJob(type); connect(job, &AgentInstanceCreateJob::result, this, &Firstrun::instanceCreated); job->start(); } void Firstrun::instanceCreated(KJob *job) { Q_ASSERT(mCurrentDefault); if (job->error()) { qCCritical(AKONADICORE_LOG) << "Creating agent instance failed for " << mCurrentDefault->name(); setupNext(); return; } AgentInstance instance = static_cast(job)->instance(); const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, "Agent"); const QString agentName = agentCfg.readEntry("Name", QString()); if (!agentName.isEmpty()) { instance.setName(agentName); } const auto service = ServerManager::agentServiceName(ServerManager::Agent, instance.identifier()); QDBusInterface *iface = new QDBusInterface(service, QStringLiteral("/Settings"), QString(), KDBusConnectionPool::threadConnection(), this); if (!iface->isValid()) { qCCritical(AKONADICORE_LOG) << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier(); setupNext(); delete iface; return; } // agent specific settings, using the D-Bus <-> KConfigXT bridge const KConfigGroup settings = KConfigGroup(mCurrentDefault, "Settings"); const QStringList lstSettings = settings.keyList(); for (const QString &setting : lstSettings) { qCDebug(AKONADICORE_LOG) << "Setting up " << setting << " for agent " << instance.identifier(); const QString methodName = QStringLiteral("set%1").arg(setting); const QVariant::Type argType = argumentType(iface->metaObject(), methodName); if (argType == QVariant::Invalid) { qCCritical(AKONADICORE_LOG) << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier(); continue; } QVariant arg; if (argType == QVariant::String) { // Since a string could be a path we always use readPathEntry here, // that shouldn't harm any normal string settings arg = settings.readPathEntry(setting, QString()); } else { arg = settings.readEntry(setting, QVariant(argType)); } const QDBusReply reply = iface->call(methodName, arg); if (!reply.isValid()) { qCCritical(AKONADICORE_LOG) << "Setting " << setting << " failed for agent " << instance.identifier(); } } iface->call(QStringLiteral("writeConfig")); instance.reconfigure(); instance.synchronize(); delete iface; // remember we set this one up already KConfigGroup cfg(mConfig, "ProcessedDefaults"); cfg.writeEntry(agentCfg.readEntry("Id", QString()), instance.identifier()); cfg.sync(); setupNext(); } QVariant::Type Firstrun::argumentType(const QMetaObject *mo, const QString &method) { QMetaMethod m; for (int i = 0; i < mo->methodCount(); ++i) { const QString signature = QString::fromLatin1(mo->method(i).methodSignature()); if (signature.startsWith(method)) { m = mo->method(i); } } if (m.methodSignature().isEmpty()) { return QVariant::Invalid; } const QList argTypes = m.parameterTypes(); if (argTypes.count() != 1) { return QVariant::Invalid; } return QVariant::nameToType(argTypes.first().constData()); } #include "moc_firstrun_p.cpp" diff --git a/src/core/models/itemmodel.cpp b/src/core/models/itemmodel.cpp index 9a4deb465..23c84927c 100644 --- a/src/core/models/itemmodel.cpp +++ b/src/core/models/itemmodel.cpp @@ -1,471 +1,470 @@ /* Copyright (c) 2006 - 2007 Volker Krause 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 "itemmodel.h" #include "akonadicore_debug.h" #include "itemfetchjob.h" #include "collectionfetchjob.h" #include "itemfetchscope.h" #include "monitor.h" #include "pastehelper_p.h" #include "session.h" #include #include #include #include using namespace Akonadi; /** * @internal * * This struct is used for optimization reasons. * because it embeds the row. * * Semantically, we could have used an item instead. */ struct ItemContainer { ItemContainer(const Item &i, int r) : item(i) , row(r) { } Item item; int row; }; /** * @internal */ class Q_DECL_HIDDEN ItemModel::Private { public: Private(ItemModel *parent) : mParent(parent) , monitor(new Monitor()) { session = new Session(QCoreApplication::instance()->applicationName().toUtf8() + QByteArray("-ItemModel-") + QByteArray::number(qrand()), mParent); monitor->setObjectName(QStringLiteral("ItemModelMonitor")); monitor->ignoreSession(session); mParent->connect(monitor, &Monitor::itemChanged, mParent, [this](const Akonadi::Item &item, const QSet &set) { itemChanged(item, set); }); mParent->connect(monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), mParent, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))); mParent->connect(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), mParent, SLOT(itemAdded(Akonadi::Item))); mParent->connect(monitor, &Monitor::itemRemoved, mParent, [this](const Akonadi::Item &item) { itemRemoved(item); }); mParent->connect(monitor, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)), mParent, SLOT(itemAdded(Akonadi::Item))); mParent->connect(monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)), mParent, SLOT(itemRemoved(Akonadi::Item))); } ~Private() { delete monitor; } void listingDone(KJob *job); void collectionFetchResult(KJob *job); void itemChanged(const Akonadi::Item &item, const QSet &); void itemsAdded(const Akonadi::Item::List &list); void itemAdded(const Akonadi::Item &item); void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &src, const Akonadi::Collection &dst); void itemRemoved(const Akonadi::Item &item); int rowForItem(const Akonadi::Item &item); bool collectionIsCompatible() const; ItemModel *mParent = nullptr; QList items; QHash itemHash; Collection collection; Monitor *monitor = nullptr; Session *session = nullptr; }; bool ItemModel::Private::collectionIsCompatible() const { // in the generic case, we show any collection if (mParent->mimeTypes() == QStringList(QStringLiteral("text/uri-list"))) { return true; } // if the model's mime types are more specific, limit to those // collections that have matching types const QStringList lstMimetypes = mParent->mimeTypes(); for (const QString &type : lstMimetypes ) { if (collection.contentMimeTypes().contains(type)) { return true; } } return false; } void ItemModel::Private::listingDone(KJob *job) { ItemFetchJob *fetch = static_cast(job); Q_UNUSED(fetch); if (job->error()) { // TODO qCWarning(AKONADICORE_LOG) << "Item query failed:" << job->errorString(); } } void ItemModel::Private::collectionFetchResult(KJob *job) { CollectionFetchJob *fetch = static_cast(job); if (fetch->collections().isEmpty()) { return; } Q_ASSERT(fetch->collections().count() == 1); // we only listed base Collection c = fetch->collections().at(0); // avoid recursion, if this fails for some reason if (!c.contentMimeTypes().isEmpty()) { mParent->setCollection(c); } else { qCWarning(AKONADICORE_LOG) << "Failed to retrieve the contents mime type of the collection: " << c; mParent->setCollection(Collection()); } } int ItemModel::Private::rowForItem(const Akonadi::Item &item) { ItemContainer *container = itemHash.value(item); if (!container) { return -1; } /* Try to find the item directly; If items have been removed, this first try won't succeed because the ItemContainer rows have not been updated (costs too much). */ if (container->row < items.count() && items.at(container->row) == container) { return container->row; } else { // Slow solution if the fist one has not succeeded int row = -1; const int numberOfItems(items.size()); for (int i = 0; i < numberOfItems; ++i) { if (items.at(i)->item == item) { row = i; break; } } return row; } } void ItemModel::Private::itemChanged(const Akonadi::Item &item, const QSet &) { int row = rowForItem(item); if (row < 0) { return; } items[row]->item = item; itemHash.remove(item); itemHash[item] = items[row]; QModelIndex start = mParent->index(row, 0, QModelIndex()); QModelIndex end = mParent->index(row, mParent->columnCount(QModelIndex()) - 1, QModelIndex()); mParent->dataChanged(start, end); } void ItemModel::Private::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &colSrc, const Akonadi::Collection &colDst) { if (colSrc == collection && colDst != collection) { // item leaving this model itemRemoved(item); return; } if (colDst == collection && colSrc != collection) { itemAdded(item); return; } } void ItemModel::Private::itemsAdded(const Akonadi::Item::List &list) { if (list.isEmpty()) { return; } mParent->beginInsertRows(QModelIndex(), items.count(), items.count() + list.count() - 1); for (const Item &item : list) { ItemContainer *c = new ItemContainer(item, items.count()); items.append(c); itemHash[item] = c; } mParent->endInsertRows(); } void ItemModel::Private::itemAdded(const Akonadi::Item &item) { const Item::List l = {item}; itemsAdded(l); } void ItemModel::Private::itemRemoved(const Akonadi::Item &_item) { int row = rowForItem(_item); if (row < 0) { return; } mParent->beginRemoveRows(QModelIndex(), row, row); const Item item = items.at(row)->item; Q_ASSERT(item.isValid()); itemHash.remove(item); delete items.takeAt(row); mParent->endRemoveRows(); } ItemModel::ItemModel(QObject *parent) : QAbstractTableModel(parent) , d(new Private(this)) { } ItemModel::~ItemModel() { delete d; } QVariant ItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= d->items.count()) { return QVariant(); } const Item item = d->items.at(index.row())->item; if (!item.isValid()) { return QVariant(); } if (role == Qt::DisplayRole) { switch (index.column()) { case Id: return QString::number(item.id()); case RemoteId: return item.remoteId(); case MimeType: return item.mimeType(); default: return QVariant(); } } if (role == IdRole) { return item.id(); } if (role == ItemRole) { QVariant var; var.setValue(item); return var; } if (role == MimeTypeRole) { return item.mimeType(); } return QVariant(); } int ItemModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { return d->items.count(); } return 0; } int ItemModel::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) { return 3; // keep in sync with Column enum } return 0; } QVariant ItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case Id: return i18n("Id"); case RemoteId: return i18n("Remote Id"); case MimeType: return i18n("MimeType"); default: return QString(); } } return QAbstractTableModel::headerData(section, orientation, role); } void ItemModel::setCollection(const Collection &collection) { - qCDebug(AKONADICORE_LOG); if (d->collection == collection) { return; } // if we don't know anything about this collection yet, fetch it if (collection.isValid() && collection.contentMimeTypes().isEmpty()) { CollectionFetchJob *job = new CollectionFetchJob(collection, CollectionFetchJob::Base, this); connect(job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*))); return; } beginResetModel(); d->monitor->setCollectionMonitored(d->collection, false); d->collection = collection; d->monitor->setCollectionMonitored(d->collection, true); // the query changed, thus everything we have already is invalid qDeleteAll(d->items); d->items.clear(); // stop all running jobs d->session->clear(); endResetModel(); // start listing job if (d->collectionIsCompatible()) { ItemFetchJob *job = new ItemFetchJob(collection, session()); job->setFetchScope(d->monitor->itemFetchScope()); connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(itemsAdded(Akonadi::Item::List))); connect(job, &ItemFetchJob::result, this, [this](KJob *job) { d->listingDone(job); }); } emit collectionChanged(collection); } void ItemModel::setFetchScope(const ItemFetchScope &fetchScope) { d->monitor->setItemFetchScope(fetchScope); } ItemFetchScope &ItemModel::fetchScope() { return d->monitor->itemFetchScope(); } Item ItemModel::itemForIndex(const QModelIndex &index) const { if (!index.isValid()) { return Akonadi::Item(); } if (index.row() >= d->items.count()) { return Akonadi::Item(); } Item item = d->items.at(index.row())->item; if (item.isValid()) { return item; } else { return Akonadi::Item(); } } Qt::ItemFlags ItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } else { return Qt::ItemIsDropEnabled | defaultFlags; } } QStringList ItemModel::mimeTypes() const { return {QStringLiteral("text/uri-list")}; } Session *ItemModel::session() const { return d->session; } QMimeData *ItemModel::mimeData(const QModelIndexList &indexes) const { QMimeData *data = new QMimeData(); // Add item uri to the mimedata for dropping in external applications QList urls; for (const QModelIndex &index : indexes) { if (index.column() != 0) { continue; } urls << itemForIndex(index).url(Item::UrlWithMimeType); } data->setUrls(urls); return data; } QModelIndex ItemModel::indexForItem(const Akonadi::Item &item, const int column) const { return index(d->rowForItem(item), column); } bool ItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); Q_UNUSED(column); Q_UNUSED(parent); KJob *job = PasteHelper::paste(data, d->collection, action != Qt::MoveAction); // TODO: error handling return job; } Collection ItemModel::collection() const { return d->collection; } Qt::DropActions ItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; } #include "moc_itemmodel.cpp"