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 : 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(Akonadi::Item,QSet<QByteArray>)), 00074 mParent, SLOT(itemChanged(Akonadi::Item,QSet<QByteArray>)) ); 00075 mParent->connect( monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), 00076 mParent, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)) ); 00077 mParent->connect( monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), 00078 mParent, SLOT(itemAdded(Akonadi::Item)) ); 00079 mParent->connect( monitor, SIGNAL(itemRemoved(Akonadi::Item)), 00080 mParent, SLOT(itemRemoved(Akonadi::Item)) ); 00081 mParent->connect( monitor, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)), 00082 mParent, SLOT(itemAdded(Akonadi::Item)) ); 00083 mParent->connect( monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)), 00084 mParent, SLOT(itemRemoved(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 // in the generic case, we show any collection 00115 if ( mParent->mimeTypes() == QStringList( QLatin1String( "text/uri-list" ) ) ) 00116 return true; 00117 // if the model's mime types are more specific, limit to those 00118 // collections that have matching types 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 // TODO 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 ); // we only listed base 00145 Collection c = fetch->collections().first(); 00146 // avoid recursion, if this fails for some reason 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 /* Try to find the item directly; 00162 00163 If items have been removed, this first try won't succeed because 00164 the ItemContainer rows have not been updated (costs too much). 00165 */ 00166 if ( container->row < items.count() 00167 && items.at( container->row ) == container ) 00168 return container->row; 00169 else { // Slow solution if the fist one has not succeeded 00170 int row = -1; 00171 const int numberOfItems( items.size() ); 00172 for ( int i = 0; i < numberOfItems; ++i ) { 00173 if ( items.at( i )->item == item ) { 00174 row = i; 00175 break; 00176 } 00177 } 00178 return row; 00179 } 00180 00181 } 00182 00183 void ItemModel::Private::itemChanged( const Akonadi::Item &item, const QSet<QByteArray>& ) 00184 { 00185 int row = rowForItem( item ); 00186 if ( row < 0 ) 00187 return; 00188 00189 items[ row ]->item = item; 00190 itemHash.remove( item ); 00191 itemHash[ item ] = items[ row ]; 00192 00193 QModelIndex start = mParent->index( row, 0, QModelIndex() ); 00194 QModelIndex end = mParent->index( row, mParent->columnCount( QModelIndex() ) - 1 , QModelIndex() ); 00195 00196 mParent->dataChanged( start, end ); 00197 } 00198 00199 void ItemModel::Private::itemMoved( const Akonadi::Item &item, const Akonadi::Collection& colSrc, const Akonadi::Collection& colDst ) 00200 { 00201 if ( colSrc == collection && colDst != collection ) // item leaving this model 00202 { 00203 itemRemoved( item ); 00204 return; 00205 } 00206 00207 00208 if ( colDst == collection && colSrc != collection ) 00209 { 00210 itemAdded( item ); 00211 return; 00212 } 00213 } 00214 00215 void ItemModel::Private::itemsAdded( const Akonadi::Item::List &list ) 00216 { 00217 if ( list.isEmpty() ) 00218 return; 00219 mParent->beginInsertRows( QModelIndex(), items.count(), items.count() + list.count() - 1 ); 00220 foreach ( const Item &item, list ) { 00221 ItemContainer *c = new ItemContainer( item, items.count() ); 00222 items.append( c ); 00223 itemHash[ item ] = c; 00224 } 00225 mParent->endInsertRows(); 00226 } 00227 00228 void ItemModel::Private::itemAdded( const Akonadi::Item &item ) 00229 { 00230 Item::List l; 00231 l << item; 00232 itemsAdded( l ); 00233 } 00234 00235 void ItemModel::Private::itemRemoved( const Akonadi::Item &_item ) 00236 { 00237 int row = rowForItem( _item ); 00238 if ( row < 0 ) 00239 return; 00240 00241 mParent->beginRemoveRows( QModelIndex(), row, row ); 00242 const Item item = items.at( row )->item; 00243 Q_ASSERT( item.isValid() ); 00244 itemHash.remove( item ); 00245 delete items.takeAt( row ); 00246 mParent->endRemoveRows(); 00247 } 00248 00249 ItemModel::ItemModel( QObject *parent ) : 00250 QAbstractTableModel( parent ), 00251 d( new Private( this ) ) 00252 { 00253 } 00254 00255 ItemModel::~ItemModel() 00256 { 00257 delete d; 00258 } 00259 00260 QVariant ItemModel::data( const QModelIndex & index, int role ) const 00261 { 00262 if ( !index.isValid() ) 00263 return QVariant(); 00264 if ( index.row() >= d->items.count() ) 00265 return QVariant(); 00266 const Item item = d->items.at( index.row() )->item; 00267 if ( !item.isValid() ) 00268 return QVariant(); 00269 00270 if ( role == Qt::DisplayRole ) { 00271 switch ( index.column() ) { 00272 case Id: 00273 return QString::number( item.id() ); 00274 case RemoteId: 00275 return item.remoteId(); 00276 case MimeType: 00277 return item.mimeType(); 00278 default: 00279 return QVariant(); 00280 } 00281 } 00282 00283 if ( role == IdRole ) 00284 return item.id(); 00285 00286 if ( role == ItemRole ) { 00287 QVariant var; 00288 var.setValue( item ); 00289 return var; 00290 } 00291 00292 if ( role == MimeTypeRole ) 00293 return item.mimeType(); 00294 00295 return QVariant(); 00296 } 00297 00298 int ItemModel::rowCount( const QModelIndex & parent ) const 00299 { 00300 if ( !parent.isValid() ) 00301 return d->items.count(); 00302 return 0; 00303 } 00304 00305 int ItemModel::columnCount(const QModelIndex & parent) const 00306 { 00307 if ( !parent.isValid() ) 00308 return 3; // keep in sync with Column enum 00309 return 0; 00310 } 00311 00312 QVariant ItemModel::headerData( int section, Qt::Orientation orientation, int role ) const 00313 { 00314 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { 00315 switch ( section ) { 00316 case Id: 00317 return i18n( "Id" ); 00318 case RemoteId: 00319 return i18n( "Remote Id" ); 00320 case MimeType: 00321 return i18n( "MimeType" ); 00322 default: 00323 return QString(); 00324 } 00325 } 00326 return QAbstractTableModel::headerData( section, orientation, role ); 00327 } 00328 00329 void ItemModel::setCollection( const Collection &collection ) 00330 { 00331 kDebug(); 00332 if ( d->collection == collection ) 00333 return; 00334 00335 // if we don't know anything about this collection yet, fetch it 00336 if ( collection.isValid() && collection.contentMimeTypes().isEmpty() ) 00337 { 00338 CollectionFetchJob* job = new CollectionFetchJob( collection, CollectionFetchJob::Base, this ); 00339 connect( job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*)) ); 00340 return; 00341 } 00342 00343 d->monitor->setCollectionMonitored( d->collection, false ); 00344 00345 d->collection = collection; 00346 00347 d->monitor->setCollectionMonitored( d->collection, true ); 00348 00349 // the query changed, thus everything we have already is invalid 00350 qDeleteAll( d->items ); 00351 d->items.clear(); 00352 reset(); 00353 00354 // stop all running jobs 00355 d->session->clear(); 00356 00357 // start listing job 00358 if ( d->collectionIsCompatible() ) { 00359 ItemFetchJob* job = new ItemFetchJob( collection, session() ); 00360 job->setFetchScope( d->monitor->itemFetchScope() ); 00361 connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), 00362 SLOT(itemsAdded(Akonadi::Item::List)) ); 00363 connect( job, SIGNAL(result(KJob*)), SLOT(listingDone(KJob*)) ); 00364 } 00365 00366 emit collectionChanged( collection ); 00367 } 00368 00369 void ItemModel::setFetchScope( const ItemFetchScope &fetchScope ) 00370 { 00371 d->monitor->setItemFetchScope( fetchScope ); 00372 } 00373 00374 ItemFetchScope &ItemModel::fetchScope() 00375 { 00376 return d->monitor->itemFetchScope(); 00377 } 00378 00379 Item ItemModel::itemForIndex( const QModelIndex & index ) const 00380 { 00381 if ( !index.isValid() ) 00382 return Akonadi::Item(); 00383 00384 if ( index.row() >= d->items.count() ) 00385 return Akonadi::Item(); 00386 00387 Item item = d->items.at( index.row() )->item; 00388 if ( item.isValid() ) { 00389 return item; 00390 } else { 00391 return Akonadi::Item(); 00392 } 00393 } 00394 00395 Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const 00396 { 00397 Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); 00398 00399 if (index.isValid()) 00400 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; 00401 else 00402 return Qt::ItemIsDropEnabled | defaultFlags; 00403 } 00404 00405 QStringList ItemModel::mimeTypes() const 00406 { 00407 return QStringList() << QLatin1String( "text/uri-list" ); 00408 } 00409 00410 Session * ItemModel::session() const 00411 { 00412 return d->session; 00413 } 00414 00415 QMimeData *ItemModel::mimeData( const QModelIndexList &indexes ) const 00416 { 00417 QMimeData *data = new QMimeData(); 00418 // Add item uri to the mimedata for dropping in external applications 00419 KUrl::List urls; 00420 foreach ( const QModelIndex &index, indexes ) { 00421 if ( index.column() != 0 ) 00422 continue; 00423 00424 urls << itemForIndex( index ).url( Item::UrlWithMimeType ); 00425 } 00426 urls.populateMimeData( data ); 00427 00428 return data; 00429 } 00430 00431 QModelIndex ItemModel::indexForItem( const Akonadi::Item &item, const int column ) const 00432 { 00433 return index( d->rowForItem( item ), column ); 00434 } 00435 00436 bool ItemModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) 00437 { 00438 Q_UNUSED( row ); 00439 Q_UNUSED( column ); 00440 Q_UNUSED( parent ); 00441 KJob* job = PasteHelper::paste( data, d->collection, action != Qt::MoveAction ); 00442 // TODO: error handling 00443 return job; 00444 } 00445 00446 Collection ItemModel::collection() const 00447 { 00448 return d->collection; 00449 } 00450 00451 Qt::DropActions ItemModel::supportedDropActions() const 00452 { 00453 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; 00454 } 00455 00456 00457 #include "itemmodel.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:44 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:44 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.