Verified Commit bf118e29 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Remove the deprecated ItemModel

ItemModel was used only by MessageModel in AkonadiMime,
which has been ported to ETM.
parent 4975a22c
......@@ -147,7 +147,6 @@ set(akonadicore_models_SRCS
models/entitytreemodel.cpp
models/entitytreemodel_p.cpp
models/favoritecollectionsmodel.cpp
models/itemmodel.cpp
models/recursivecollectionfilterproxymodel.cpp
models/selectionproxymodel.cpp
models/statisticsproxymodel.cpp
......@@ -168,7 +167,6 @@ ecm_generate_headers(AkonadiCore_models_HEADERS
EntityRightsFilterModel
EntityTreeModel
FavoriteCollectionsModel
ItemModel
RecursiveCollectionFilterProxyModel
SelectionProxyModel
StatisticsProxyModel
......
/*
Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
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 <KLocalizedString>
#include <QUrl>
#include <QCoreApplication>
#include <QMimeData>
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<QByteArray> &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<QByteArray> &);
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<ItemContainer *> items;
QHash<Item, ItemContainer *> 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<ItemFetchJob *>(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<CollectionFetchJob *>(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<QByteArray> &)
{
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());
Q_EMIT 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)
{
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); });
}
Q_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<QUrl> 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"
/*
Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
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.
*/
#ifndef AKONADI_ITEMMODEL_H
#define AKONADI_ITEMMODEL_H
#include "akonadicore_export.h"
#include "item.h"
#include "job.h"
#include <QAbstractTableModel>
namespace Akonadi
{
class Collection;
class ItemFetchScope;
class Job;
class Session;
/**
* @short A table model for items.
*
* A self-updating table model that shows all items of
* a collection.
*
* @code
*
* QTableView *view = new QTableView( this );
*
* Akonadi::ItemModel *model = new Akonadi::ItemModel();
* view->setModel( model );
*
* model->setCollection( Akonadi::Collection::root() );
*
* @endcode
*
* @author Volker Krause <vkrause@kde.org>
* @deprecated Use Akonadi::EntityTreeModel instead
*/
class AKONADICORE_DEPRECATED_EXPORT ItemModel : public QAbstractTableModel
{
Q_OBJECT
public:
/**
* Describes the types of the columns in the model.
*/
enum Column {
Id = 0, ///< The unique id.
RemoteId, ///< The remote identifier.
MimeType ///< The item's mime type.
};
/**
* Describes the roles of the model.
*/
enum Roles {
IdRole = Qt::UserRole + 1, ///< The id of the item.
ItemRole, ///< The item object.
MimeTypeRole, ///< The mime type of the item.
UserRole = Qt::UserRole + 42 ///< Role for user extensions.
};
/**
* Creates a new item model.
*
* @param parent The parent object.
*/
explicit ItemModel(QObject *parent = nullptr);
/**
* Destroys the item model.
*/
~ItemModel() override;
Q_REQUIRED_RESULT int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Q_REQUIRED_RESULT QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_REQUIRED_RESULT int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_REQUIRED_RESULT QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Q_REQUIRED_RESULT Qt::ItemFlags flags(const QModelIndex &index) const override;
Q_REQUIRED_RESULT QMimeData *mimeData(const QModelIndexList &indexes) const override;
Q_REQUIRED_RESULT QStringList mimeTypes() const override;
Q_REQUIRED_RESULT Qt::DropActions supportedDropActions() const override;
/**
* Sets the item fetch scope.
*
* The ItemFetchScope controls how much of an item's data is fetched from the
* server, e.g. whether to fetch the full item payload or only meta data.
*
* @param fetchScope The new scope for item fetch operations.
*
* @see fetchScope()
*/
void setFetchScope(const ItemFetchScope &fetchScope);
/**
* Returns the item fetch scope.
*
* Since this returns a reference it can be used to conveniently modify the
* current scope in-place, i.e. by calling a method on the returned reference
* without storing it in a local variable. See the ItemFetchScope documentation
* for an example.
*
* @return a reference to the current item fetch scope.
*