00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00116 if ( mParent->mimeTypes() == QStringList( QLatin1String("text/uri-list") ) )
00117 return true;
00118
00119
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
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 );
00142 Collection c = fetch->collections().first();
00143
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
00159
00160
00161
00162
00163 if ( container->row < items.count()
00164 && items.at( container->row ) == container )
00165 return container->row;
00166 else {
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 )
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;
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
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
00347 qDeleteAll( d->items );
00348 d->items.clear();
00349 reset();
00350
00351
00352 d->session->clear();
00353
00354
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
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
00437 return job;
00438 }
00439
00440 Collection ItemModel::collection() const
00441 {
00442 return d->collection;
00443 }
00444
00445 #include "itemmodel.moc"