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