• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
itemmodel.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "itemmodel.h"
21 
22 #include "itemfetchjob.h"
23 #include "collectionfetchjob.h"
24 #include "itemfetchscope.h"
25 #include "monitor.h"
26 #include "pastehelper_p.h"
27 #include "session.h"
28 
29 #include <kdebug.h>
30 #include <klocalizedstring.h>
31 #include <kurl.h>
32 
33 #include <QCoreApplication>
34 #include <QtCore/QDebug>
35 #include <QtCore/QMimeData>
36 
37 using namespace Akonadi;
38 
47 struct ItemContainer
48 {
49  ItemContainer(const Item &i, int r)
50  : item(i)
51  , row(r)
52  {
53  }
54  Item item;
55  int row;
56 };
57 
61 class ItemModel::Private
62 {
63 public:
64  Private(ItemModel *parent)
65  : mParent(parent)
66  , monitor(new Monitor())
67  {
68  session = new Session(QCoreApplication::instance()->applicationName().toUtf8()
69  + QByteArray("-ItemModel-") + QByteArray::number(qrand()), mParent);
70 
71  monitor->ignoreSession(session);
72 
73  mParent->connect(monitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
74  mParent, SLOT(itemChanged(Akonadi::Item,QSet<QByteArray>)));
75  mParent->connect(monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
76  mParent, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
77  mParent->connect(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
78  mParent, SLOT(itemAdded(Akonadi::Item)));
79  mParent->connect(monitor, SIGNAL(itemRemoved(Akonadi::Item)),
80  mParent, SLOT(itemRemoved(Akonadi::Item)));
81  mParent->connect(monitor, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
82  mParent, SLOT(itemAdded(Akonadi::Item)));
83  mParent->connect(monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
84  mParent, SLOT(itemRemoved(Akonadi::Item)));
85  }
86 
87  ~Private()
88  {
89  delete monitor;
90  }
91 
92  void listingDone(KJob *job);
93  void collectionFetchResult(KJob *job);
94  void itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &);
95  void itemsAdded(const Akonadi::Item::List &list);
96  void itemAdded(const Akonadi::Item &item);
97  void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &src, const Akonadi::Collection &dst);
98  void itemRemoved(const Akonadi::Item &item);
99  int rowForItem(const Akonadi::Item &item);
100  bool collectionIsCompatible() const;
101 
102  ItemModel *mParent;
103 
104  QList<ItemContainer *> items;
105  QHash<Item, ItemContainer *> itemHash;
106 
107  Collection collection;
108  Monitor *monitor;
109  Session *session;
110 };
111 
112 bool ItemModel::Private::collectionIsCompatible() const
113 {
114  // in the generic case, we show any collection
115  if (mParent->mimeTypes() == QStringList(QLatin1String("text/uri-list"))) {
116  return true;
117  }
118  // if the model's mime types are more specific, limit to those
119  // collections that have matching types
120  Q_FOREACH (const QString &type, mParent->mimeTypes()) {
121  if (collection.contentMimeTypes().contains(type)) {
122  return true;
123  }
124  }
125  return false;
126 }
127 
128 void ItemModel::Private::listingDone(KJob *job)
129 {
130  ItemFetchJob *fetch = static_cast<ItemFetchJob *>(job);
131  Q_UNUSED(fetch);
132  if (job->error()) {
133  // TODO
134  kWarning() << "Item query failed:" << job->errorString();
135  }
136 }
137 
138 void ItemModel::Private::collectionFetchResult(KJob *job)
139 {
140  CollectionFetchJob *fetch = static_cast<CollectionFetchJob *>(job);
141 
142  if (fetch->collections().isEmpty()) {
143  return;
144  }
145 
146  Q_ASSERT(fetch->collections().count() == 1); // we only listed base
147  Collection c = fetch->collections().first();
148  // avoid recursion, if this fails for some reason
149  if (!c.contentMimeTypes().isEmpty()) {
150  mParent->setCollection(c);
151  } else {
152  kWarning() << "Failed to retrieve the contents mime type of the collection: " << c;
153  mParent->setCollection(Collection());
154  }
155 }
156 
157 int ItemModel::Private::rowForItem(const Akonadi::Item &item)
158 {
159  ItemContainer *container = itemHash.value(item);
160  if (!container) {
161  return -1;
162  }
163 
164  /* Try to find the item directly;
165 
166  If items have been removed, this first try won't succeed because
167  the ItemContainer rows have not been updated (costs too much).
168  */
169  if (container->row < items.count()
170  && items.at(container->row) == container) {
171  return container->row;
172  } else {
173  // Slow solution if the fist one has not succeeded
174  int row = -1;
175  const int numberOfItems(items.size());
176  for (int i = 0; i < numberOfItems; ++i) {
177  if (items.at(i)->item == item) {
178  row = i;
179  break;
180  }
181  }
182  return row;
183  }
184 
185 }
186 
187 void ItemModel::Private::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &)
188 {
189  int row = rowForItem(item);
190  if (row < 0) {
191  return;
192  }
193 
194  items[row]->item = item;
195  itemHash.remove(item);
196  itemHash[item] = items[row];
197 
198  QModelIndex start = mParent->index(row, 0, QModelIndex());
199  QModelIndex end = mParent->index(row, mParent->columnCount(QModelIndex()) - 1 , QModelIndex());
200 
201  mParent->dataChanged(start, end);
202 }
203 
204 void ItemModel::Private::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &colSrc, const Akonadi::Collection &colDst)
205 {
206  if (colSrc == collection && colDst != collection) {
207  // item leaving this model
208  itemRemoved(item);
209  return;
210  }
211 
212  if (colDst == collection && colSrc != collection) {
213  itemAdded(item);
214  return;
215  }
216 }
217 
218 void ItemModel::Private::itemsAdded(const Akonadi::Item::List &list)
219 {
220  if (list.isEmpty()) {
221  return;
222  }
223  mParent->beginInsertRows(QModelIndex(), items.count(), items.count() + list.count() - 1);
224  foreach (const Item &item, list) {
225  ItemContainer *c = new ItemContainer(item, items.count());
226  items.append(c);
227  itemHash[item] = c;
228  }
229  mParent->endInsertRows();
230 }
231 
232 void ItemModel::Private::itemAdded(const Akonadi::Item &item)
233 {
234  Item::List l;
235  l << item;
236  itemsAdded(l);
237 }
238 
239 void ItemModel::Private::itemRemoved(const Akonadi::Item &_item)
240 {
241  int row = rowForItem(_item);
242  if (row < 0) {
243  return;
244  }
245 
246  mParent->beginRemoveRows(QModelIndex(), row, row);
247  const Item item = items.at(row)->item;
248  Q_ASSERT(item.isValid());
249  itemHash.remove(item);
250  delete items.takeAt(row);
251  mParent->endRemoveRows();
252 }
253 
254 ItemModel::ItemModel(QObject *parent)
255  : QAbstractTableModel(parent)
256  , d(new Private(this))
257 {
258 }
259 
260 ItemModel::~ItemModel()
261 {
262  delete d;
263 }
264 
265 QVariant ItemModel::data(const QModelIndex &index, int role) const
266 {
267  if (!index.isValid()) {
268  return QVariant();
269  }
270  if (index.row() >= d->items.count()) {
271  return QVariant();
272  }
273  const Item item = d->items.at(index.row())->item;
274  if (!item.isValid()) {
275  return QVariant();
276  }
277 
278  if (role == Qt::DisplayRole) {
279  switch (index.column()) {
280  case Id:
281  return QString::number(item.id());
282  case RemoteId:
283  return item.remoteId();
284  case MimeType:
285  return item.mimeType();
286  default:
287  return QVariant();
288  }
289  }
290 
291  if (role == IdRole) {
292  return item.id();
293  }
294 
295  if (role == ItemRole) {
296  QVariant var;
297  var.setValue(item);
298  return var;
299  }
300 
301  if (role == MimeTypeRole) {
302  return item.mimeType();
303  }
304 
305  return QVariant();
306 }
307 
308 int ItemModel::rowCount(const QModelIndex &parent) const
309 {
310  if (!parent.isValid()) {
311  return d->items.count();
312  }
313  return 0;
314 }
315 
316 int ItemModel::columnCount(const QModelIndex &parent) const
317 {
318  if (!parent.isValid()) {
319  return 3; // keep in sync with Column enum
320  }
321  return 0;
322 }
323 
324 QVariant ItemModel::headerData(int section, Qt::Orientation orientation, int role) const
325 {
326  if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
327  switch (section) {
328  case Id:
329  return i18n("Id");
330  case RemoteId:
331  return i18n("Remote Id");
332  case MimeType:
333  return i18n("MimeType");
334  default:
335  return QString();
336  }
337  }
338  return QAbstractTableModel::headerData(section, orientation, role);
339 }
340 
341 void ItemModel::setCollection(const Collection &collection)
342 {
343  kDebug();
344  if (d->collection == collection) {
345  return;
346  }
347 
348  // if we don't know anything about this collection yet, fetch it
349  if (collection.isValid() && collection.contentMimeTypes().isEmpty()) {
350  CollectionFetchJob *job = new CollectionFetchJob(collection, CollectionFetchJob::Base, this);
351  connect(job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*)));
352  return;
353  }
354 
355  d->monitor->setCollectionMonitored(d->collection, false);
356 
357  d->collection = collection;
358 
359  d->monitor->setCollectionMonitored(d->collection, true);
360 
361  // the query changed, thus everything we have already is invalid
362  qDeleteAll(d->items);
363  d->items.clear();
364  reset();
365 
366  // stop all running jobs
367  d->session->clear();
368 
369  // start listing job
370  if (d->collectionIsCompatible()) {
371  ItemFetchJob *job = new ItemFetchJob(collection, session());
372  job->setFetchScope(d->monitor->itemFetchScope());
373  connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)),
374  SLOT(itemsAdded(Akonadi::Item::List)));
375  connect(job, SIGNAL(result(KJob*)), SLOT(listingDone(KJob*)));
376  }
377 
378  emit collectionChanged(collection);
379 }
380 
381 void ItemModel::setFetchScope(const ItemFetchScope &fetchScope)
382 {
383  d->monitor->setItemFetchScope(fetchScope);
384 }
385 
386 ItemFetchScope &ItemModel::fetchScope()
387 {
388  return d->monitor->itemFetchScope();
389 }
390 
391 Item ItemModel::itemForIndex(const QModelIndex &index) const
392 {
393  if (!index.isValid()) {
394  return Akonadi::Item();
395  }
396 
397  if (index.row() >= d->items.count()) {
398  return Akonadi::Item();
399  }
400 
401  Item item = d->items.at(index.row())->item;
402  if (item.isValid()) {
403  return item;
404  } else {
405  return Akonadi::Item();
406  }
407 }
408 
409 Qt::ItemFlags ItemModel::flags(const QModelIndex &index) const
410 {
411  Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
412 
413  if (index.isValid()) {
414  return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
415  } else {
416  return Qt::ItemIsDropEnabled | defaultFlags;
417  }
418 }
419 
420 QStringList ItemModel::mimeTypes() const
421 {
422  return QStringList() << QLatin1String("text/uri-list");
423 }
424 
425 Session *ItemModel::session() const
426 {
427  return d->session;
428 }
429 
430 QMimeData *ItemModel::mimeData(const QModelIndexList &indexes) const
431 {
432  QMimeData *data = new QMimeData();
433  // Add item uri to the mimedata for dropping in external applications
434  KUrl::List urls;
435  foreach (const QModelIndex &index, indexes) {
436  if (index.column() != 0) {
437  continue;
438  }
439 
440  urls << itemForIndex(index).url(Item::UrlWithMimeType);
441  }
442  urls.populateMimeData(data);
443 
444  return data;
445 }
446 
447 QModelIndex ItemModel::indexForItem(const Akonadi::Item &item, const int column) const
448 {
449  return index(d->rowForItem(item), column);
450 }
451 
452 bool ItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
453 {
454  Q_UNUSED(row);
455  Q_UNUSED(column);
456  Q_UNUSED(parent);
457  KJob *job = PasteHelper::paste(data, d->collection, action != Qt::MoveAction);
458  // TODO: error handling
459  return job;
460 }
461 
462 Collection ItemModel::collection() const
463 {
464  return d->collection;
465 }
466 
467 Qt::DropActions ItemModel::supportedDropActions() const
468 {
469  return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
470 }
471 
472 #include "moc_itemmodel.cpp"
QModelIndex
Akonadi::ItemModel::collectionChanged
void collectionChanged(const Akonadi::Collection &collection)
This signal is emitted whenever setCollection is called.
Akonadi::CollectionFetchJob::collections
Collection::List collections() const
Returns the list of fetched collection.
Definition: collectionfetchjob.cpp:169
QByteArray
QAbstractTableModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
Akonadi::ItemModel::session
Session * session() const
Returns the Session object used for all operations by this model.
Definition: itemmodel.cpp:425
QAbstractTableModel
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
Akonadi::PasteHelper::paste
KJob * paste(const QMimeData *mimeData, const Collection &collection, bool copy=true, Session *session=0)
Paste/drop the given mime data into the given collection.
Definition: pastehelper.cpp:272
Akonadi::ItemModel::fetchScope
ItemFetchScope & fetchScope()
Returns the item fetch scope.
Definition: itemmodel.cpp:386
Akonadi::CollectionFetchJob
Job that fetches collections from the Akonadi storage.
Definition: collectionfetchjob.h:53
Akonadi::ItemModel::ItemModel
ItemModel(QObject *parent=0)
Creates a new item model.
Definition: itemmodel.cpp:254
QMimeData
Akonadi::CollectionFetchJob::Base
Only fetch the base collection.
Definition: collectionfetchjob.h:62
Akonadi::ItemModel::itemForIndex
Item itemForIndex(const QModelIndex &index) const
Returns the item at the given index.
Definition: itemmodel.cpp:391
QAbstractItemModel::reset
void reset()
QModelIndex::isValid
bool isValid() const
QString::number
QString number(int n, int base)
QList::count
int count(const T &value) const
Akonadi::ItemFetchJob::setFetchScope
void setFetchScope(ItemFetchScope &fetchScope)
Sets the item fetch scope.
Definition: itemfetchjob.cpp:247
QHash< Item, ItemContainer * >
Akonadi::ItemModel::IdRole
The id of the item.
Definition: itemmodel.h:74
QObject
QList::isEmpty
bool isEmpty() const
Akonadi::ItemModel::MimeType
The item's mime type.
Definition: itemmodel.h:67
Akonadi::ItemModel
A table model for items.
Definition: itemmodel.h:56
QByteArray::number
QByteArray number(int n, int base)
QModelIndex::row
int row() const
Akonadi::ItemModel::RemoteId
The remote identifier.
Definition: itemmodel.h:66
Akonadi::ItemModel::Id
The unique id.
Definition: itemmodel.h:65
Akonadi::Session
A communication session with the Akonadi storage.
Definition: session.h:59
QCoreApplication::instance
QCoreApplication * instance()
QSet< QByteArray >
QList::first
T & first()
QString
QList< ItemContainer * >
Akonadi::ItemModel::MimeTypeRole
The mime type of the item.
Definition: itemmodel.h:76
QStringList
QAbstractItemModel::headerData
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
Akonadi::ItemFetchScope
Specifies which parts of an item should be fetched from the Akonadi storage.
Definition: itemfetchscope.h:69
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QVariant::setValue
void setValue(const T &value)
Akonadi::ItemModel::setCollection
void setCollection(const Akonadi::Collection &collection)
Sets the collection the model should display.
Definition: itemmodel.cpp:341
Akonadi::ItemModel::ItemRole
The item object.
Definition: itemmodel.h:75
Akonadi::Monitor
Monitors an item or collection for changes.
Definition: monitor.h:74
Akonadi::ItemModel::setFetchScope
void setFetchScope(const ItemFetchScope &fetchScope)
Sets the item fetch scope.
Definition: itemmodel.cpp:381
QLatin1String
Qt::DropActions
typedef DropActions
Akonadi::ItemModel::indexForItem
QModelIndex indexForItem(const Akonadi::Item &item, const int column) const
Returns the model index for the given item, with the given column.
Definition: itemmodel.cpp:447
Akonadi::ItemFetchJob
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:82
QModelIndex::column
int column() const
Akonadi::Collection::contentMimeTypes
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
Definition: collection.cpp:115
QAbstractItemModel::flags
virtual Qt::ItemFlags flags(const QModelIndex &index) const
Akonadi::ItemModel::collection
Collection collection() const
Returns the collection being displayed in the model.
Definition: itemmodel.cpp:462
Akonadi::Entity::isValid
bool isValid() const
Returns whether the entity is valid.
Definition: entity.cpp:97
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const
Akonadi::ItemModel::~ItemModel
virtual ~ItemModel()
Destroys the item model.
Definition: itemmodel.cpp:260
QVariant
Qt::ItemFlags
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal