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 : item( i ), row( r )
00053 {
00054 }
00055 Item item;
00056 int row;
00057 };
00058
00062 class ItemModel::Private
00063 {
00064 public:
00065 Private( ItemModel *parent )
00066 : mParent( parent ), monitor( new Monitor() )
00067 {
00068 session = new Session( QCoreApplication::instance()->applicationName().toUtf8()
00069 + QByteArray( "-ItemModel-" ) + QByteArray::number( qrand() ), mParent );
00070
00071 monitor->ignoreSession( session );
00072
00073 mParent->connect( monitor, SIGNAL( itemChanged( const Akonadi::Item&, const QSet<QByteArray>& ) ),
00074 mParent, SLOT( itemChanged( const Akonadi::Item&, const QSet<QByteArray>& ) ) );
00075 mParent->connect( monitor, SIGNAL( itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ),
00076 mParent, SLOT( itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ) );
00077 mParent->connect( monitor, SIGNAL( itemAdded( const Akonadi::Item&, const Akonadi::Collection& ) ),
00078 mParent, SLOT( itemAdded( const Akonadi::Item& ) ) );
00079 mParent->connect( monitor, SIGNAL( itemRemoved( const Akonadi::Item& ) ),
00080 mParent, SLOT( itemRemoved( const Akonadi::Item& ) ) );
00081 mParent->connect( monitor, SIGNAL( itemLinked( const Akonadi::Item&, const Akonadi::Collection& ) ),
00082 mParent, SLOT( itemAdded( const Akonadi::Item& ) ) );
00083 mParent->connect( monitor, SIGNAL( itemUnlinked( const Akonadi::Item&, const Akonadi::Collection& ) ),
00084 mParent, SLOT( itemRemoved( const Akonadi::Item& ) ) );
00085 }
00086
00087 ~Private()
00088 {
00089 delete monitor;
00090 }
00091
00092 void listingDone( KJob* );
00093 void collectionFetchResult( KJob* );
00094 void itemChanged( const Akonadi::Item&, const QSet<QByteArray>& );
00095 void itemsAdded( const Akonadi::Item::List &list );
00096 void itemAdded( const Akonadi::Item &item );
00097 void itemMoved( const Akonadi::Item&, const Akonadi::Collection& src, const Akonadi::Collection& dst );
00098 void itemRemoved( const Akonadi::Item& );
00099 int rowForItem( const Akonadi::Item& );
00100 bool collectionIsCompatible() const;
00101
00102 ItemModel *mParent;
00103
00104 QList<ItemContainer*> items;
00105 QHash<Item, ItemContainer*> itemHash;
00106
00107 Collection collection;
00108 Monitor *monitor;
00109 Session *session;
00110 };
00111
00112 bool ItemModel::Private::collectionIsCompatible() const
00113 {
00114
00115 if ( mParent->mimeTypes() == QStringList( QLatin1String( "text/uri-list" ) ) )
00116 return true;
00117
00118
00119 Q_FOREACH( const QString &type, mParent->mimeTypes() ) {
00120 if ( collection.contentMimeTypes().contains( type ) ) {
00121 return true;
00122 }
00123 }
00124 return false;
00125 }
00126
00127 void ItemModel::Private::listingDone( KJob * job )
00128 {
00129 ItemFetchJob *fetch = static_cast<ItemFetchJob*>( job );
00130 Q_UNUSED( fetch );
00131 if ( job->error() ) {
00132
00133 kWarning() << "Item query failed:" << job->errorString();
00134 }
00135 }
00136
00137 void ItemModel::Private::collectionFetchResult( KJob * job )
00138 {
00139 CollectionFetchJob *fetch = static_cast<CollectionFetchJob*>( job );
00140
00141 if ( fetch->collections().isEmpty() )
00142 return;
00143
00144 Q_ASSERT( fetch->collections().count() == 1 );
00145 Collection c = fetch->collections().first();
00146
00147 if ( !c.contentMimeTypes().isEmpty() ) {
00148 mParent->setCollection(c);
00149 } else {
00150 kWarning() << "Failed to retrieve the contents mime type of the collection: " << c;
00151 mParent->setCollection(Collection());
00152 }
00153 }
00154
00155 int ItemModel::Private::rowForItem( const Akonadi::Item& item )
00156 {
00157 ItemContainer *container = itemHash.value( item );
00158 if ( !container )
00159 return -1;
00160
00161
00162
00163
00164
00165
00166 if ( container->row < items.count()
00167 && items.at( container->row ) == container )
00168 return container->row;
00169 else {
00170 int row = -1;
00171 for ( int i = 0; i < items.size(); ++i ) {
00172 if ( items.at( i )->item == item ) {
00173 row = i;
00174 break;
00175 }
00176 }
00177 return row;
00178 }
00179
00180 }
00181
00182 void ItemModel::Private::itemChanged( const Akonadi::Item &item, const QSet<QByteArray>& )
00183 {
00184 int row = rowForItem( item );
00185 if ( row < 0 )
00186 return;
00187
00188 items[ row ]->item = item;
00189 itemHash.remove( item );
00190 itemHash[ item ] = items[ row ];
00191
00192 QModelIndex start = mParent->index( row, 0, QModelIndex() );
00193 QModelIndex end = mParent->index( row, mParent->columnCount( QModelIndex() ) - 1 , QModelIndex() );
00194
00195 mParent->dataChanged( start, end );
00196 }
00197
00198 void ItemModel::Private::itemMoved( const Akonadi::Item &item, const Akonadi::Collection& colSrc, const Akonadi::Collection& colDst )
00199 {
00200 if ( colSrc == collection && colDst != collection )
00201 {
00202 itemRemoved( item );
00203 return;
00204 }
00205
00206
00207 if ( colDst == collection && colSrc != collection )
00208 {
00209 itemAdded( item );
00210 return;
00211 }
00212 }
00213
00214 void ItemModel::Private::itemsAdded( const Akonadi::Item::List &list )
00215 {
00216 if ( list.isEmpty() )
00217 return;
00218 mParent->beginInsertRows( QModelIndex(), items.count(), items.count() + list.count() - 1 );
00219 foreach ( const Item &item, list ) {
00220 ItemContainer *c = new ItemContainer( item, items.count() );
00221 items.append( c );
00222 itemHash[ item ] = c;
00223 }
00224 mParent->endInsertRows();
00225 }
00226
00227 void ItemModel::Private::itemAdded( const Akonadi::Item &item )
00228 {
00229 Item::List l;
00230 l << item;
00231 itemsAdded( l );
00232 }
00233
00234 void ItemModel::Private::itemRemoved( const Akonadi::Item &_item )
00235 {
00236 int row = rowForItem( _item );
00237 if ( row < 0 )
00238 return;
00239
00240 mParent->beginRemoveRows( QModelIndex(), row, row );
00241 const Item item = items.at( row )->item;
00242 Q_ASSERT( item.isValid() );
00243 itemHash.remove( item );
00244 delete items.takeAt( row );
00245 mParent->endRemoveRows();
00246 }
00247
00248 ItemModel::ItemModel( QObject *parent ) :
00249 QAbstractTableModel( parent ),
00250 d( new Private( this ) )
00251 {
00252 }
00253
00254 ItemModel::~ItemModel()
00255 {
00256 delete d;
00257 }
00258
00259 QVariant ItemModel::data( const QModelIndex & index, int role ) const
00260 {
00261 if ( !index.isValid() )
00262 return QVariant();
00263 if ( index.row() >= d->items.count() )
00264 return QVariant();
00265 const Item item = d->items.at( index.row() )->item;
00266 if ( !item.isValid() )
00267 return QVariant();
00268
00269 if ( role == Qt::DisplayRole ) {
00270 switch ( index.column() ) {
00271 case Id:
00272 return QString::number( item.id() );
00273 case RemoteId:
00274 return item.remoteId();
00275 case MimeType:
00276 return item.mimeType();
00277 default:
00278 return QVariant();
00279 }
00280 }
00281
00282 if ( role == IdRole )
00283 return item.id();
00284
00285 if ( role == ItemRole ) {
00286 QVariant var;
00287 var.setValue( item );
00288 return var;
00289 }
00290
00291 if ( role == MimeTypeRole )
00292 return item.mimeType();
00293
00294 return QVariant();
00295 }
00296
00297 int ItemModel::rowCount( const QModelIndex & parent ) const
00298 {
00299 if ( !parent.isValid() )
00300 return d->items.count();
00301 return 0;
00302 }
00303
00304 int ItemModel::columnCount(const QModelIndex & parent) const
00305 {
00306 if ( !parent.isValid() )
00307 return 3;
00308 return 0;
00309 }
00310
00311 QVariant ItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
00312 {
00313 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
00314 switch ( section ) {
00315 case Id:
00316 return i18n( "Id" );
00317 case RemoteId:
00318 return i18n( "Remote Id" );
00319 case MimeType:
00320 return i18n( "MimeType" );
00321 default:
00322 return QString();
00323 }
00324 }
00325 return QAbstractTableModel::headerData( section, orientation, role );
00326 }
00327
00328 void ItemModel::setCollection( const Collection &collection )
00329 {
00330 kDebug();
00331 if ( d->collection == collection )
00332 return;
00333
00334
00335 if ( collection.isValid() && collection.contentMimeTypes().isEmpty() )
00336 {
00337 CollectionFetchJob* job = new CollectionFetchJob( collection, CollectionFetchJob::Base, this );
00338 connect( job, SIGNAL( result( KJob* ) ), this, SLOT( collectionFetchResult( KJob* ) ) );
00339 return;
00340 }
00341
00342 d->monitor->setCollectionMonitored( d->collection, false );
00343
00344 d->collection = collection;
00345
00346 d->monitor->setCollectionMonitored( d->collection, true );
00347
00348
00349 qDeleteAll( d->items );
00350 d->items.clear();
00351 reset();
00352
00353
00354 d->session->clear();
00355
00356
00357 if ( d->collectionIsCompatible() ) {
00358 ItemFetchJob* job = new ItemFetchJob( collection, session() );
00359 job->setFetchScope( d->monitor->itemFetchScope() );
00360 connect( job, SIGNAL( itemsReceived( const Akonadi::Item::List& ) ),
00361 SLOT( itemsAdded( const Akonadi::Item::List& ) ) );
00362 connect( job, SIGNAL( result( KJob* ) ), SLOT( listingDone( KJob* ) ) );
00363 }
00364
00365 emit collectionChanged( collection );
00366 }
00367
00368 void ItemModel::setFetchScope( const ItemFetchScope &fetchScope )
00369 {
00370 d->monitor->setItemFetchScope( fetchScope );
00371 }
00372
00373 ItemFetchScope &ItemModel::fetchScope()
00374 {
00375 return d->monitor->itemFetchScope();
00376 }
00377
00378 Item ItemModel::itemForIndex( const QModelIndex & index ) const
00379 {
00380 if ( !index.isValid() )
00381 return Akonadi::Item();
00382
00383 if ( index.row() >= d->items.count() )
00384 return Akonadi::Item();
00385
00386 Item item = d->items.at( index.row() )->item;
00387 Q_ASSERT( item.isValid() );
00388
00389 return item;
00390 }
00391
00392 Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const
00393 {
00394 Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
00395
00396 if (index.isValid())
00397 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
00398 else
00399 return Qt::ItemIsDropEnabled | defaultFlags;
00400 }
00401
00402 QStringList ItemModel::mimeTypes() const
00403 {
00404 return QStringList() << QLatin1String( "text/uri-list" );
00405 }
00406
00407 Session * ItemModel::session() const
00408 {
00409 return d->session;
00410 }
00411
00412 QMimeData *ItemModel::mimeData( const QModelIndexList &indexes ) const
00413 {
00414 QMimeData *data = new QMimeData();
00415
00416 KUrl::List urls;
00417 foreach ( const QModelIndex &index, indexes ) {
00418 if ( index.column() != 0 )
00419 continue;
00420
00421 urls << itemForIndex( index ).url( Item::UrlWithMimeType );
00422 }
00423 urls.populateMimeData( data );
00424
00425 return data;
00426 }
00427
00428 QModelIndex ItemModel::indexForItem( const Akonadi::Item &item, const int column ) const
00429 {
00430 return index( d->rowForItem( item ), column );
00431 }
00432
00433 bool ItemModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
00434 {
00435 Q_UNUSED( row );
00436 Q_UNUSED( column );
00437 Q_UNUSED( parent );
00438 KJob* job = PasteHelper::paste( data, d->collection, action != Qt::MoveAction );
00439
00440 return job;
00441 }
00442
00443 Collection ItemModel::collection() const
00444 {
00445 return d->collection;
00446 }
00447
00448 Qt::DropActions ItemModel::supportedDropActions() const
00449 {
00450 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
00451 }
00452
00453
00454 #include "itemmodel.moc"