• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

akonadi

itemmodel.cpp

00001 /*
00002     Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #include "itemmodel.h"
00021 
00022 #include "itemfetchjob.h"
00023 #include "collectionfetchjob.h"
00024 #include "itemfetchscope.h"
00025 #include "monitor.h"
00026 #include "pastehelper_p.h"
00027 #include "session.h"
00028 
00029 #include <kmime/kmime_message.h>
00030 
00031 #include <kdebug.h>
00032 #include <klocale.h>
00033 #include <kurl.h>
00034 
00035 #include <QCoreApplication>
00036 #include <QtCore/QDebug>
00037 #include <QtCore/QMimeData>
00038 
00039 using namespace Akonadi;
00040 
00049 struct ItemContainer
00050 {
00051   ItemContainer( const Item& i, int r )
00052   {
00053     item = i;
00054     row = r;
00055   }
00056   Item item;
00057   int row;
00058 };
00059 
00063 class ItemModel::Private
00064 {
00065   public:
00066     Private( ItemModel *parent )
00067       : mParent( parent ), monitor( new Monitor() )
00068     {
00069       session = new Session( QCoreApplication::instance()->applicationName().toUtf8()
00070           + QByteArray("-ItemModel-") + QByteArray::number( qrand() ), mParent );
00071 
00072       monitor->ignoreSession( session );
00073 
00074       mParent->connect( monitor, SIGNAL(itemChanged( const Akonadi::Item&, const QSet<QByteArray>& )),
00075                         mParent, SLOT(itemChanged( const Akonadi::Item&, const QSet<QByteArray>& )) );
00076       mParent->connect( monitor, SIGNAL(itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& )),
00077                         mParent, SLOT(itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ) );
00078       mParent->connect( monitor, SIGNAL(itemAdded( const Akonadi::Item&, const Akonadi::Collection& )),
00079                         mParent, SLOT(itemAdded( const Akonadi::Item& )) );
00080       mParent->connect( monitor, SIGNAL(itemRemoved(Akonadi::Item)),
00081                         mParent, SLOT(itemRemoved(Akonadi::Item)) );
00082       mParent->connect( monitor, SIGNAL(itemLinked(const Akonadi::Item&, const Akonadi::Collection&)),
00083                         mParent, SLOT(itemAdded(const Akonadi::Item&)) );
00084       mParent->connect( monitor, SIGNAL(itemUnlinked(const Akonadi::Item&, const Akonadi::Collection&)),
00085                         mParent, SLOT(itemRemoved(const Akonadi::Item&)) );
00086     }
00087 
00088     ~Private()
00089     {
00090       delete monitor;
00091     }
00092 
00093     void listingDone( KJob* );
00094     void collectionFetchResult( KJob* );
00095     void itemChanged( const Akonadi::Item&, const QSet<QByteArray>& );
00096     void itemsAdded( const Akonadi::Item::List &list );
00097     void itemAdded( const Akonadi::Item &item );
00098     void itemMoved( const Akonadi::Item&, const Akonadi::Collection& src, const Akonadi::Collection& dst );
00099     void itemRemoved( const Akonadi::Item& );
00100     int rowForItem( const Akonadi::Item& );
00101     bool collectionIsCompatible() const;
00102 
00103     ItemModel *mParent;
00104 
00105     QList<ItemContainer*> items;
00106     QHash<Item, ItemContainer*> itemHash;
00107 
00108     Collection collection;
00109     Monitor *monitor;
00110     Session *session;
00111 };
00112 
00113 bool ItemModel::Private::collectionIsCompatible() const
00114 {
00115   // in the generic case, we show any collection
00116   if ( mParent->mimeTypes() == QStringList( QLatin1String("text/uri-list") ) )
00117     return true;
00118   // if the model's mime types are more specific, limit to those
00119   // collections that have matching types
00120   Q_FOREACH( QString type, mParent->mimeTypes() ) {
00121     if ( collection.contentMimeTypes().contains( type ) ) {
00122       return true;
00123     }
00124   }
00125   return false;
00126 }
00127 
00128 void ItemModel::Private::listingDone( KJob * job )
00129 {
00130   ItemFetchJob *fetch = static_cast<ItemFetchJob*>( job );
00131   Q_UNUSED( fetch );
00132   if ( job->error() ) {
00133     // TODO
00134     kWarning( 5250 ) << "Item query failed:" << job->errorString();
00135   }
00136 }
00137 
00138 void ItemModel::Private::collectionFetchResult( KJob * job )
00139 {
00140   CollectionFetchJob *fetch = static_cast<CollectionFetchJob*>( job );
00141   Q_ASSERT( fetch->collections().count() == 1 ); // we only listed base
00142   Collection c = fetch->collections().first();
00143    // avoid recursion, if this fails for some reason
00144   if ( !c.contentMimeTypes().isEmpty() ) {
00145     mParent->setCollection(c);
00146   } else {
00147     kWarning( 5250 ) << "Failed to retrieve the contents mime type of the collection: " << c;
00148     mParent->setCollection(Collection());
00149   }
00150 }
00151 
00152 int ItemModel::Private::rowForItem( const Akonadi::Item& item )
00153 {
00154   ItemContainer *container = itemHash.value( item );
00155   if ( !container )
00156     return -1;
00157 
00158   /* Try to find the item directly;
00159 
00160      If items have been removed, this first try won't succeed because
00161      the ItemContainer rows have not been updated (costs too much).
00162   */
00163   if ( container->row < items.count()
00164        && items.at( container->row ) == container )
00165     return container->row;
00166   else { // Slow solution if the fist one has not succeeded
00167     int row = -1;
00168     for ( int i = 0; i < items.size(); ++i ) {
00169       if ( items.at( i )->item == item ) {
00170         row = i;
00171         break;
00172       }
00173     }
00174     return row;
00175   }
00176 
00177 }
00178 
00179 void ItemModel::Private::itemChanged( const Akonadi::Item &item, const QSet<QByteArray>& )
00180 {
00181   int row = rowForItem( item );
00182   if ( row < 0 )
00183     return;
00184 
00185   items[ row ]->item = item;
00186   itemHash.remove( item );
00187   itemHash[ item ] = items[ row ];
00188 
00189   QModelIndex start = mParent->index( row, 0, QModelIndex() );
00190   QModelIndex end = mParent->index( row, mParent->columnCount( QModelIndex() ) - 1 , QModelIndex() );
00191 
00192   mParent->dataChanged( start, end );
00193 }
00194 
00195 void ItemModel::Private::itemMoved( const Akonadi::Item &item, const Akonadi::Collection& colSrc, const Akonadi::Collection& colDst )
00196 {
00197   if ( colSrc == collection && colDst != collection ) // item leaving this model
00198   {
00199     itemRemoved( item );
00200     return;
00201   }
00202 
00203 
00204   if ( colDst == collection && colSrc != collection )
00205   {
00206     itemAdded( item );
00207     return;
00208   }
00209 }
00210 
00211 void ItemModel::Private::itemsAdded( const Akonadi::Item::List &list )
00212 {
00213   if ( list.isEmpty() )
00214     return;
00215   mParent->beginInsertRows( QModelIndex(), items.count(), items.count() + list.count() - 1 );
00216   foreach( const Item &item, list ) {
00217     ItemContainer *c = new ItemContainer( item, items.count() );
00218     items.append( c );
00219     itemHash[ item ] = c;
00220   }
00221   mParent->endInsertRows();
00222 }
00223 
00224 void ItemModel::Private::itemAdded( const Akonadi::Item &item )
00225 {
00226   Item::List l;
00227   l << item;
00228   itemsAdded( l );
00229 }
00230 
00231 void ItemModel::Private::itemRemoved( const Akonadi::Item &_item )
00232 {
00233   int row = rowForItem( _item );
00234   if ( row < 0 )
00235     return;
00236 
00237   mParent->beginRemoveRows( QModelIndex(), row, row );
00238   const Item item = items.at( row )->item;
00239   Q_ASSERT( item.isValid() );
00240   itemHash.remove( item );
00241   delete items.takeAt( row );
00242   mParent->endRemoveRows();
00243 }
00244 
00245 ItemModel::ItemModel( QObject *parent ) :
00246     QAbstractTableModel( parent ),
00247     d( new Private( this ) )
00248 {
00249   setSupportedDragActions( Qt::MoveAction | Qt::CopyAction );
00250 }
00251 
00252 ItemModel::~ItemModel()
00253 {
00254   delete d;
00255 }
00256 
00257 QVariant ItemModel::data( const QModelIndex & index, int role ) const
00258 {
00259   if ( !index.isValid() )
00260     return QVariant();
00261   if ( index.row() >= d->items.count() )
00262     return QVariant();
00263   const Item item = d->items.at( index.row() )->item;
00264   if ( !item.isValid() )
00265     return QVariant();
00266 
00267   if ( role == Qt::DisplayRole ) {
00268     switch ( index.column() ) {
00269       case Id:
00270         return QString::number( item.id() );
00271       case RemoteId:
00272         return item.remoteId();
00273       case MimeType:
00274         return item.mimeType();
00275       default:
00276         return QVariant();
00277     }
00278   }
00279 
00280   if ( role == IdRole )
00281     return item.id();
00282 
00283   if ( role == ItemRole ) {
00284     QVariant var;
00285     var.setValue( item );
00286     return var;
00287   }
00288 
00289   if ( role == MimeTypeRole )
00290     return item.mimeType();
00291 
00292   return QVariant();
00293 }
00294 
00295 int ItemModel::rowCount( const QModelIndex & parent ) const
00296 {
00297   if ( !parent.isValid() )
00298     return d->items.count();
00299   return 0;
00300 }
00301 
00302 int ItemModel::columnCount(const QModelIndex & parent) const
00303 {
00304   if ( !parent.isValid() )
00305     return 3; // keep in sync with Column enum
00306   return 0;
00307 }
00308 
00309 QVariant ItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
00310 {
00311   if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
00312     switch ( section ) {
00313       case Id:
00314         return i18n( "Id" );
00315       case RemoteId:
00316         return i18n( "Remote Id" );
00317       case MimeType:
00318         return i18n( "MimeType" );
00319       default:
00320         return QString();
00321     }
00322   }
00323   return QAbstractTableModel::headerData( section, orientation, role );
00324 }
00325 
00326 void ItemModel::setCollection( const Collection &collection )
00327 {
00328   kDebug( 5250 );
00329   if ( d->collection == collection )
00330     return;
00331 
00332   // if we don't know anything about this collection yet, fetch it
00333   if ( collection.isValid() && collection.contentMimeTypes().isEmpty() )
00334   {
00335     CollectionFetchJob* job = new CollectionFetchJob( collection, CollectionFetchJob::Base, this );
00336     connect( job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*)) );
00337     return;
00338   }
00339 
00340   d->monitor->setCollectionMonitored( d->collection, false );
00341 
00342   d->collection = collection;
00343 
00344   d->monitor->setCollectionMonitored( d->collection, true );
00345 
00346   // the query changed, thus everything we have already is invalid
00347   qDeleteAll( d->items );
00348   d->items.clear();
00349   reset();
00350 
00351   // stop all running jobs
00352   d->session->clear();
00353 
00354   // start listing job
00355   if ( d->collectionIsCompatible() ) {
00356     ItemFetchJob* job = new ItemFetchJob( collection, session() );
00357     job->setFetchScope( d->monitor->itemFetchScope() );
00358     connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(itemsAdded(Akonadi::Item::List)) );
00359     connect( job, SIGNAL(result(KJob*)), SLOT(listingDone(KJob*)) );
00360   }
00361 
00362   emit collectionChanged( collection );
00363 }
00364 
00365 void ItemModel::setFetchScope( const ItemFetchScope &fetchScope )
00366 {
00367   d->monitor->setItemFetchScope( fetchScope );
00368 }
00369 
00370 ItemFetchScope &ItemModel::fetchScope()
00371 {
00372   return d->monitor->itemFetchScope();
00373 }
00374 
00375 Item ItemModel::itemForIndex( const QModelIndex & index ) const
00376 {
00377   if ( !index.isValid() )
00378     return Akonadi::Item();
00379 
00380   if ( index.row() >= d->items.count() )
00381     return Akonadi::Item();
00382 
00383   Item item = d->items.at( index.row() )->item;
00384   Q_ASSERT( item.isValid() );
00385 
00386   return item;
00387 }
00388 
00389 Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const
00390 {
00391   Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
00392 
00393   if (index.isValid())
00394     return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
00395   else
00396     return Qt::ItemIsDropEnabled | defaultFlags;
00397 }
00398 
00399 QStringList ItemModel::mimeTypes() const
00400 {
00401   return QStringList() << QLatin1String("text/uri-list");
00402 }
00403 
00404 Session * ItemModel::session() const
00405 {
00406   return d->session;
00407 }
00408 
00409 QMimeData *ItemModel::mimeData( const QModelIndexList &indexes ) const
00410 {
00411   QMimeData *data = new QMimeData();
00412   // Add item uri to the mimedata for dropping in external applications
00413   KUrl::List urls;
00414   foreach ( const QModelIndex &index, indexes ) {
00415     if ( index.column() != 0 )
00416       continue;
00417 
00418     urls << itemForIndex( index ).url( Item::UrlWithMimeType );
00419   }
00420   urls.populateMimeData( data );
00421 
00422   return data;
00423 }
00424 
00425 QModelIndex ItemModel::indexForItem( const Akonadi::Item &item, const int column ) const
00426 {
00427   return index( d->rowForItem( item ), column );
00428 }
00429 
00430 bool ItemModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
00431 {
00432   Q_UNUSED( row );
00433   Q_UNUSED( column );
00434   Q_UNUSED( parent );
00435   KJob* job = PasteHelper::paste( data, d->collection, action != Qt::MoveAction );
00436   // TODO: error handling
00437   return job;
00438 }
00439 
00440 Collection ItemModel::collection() const
00441 {
00442   return d->collection;
00443 }
00444 
00445 #include "itemmodel.moc"

akonadi

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal