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

akonadi

  • 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 <kmime/kmime_message.h>
30 
31 #include <kdebug.h>
32 #include <klocale.h>
33 #include <kurl.h>
34 
35 #include <QCoreApplication>
36 #include <QtCore/QDebug>
37 #include <QtCore/QMimeData>
38 
39 using namespace Akonadi;
40 
49 struct ItemContainer
50 {
51  ItemContainer( const Item& i, int r )
52  : item( i ), row( r )
53  {
54  }
55  Item item;
56  int row;
57 };
58 
62 class ItemModel::Private
63 {
64  public:
65  Private( ItemModel *parent )
66  : mParent( parent ), 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* );
93  void collectionFetchResult( KJob* );
94  void itemChanged( const Akonadi::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&, const Akonadi::Collection& src, const Akonadi::Collection& dst );
98  void itemRemoved( const Akonadi::Item& );
99  int rowForItem( const Akonadi::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  // if the model's mime types are more specific, limit to those
118  // collections that have matching types
119  Q_FOREACH( const QString &type, mParent->mimeTypes() ) {
120  if ( collection.contentMimeTypes().contains( type ) ) {
121  return true;
122  }
123  }
124  return false;
125 }
126 
127 void ItemModel::Private::listingDone( KJob * job )
128 {
129  ItemFetchJob *fetch = static_cast<ItemFetchJob*>( job );
130  Q_UNUSED( fetch );
131  if ( job->error() ) {
132  // TODO
133  kWarning() << "Item query failed:" << job->errorString();
134  }
135 }
136 
137 void ItemModel::Private::collectionFetchResult( KJob * job )
138 {
139  CollectionFetchJob *fetch = static_cast<CollectionFetchJob*>( job );
140 
141  if ( fetch->collections().isEmpty() )
142  return;
143 
144  Q_ASSERT( fetch->collections().count() == 1 ); // we only listed base
145  Collection c = fetch->collections().first();
146  // avoid recursion, if this fails for some reason
147  if ( !c.contentMimeTypes().isEmpty() ) {
148  mParent->setCollection(c);
149  } else {
150  kWarning() << "Failed to retrieve the contents mime type of the collection: " << c;
151  mParent->setCollection(Collection());
152  }
153 }
154 
155 int ItemModel::Private::rowForItem( const Akonadi::Item& item )
156 {
157  ItemContainer *container = itemHash.value( item );
158  if ( !container )
159  return -1;
160 
161  /* Try to find the item directly;
162 
163  If items have been removed, this first try won't succeed because
164  the ItemContainer rows have not been updated (costs too much).
165  */
166  if ( container->row < items.count()
167  && items.at( container->row ) == container )
168  return container->row;
169  else { // Slow solution if the fist one has not succeeded
170  int row = -1;
171  const int numberOfItems( items.size() );
172  for ( int i = 0; i < numberOfItems; ++i ) {
173  if ( items.at( i )->item == item ) {
174  row = i;
175  break;
176  }
177  }
178  return row;
179  }
180 
181 }
182 
183 void ItemModel::Private::itemChanged( const Akonadi::Item &item, const QSet<QByteArray>& )
184 {
185  int row = rowForItem( item );
186  if ( row < 0 )
187  return;
188 
189  items[ row ]->item = item;
190  itemHash.remove( item );
191  itemHash[ item ] = items[ row ];
192 
193  QModelIndex start = mParent->index( row, 0, QModelIndex() );
194  QModelIndex end = mParent->index( row, mParent->columnCount( QModelIndex() ) - 1 , QModelIndex() );
195 
196  mParent->dataChanged( start, end );
197 }
198 
199 void ItemModel::Private::itemMoved( const Akonadi::Item &item, const Akonadi::Collection& colSrc, const Akonadi::Collection& colDst )
200 {
201  if ( colSrc == collection && colDst != collection ) // item leaving this model
202  {
203  itemRemoved( item );
204  return;
205  }
206 
207 
208  if ( colDst == collection && colSrc != collection )
209  {
210  itemAdded( item );
211  return;
212  }
213 }
214 
215 void ItemModel::Private::itemsAdded( const Akonadi::Item::List &list )
216 {
217  if ( list.isEmpty() )
218  return;
219  mParent->beginInsertRows( QModelIndex(), items.count(), items.count() + list.count() - 1 );
220  foreach ( const Item &item, list ) {
221  ItemContainer *c = new ItemContainer( item, items.count() );
222  items.append( c );
223  itemHash[ item ] = c;
224  }
225  mParent->endInsertRows();
226 }
227 
228 void ItemModel::Private::itemAdded( const Akonadi::Item &item )
229 {
230  Item::List l;
231  l << item;
232  itemsAdded( l );
233 }
234 
235 void ItemModel::Private::itemRemoved( const Akonadi::Item &_item )
236 {
237  int row = rowForItem( _item );
238  if ( row < 0 )
239  return;
240 
241  mParent->beginRemoveRows( QModelIndex(), row, row );
242  const Item item = items.at( row )->item;
243  Q_ASSERT( item.isValid() );
244  itemHash.remove( item );
245  delete items.takeAt( row );
246  mParent->endRemoveRows();
247 }
248 
249 ItemModel::ItemModel( QObject *parent ) :
250  QAbstractTableModel( parent ),
251  d( new Private( this ) )
252 {
253 }
254 
255 ItemModel::~ItemModel()
256 {
257  delete d;
258 }
259 
260 QVariant ItemModel::data( const QModelIndex & index, int role ) const
261 {
262  if ( !index.isValid() )
263  return QVariant();
264  if ( index.row() >= d->items.count() )
265  return QVariant();
266  const Item item = d->items.at( index.row() )->item;
267  if ( !item.isValid() )
268  return QVariant();
269 
270  if ( role == Qt::DisplayRole ) {
271  switch ( index.column() ) {
272  case Id:
273  return QString::number( item.id() );
274  case RemoteId:
275  return item.remoteId();
276  case MimeType:
277  return item.mimeType();
278  default:
279  return QVariant();
280  }
281  }
282 
283  if ( role == IdRole )
284  return item.id();
285 
286  if ( role == ItemRole ) {
287  QVariant var;
288  var.setValue( item );
289  return var;
290  }
291 
292  if ( role == MimeTypeRole )
293  return item.mimeType();
294 
295  return QVariant();
296 }
297 
298 int ItemModel::rowCount( const QModelIndex & parent ) const
299 {
300  if ( !parent.isValid() )
301  return d->items.count();
302  return 0;
303 }
304 
305 int ItemModel::columnCount(const QModelIndex & parent) const
306 {
307  if ( !parent.isValid() )
308  return 3; // keep in sync with Column enum
309  return 0;
310 }
311 
312 QVariant ItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
313 {
314  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
315  switch ( section ) {
316  case Id:
317  return i18n( "Id" );
318  case RemoteId:
319  return i18n( "Remote Id" );
320  case MimeType:
321  return i18n( "MimeType" );
322  default:
323  return QString();
324  }
325  }
326  return QAbstractTableModel::headerData( section, orientation, role );
327 }
328 
329 void ItemModel::setCollection( const Collection &collection )
330 {
331  kDebug();
332  if ( d->collection == collection )
333  return;
334 
335  // if we don't know anything about this collection yet, fetch it
336  if ( collection.isValid() && collection.contentMimeTypes().isEmpty() )
337  {
338  CollectionFetchJob* job = new CollectionFetchJob( collection, CollectionFetchJob::Base, this );
339  connect( job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*)) );
340  return;
341  }
342 
343  d->monitor->setCollectionMonitored( d->collection, false );
344 
345  d->collection = collection;
346 
347  d->monitor->setCollectionMonitored( d->collection, true );
348 
349  // the query changed, thus everything we have already is invalid
350  qDeleteAll( d->items );
351  d->items.clear();
352  reset();
353 
354  // stop all running jobs
355  d->session->clear();
356 
357  // start listing job
358  if ( d->collectionIsCompatible() ) {
359  ItemFetchJob* job = new ItemFetchJob( collection, session() );
360  job->setFetchScope( d->monitor->itemFetchScope() );
361  connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)),
362  SLOT(itemsAdded(Akonadi::Item::List)) );
363  connect( job, SIGNAL(result(KJob*)), SLOT(listingDone(KJob*)) );
364  }
365 
366  emit collectionChanged( collection );
367 }
368 
369 void ItemModel::setFetchScope( const ItemFetchScope &fetchScope )
370 {
371  d->monitor->setItemFetchScope( fetchScope );
372 }
373 
374 ItemFetchScope &ItemModel::fetchScope()
375 {
376  return d->monitor->itemFetchScope();
377 }
378 
379 Item ItemModel::itemForIndex( const QModelIndex & index ) const
380 {
381  if ( !index.isValid() )
382  return Akonadi::Item();
383 
384  if ( index.row() >= d->items.count() )
385  return Akonadi::Item();
386 
387  Item item = d->items.at( index.row() )->item;
388  if ( item.isValid() ) {
389  return item;
390  } else {
391  return Akonadi::Item();
392  }
393 }
394 
395 Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const
396 {
397  Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
398 
399  if (index.isValid())
400  return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
401  else
402  return Qt::ItemIsDropEnabled | defaultFlags;
403 }
404 
405 QStringList ItemModel::mimeTypes() const
406 {
407  return QStringList() << QLatin1String( "text/uri-list" );
408 }
409 
410 Session * ItemModel::session() const
411 {
412  return d->session;
413 }
414 
415 QMimeData *ItemModel::mimeData( const QModelIndexList &indexes ) const
416 {
417  QMimeData *data = new QMimeData();
418  // Add item uri to the mimedata for dropping in external applications
419  KUrl::List urls;
420  foreach ( const QModelIndex &index, indexes ) {
421  if ( index.column() != 0 )
422  continue;
423 
424  urls << itemForIndex( index ).url( Item::UrlWithMimeType );
425  }
426  urls.populateMimeData( data );
427 
428  return data;
429 }
430 
431 QModelIndex ItemModel::indexForItem( const Akonadi::Item &item, const int column ) const
432 {
433  return index( d->rowForItem( item ), column );
434 }
435 
436 bool ItemModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
437 {
438  Q_UNUSED( row );
439  Q_UNUSED( column );
440  Q_UNUSED( parent );
441  KJob* job = PasteHelper::paste( data, d->collection, action != Qt::MoveAction );
442  // TODO: error handling
443  return job;
444 }
445 
446 Collection ItemModel::collection() const
447 {
448  return d->collection;
449 }
450 
451 Qt::DropActions ItemModel::supportedDropActions() const
452 {
453  return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
454 }
455 
456 
457 #include "moc_itemmodel.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:38 by doxygen 1.8.3.1 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-4.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 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
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