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

akonadi

entitytreemodel.cpp

00001 /*
00002     Copyright (c) 2008 Stephen Kelly <steveire@gmail.com>
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 "entitytreemodel.h"
00021 #include "entitytreemodel_p.h"
00022 
00023 #include "monitor_p.h"
00024 
00025 #include <QtCore/QHash>
00026 #include <QtCore/QMimeData>
00027 #include <QtCore/QTimer>
00028 #include <QtGui/QApplication>
00029 #include <QtGui/QPalette>
00030 
00031 #include <KDE/KIcon>
00032 #include <KDE/KLocale>
00033 #include <KDE/KUrl>
00034 
00035 #include <akonadi/attributefactory.h>
00036 #include <akonadi/changerecorder.h>
00037 #include <akonadi/collectionmodifyjob.h>
00038 #include <akonadi/entitydisplayattribute.h>
00039 #include <akonadi/transactionsequence.h>
00040 #include <akonadi/itemmodifyjob.h>
00041 #include <akonadi/session.h>
00042 #include "collectionfetchscope.h"
00043 
00044 #include "collectionutils_p.h"
00045 
00046 #include "kdebug.h"
00047 #include "pastehelper_p.h"
00048 
00049 // TODO:
00050 // * Implement ordering support.
00051 
00052 Q_DECLARE_METATYPE( QSet<QByteArray> )
00053 
00054 using namespace Akonadi;
00055 
00056 EntityTreeModel::EntityTreeModel( ChangeRecorder *monitor,
00057                                   QObject *parent
00058                                 )
00059   : QAbstractItemModel( parent ),
00060     d_ptr( new EntityTreeModelPrivate( this ) )
00061 {
00062   Q_D( EntityTreeModel );
00063   d->init( monitor );
00064 }
00065 
00066 EntityTreeModel::EntityTreeModel( ChangeRecorder *monitor,
00067                                   EntityTreeModelPrivate *d,
00068                                   QObject *parent )
00069   : QAbstractItemModel( parent ),
00070     d_ptr( d )
00071 {
00072   d->init(monitor );
00073 }
00074 
00075 EntityTreeModel::~EntityTreeModel()
00076 {
00077   Q_D( EntityTreeModel );
00078 
00079   foreach( const QList<Node*> &list, d->m_childEntities ) {
00080     qDeleteAll( list );
00081   }
00082 
00083   delete d_ptr;
00084 }
00085 
00086 bool EntityTreeModel::systemEntitiesShown() const
00087 {
00088   Q_D( const EntityTreeModel );
00089   return d->m_showSystemEntities;
00090 }
00091 
00092 void EntityTreeModel::setShowSystemEntities( bool show )
00093 {
00094   Q_D( EntityTreeModel );
00095   d->m_showSystemEntities = show;
00096 }
00097 
00098 void EntityTreeModel::clearAndReset()
00099 {
00100   Q_D( EntityTreeModel );
00101   d->beginResetModel();
00102   d->endResetModel();
00103 }
00104 
00105 int EntityTreeModel::columnCount( const QModelIndex & parent ) const
00106 {
00107 // TODO: Statistics?
00108   if ( parent.isValid() && parent.column() != 0 )
00109     return 0;
00110 
00111   return qMax( entityColumnCount( CollectionTreeHeaders ), entityColumnCount( ItemListHeaders ) );
00112 }
00113 
00114 
00115 QVariant EntityTreeModel::entityData( const Item &item, int column, int role ) const
00116 {
00117   if ( column == 0 ) {
00118     switch ( role ) {
00119       case Qt::DisplayRole:
00120       case Qt::EditRole:
00121         if ( item.hasAttribute<EntityDisplayAttribute>() &&
00122              !item.attribute<EntityDisplayAttribute>()->displayName().isEmpty() ) {
00123           return item.attribute<EntityDisplayAttribute>()->displayName();
00124         } else {
00125           return item.remoteId();
00126         }
00127         break;
00128       case Qt::DecorationRole:
00129         if ( item.hasAttribute<EntityDisplayAttribute>() &&
00130              !item.attribute<EntityDisplayAttribute>()->iconName().isEmpty() )
00131           return item.attribute<EntityDisplayAttribute>()->icon();
00132         break;
00133       default:
00134         break;
00135     }
00136   }
00137 
00138   return QVariant();
00139 }
00140 
00141 QVariant EntityTreeModel::entityData( const Collection &collection, int column, int role ) const
00142 {
00143   Q_D( const EntityTreeModel );
00144 
00145   if ( column > 0 )
00146     return QString();
00147 
00148   if ( collection == Collection::root() ) {
00149     // Only display the root collection. It may not be edited.
00150     if ( role == Qt::DisplayRole )
00151       return d->m_rootCollectionDisplayName;
00152 
00153     if ( role == Qt::EditRole )
00154       return QVariant();
00155   }
00156 
00157   switch ( role ) {
00158     case Qt::DisplayRole:
00159     case Qt::EditRole:
00160       if ( column == 0 ) {
00161         if ( collection.hasAttribute<EntityDisplayAttribute>() &&
00162              !collection.attribute<EntityDisplayAttribute>()->displayName().isEmpty() ) {
00163           return collection.attribute<EntityDisplayAttribute>()->displayName();
00164         }
00165         return collection.name();
00166       }
00167       break;
00168     case Qt::DecorationRole:
00169       if ( collection.hasAttribute<EntityDisplayAttribute>() &&
00170            !collection.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
00171         return collection.attribute<EntityDisplayAttribute>()->icon();
00172       }
00173       return KIcon( CollectionUtils::defaultIconName( collection ) );
00174     default:
00175       break;
00176   }
00177 
00178   return QVariant();
00179 }
00180 
00181 QVariant EntityTreeModel::data( const QModelIndex & index, int role ) const
00182 {
00183   Q_D( const EntityTreeModel );
00184   if ( role == SessionRole )
00185     return QVariant::fromValue( qobject_cast<QObject *>( d->m_session ) );
00186 
00187   // Ugly, but at least the API is clean.
00188   const HeaderGroup headerGroup = static_cast<HeaderGroup>( ( role / static_cast<int>( TerminalUserRole ) ) );
00189 
00190   role %= TerminalUserRole;
00191   if ( !index.isValid() ) {
00192     if ( ColumnCountRole != role )
00193       return QVariant();
00194 
00195     return entityColumnCount( headerGroup );
00196   }
00197 
00198   if ( ColumnCountRole == role )
00199     return entityColumnCount( headerGroup );
00200 
00201   const Node *node = reinterpret_cast<Node *>( index.internalPointer() );
00202 
00203   if ( ParentCollectionRole == role ) {
00204     const Collection parentCollection = d->m_collections.value( node->parent );
00205     Q_ASSERT( parentCollection.isValid() );
00206 
00207     return QVariant::fromValue( parentCollection );
00208   }
00209 
00210   if ( Node::Collection == node->type ) {
00211 
00212     const Collection collection = d->m_collections.value( node->id );
00213 
00214     if ( !collection.isValid() )
00215       return QVariant();
00216 
00217     switch ( role ) {
00218       case MimeTypeRole:
00219         return collection.mimeType();
00220         break;
00221       case RemoteIdRole:
00222         return collection.remoteId();
00223         break;
00224       case CollectionIdRole:
00225         return collection.id();
00226         break;
00227       case ItemIdRole:
00228         // QVariant().toInt() is 0, not -1, so we have to handle the ItemIdRole
00229         // and CollectionIdRole (below) specially
00230         return -1;
00231         break;
00232       case CollectionRole:
00233         return QVariant::fromValue( collection );
00234         break;
00235       case EntityUrlRole:
00236         return collection.url().url();
00237         break;
00238       case Qt::BackgroundRole:
00239       {
00240         if ( collection.hasAttribute<EntityDisplayAttribute>() )
00241         {
00242           EntityDisplayAttribute *eda = collection.attribute<EntityDisplayAttribute>();
00243           QColor color = eda->backgroundColor();
00244           if ( color.isValid() )
00245             return color;
00246         }
00247         // fall through.
00248       }
00249       default:
00250         return entityData( collection, index.column(), role );
00251         break;
00252     }
00253 
00254   } else if ( Node::Item == node->type ) {
00255     const Item item = d->m_items.value( node->id );
00256     if ( !item.isValid() )
00257       return QVariant();
00258 
00259     switch ( role ) {
00260       case MimeTypeRole:
00261         return item.mimeType();
00262         break;
00263       case RemoteIdRole:
00264         return item.remoteId();
00265         break;
00266       case ItemRole:
00267         return QVariant::fromValue( item );
00268         break;
00269       case ItemIdRole:
00270         return item.id();
00271         break;
00272       case CollectionIdRole:
00273         return -1;
00274         break;
00275       case LoadedPartsRole:
00276         return QVariant::fromValue( item.loadedPayloadParts() );
00277         break;
00278       case AvailablePartsRole:
00279         return QVariant::fromValue( item.availablePayloadParts() );
00280         break;
00281       case EntityUrlRole:
00282         return item.url( Akonadi::Item::UrlWithMimeType ).url();
00283         break;
00284       case Qt::BackgroundRole:
00285       {
00286         if ( item.hasAttribute<EntityDisplayAttribute>() )
00287         {
00288           EntityDisplayAttribute *eda = item.attribute<EntityDisplayAttribute>();
00289           QColor color = eda->backgroundColor();
00290           if ( color.isValid() )
00291             return color;
00292         }
00293         // fall through.
00294       }
00295       default:
00296         return entityData( item, index.column(), role );
00297         break;
00298     }
00299   }
00300 
00301   return QVariant();
00302 }
00303 
00304 
00305 Qt::ItemFlags EntityTreeModel::flags( const QModelIndex & index ) const
00306 {
00307   Q_D( const EntityTreeModel );
00308   // Pass modeltest.
00309   // http://labs.trolltech.com/forums/topic/79
00310   if ( !index.isValid() )
00311     return 0;
00312 
00313   Qt::ItemFlags flags = QAbstractItemModel::flags( index );
00314 
00315   const Node *node = reinterpret_cast<Node *>( index.internalPointer() );
00316 
00317   if ( Node::Collection == node->type ) {
00318     // cut out entities will be shown as inactive
00319     if ( d->m_pendingCutCollections.contains( node->id ) )
00320       return Qt::ItemIsSelectable;
00321 
00322     const Collection collection = d->m_collections.value( node->id );
00323     if ( collection.isValid() ) {
00324 
00325       if ( collection == Collection::root() ) {
00326         // Selectable and displayable only.
00327         return flags;
00328       }
00329 
00330       const int rights = collection.rights();
00331 
00332       if ( rights & Collection::CanChangeCollection ) {
00333         if ( index.column() == 0 )
00334           flags |= Qt::ItemIsEditable;
00335         // Changing the collection includes changing the metadata (child entityordering).
00336         // Need to allow this by drag and drop.
00337         flags |= Qt::ItemIsDropEnabled;
00338       }
00339       if ( rights & ( Collection::CanCreateCollection | Collection::CanCreateItem | Collection::CanLinkItem ) ) {
00340         // Can we drop new collections and items into this collection?
00341         flags |= Qt::ItemIsDropEnabled;
00342       }
00343 
00344       // dragging is always possible, even for read-only objects, but they can only be copied, not moved.
00345       flags |= Qt::ItemIsDragEnabled;
00346 
00347     }
00348   } else if ( Node::Item == node->type ) {
00349     if ( d->m_pendingCutItems.contains( node->id ) )
00350       return Qt::ItemIsSelectable;
00351 
00352     // Rights come from the parent collection.
00353 
00354     Collection parentCollection;
00355     if ( !index.parent().isValid() )
00356     {
00357       parentCollection = d->m_rootCollection;
00358     }
00359     else
00360     {
00361       const Node *parentNode = reinterpret_cast<Node *>( index.parent().internalPointer() );
00362 
00363       parentCollection = d->m_collections.value( parentNode->id );
00364     }
00365     if ( parentCollection.isValid() ) {
00366       const int rights = parentCollection.rights();
00367 
00368       // Can't drop onto items.
00369       if ( rights & Collection::CanChangeItem && index.column() == 0 ) {
00370         flags = flags | Qt::ItemIsEditable;
00371       }
00372       // dragging is always possible, even for read-only objects, but they can only be copied, not moved.
00373       flags |= Qt::ItemIsDragEnabled;
00374     }
00375   }
00376 
00377   return flags;
00378 }
00379 
00380 Qt::DropActions EntityTreeModel::supportedDropActions() const
00381 {
00382   return (Qt::CopyAction | Qt::MoveAction | Qt::LinkAction);
00383 }
00384 
00385 QStringList EntityTreeModel::mimeTypes() const
00386 {
00387   // TODO: Should this return the mimetypes that the items provide? Allow dragging a contact from here for example.
00388   return QStringList() << QLatin1String( "text/uri-list" );
00389 }
00390 
00391 bool EntityTreeModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
00392 {
00393   Q_UNUSED( row );
00394   Q_UNUSED( column );
00395   Q_D( EntityTreeModel );
00396 
00397   // Can't drop onto Collection::root.
00398   if ( !parent.isValid() )
00399     return false;
00400 
00401   // TODO Use action and collection rights and return false if necessary
00402 
00403   // if row and column are -1, then the drop was on parent directly.
00404   // data should then be appended on the end of the items of the collections as appropriate.
00405   // That will mean begin insert rows etc.
00406   // Otherwise it was a sibling of the row^th item of parent.
00407   // Needs to be handled when ordering is accounted for.
00408 
00409   // Handle dropping between items as well as on items.
00410 //   if ( row != -1 && column != -1 )
00411 //   {
00412 //   }
00413 
00414 
00415   if ( action == Qt::IgnoreAction )
00416     return true;
00417 
00418 // Shouldn't do this. Need to be able to drop vcards for example.
00419 //   if ( !data->hasFormat( "text/uri-list" ) )
00420 //       return false;
00421 
00422   Node *node = reinterpret_cast<Node *>( parent.internalId() );
00423 
00424   Q_ASSERT( node );
00425 
00426   if ( Node::Item == node->type ) {
00427     if ( !parent.parent().isValid() ) {
00428       // The drop is somehow on an item with no parent (shouldn't happen)
00429       // The drop should be considered handled anyway.
00430       kWarning() << "Dropped onto item with no parent collection";
00431       return true;
00432     }
00433 
00434     // A drop onto an item should be considered as a drop onto its parent collection
00435     node = reinterpret_cast<Node *>( parent.parent().internalId() );
00436   }
00437 
00438   if ( Node::Collection == node->type ) {
00439     const Collection destCollection = d->m_collections.value( node->id );
00440 
00441     // Applications can't create new collections in root. Only resources can.
00442     if ( destCollection == Collection::root() )
00443       // Accept the event so that it doesn't propagate.
00444       return true;
00445 
00446     if ( data->hasFormat( QLatin1String( "text/uri-list" ) ) ) {
00447 
00448       MimeTypeChecker mimeChecker;
00449       mimeChecker.setWantedMimeTypes( destCollection.contentMimeTypes() );
00450 
00451       const KUrl::List urls = KUrl::List::fromMimeData( data );
00452       foreach ( const KUrl &url, urls ) {
00453         const Collection collection = d->m_collections.value( Collection::fromUrl( url ).id() );
00454         if ( collection.isValid() ) {
00455           if ( collection.parentCollection().id() == destCollection.id() ) {
00456             kDebug() << "Error: source and destination of move are the same.";
00457             return false;
00458           }
00459 
00460           if ( !mimeChecker.isWantedCollection( collection ) ) {
00461             kDebug() << "unwanted collection" << mimeChecker.wantedMimeTypes() << collection.contentMimeTypes();
00462             return false;
00463           }
00464         } else {
00465           const Item item = d->m_items.value( Item::fromUrl( url ).id() );
00466           if ( item.isValid() ) {
00467             if ( item.parentCollection().id() == destCollection.id() ) {
00468               kDebug() << "Error: source and destination of move are the same.";
00469               return false;
00470             }
00471 
00472             if ( !mimeChecker.isWantedItem( item ) ) {
00473               kDebug() << "unwanted item" << mimeChecker.wantedMimeTypes() << item.mimeType();
00474               return false;
00475             }
00476           }
00477         }
00478       }
00479 
00480       KJob *job = PasteHelper::pasteUriList( data, destCollection, action, d->m_session );
00481       if ( !job )
00482         return false;
00483 
00484       connect( job, SIGNAL( result( KJob* ) ), SLOT( pasteJobDone( KJob* ) ) );
00485 
00486       // Accpet the event so that it doesn't propagate.
00487       return true;
00488     } else {
00489 //       not a set of uris. Maybe vcards etc. Check if the parent supports them, and maybe do
00490       // fromMimeData for them. Hmm, put it in the same transaction with the above?
00491       // TODO: This should be handled first, not last.
00492     }
00493   }
00494 
00495   return false;
00496 }
00497 
00498 QModelIndex EntityTreeModel::index( int row, int column, const QModelIndex & parent ) const
00499 {
00500 
00501   Q_D( const EntityTreeModel );
00502 
00503   if ( parent.column() > 0 )
00504     return QModelIndex();
00505 
00506   //TODO: don't use column count here? Use some d-> func.
00507   if ( column >= columnCount() || column < 0 )
00508     return QModelIndex();
00509 
00510   QList<Node*> childEntities;
00511 
00512   const Node *parentNode = reinterpret_cast<Node*>( parent.internalPointer() );
00513 
00514   if ( !parentNode || !parent.isValid() ) {
00515     if ( d->m_showRootCollection )
00516       childEntities << d->m_childEntities.value( -1 );
00517     else
00518       childEntities = d->m_childEntities.value( d->m_rootCollection.id() );
00519   } else {
00520     if ( parentNode->id >= 0 )
00521       childEntities = d->m_childEntities.value( parentNode->id );
00522   }
00523 
00524   const int size = childEntities.size();
00525   if ( row < 0 || row >= size )
00526     return QModelIndex();
00527 
00528   Node *node = childEntities.at( row );
00529 
00530   return createIndex( row, column, reinterpret_cast<void*>( node ) );
00531 }
00532 
00533 QModelIndex EntityTreeModel::parent( const QModelIndex & index ) const
00534 {
00535   Q_D( const EntityTreeModel );
00536 
00537   if ( !index.isValid() )
00538     return QModelIndex();
00539 
00540   const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
00541 
00542   if ( !node )
00543     return QModelIndex();
00544 
00545   const Collection collection = d->m_collections.value( node->parent );
00546 
00547   if ( !collection.isValid() )
00548     return QModelIndex();
00549 
00550   if ( collection.id() == d->m_rootCollection.id() ) {
00551     if ( !d->m_showRootCollection )
00552       return QModelIndex();
00553     else
00554       return createIndex( 0, 0, reinterpret_cast<void *>( d->m_rootNode ) );
00555   }
00556 
00557   const int row = d->indexOf( d->m_childEntities.value( collection.parentCollection().id() ), collection.id() );
00558 
00559   Q_ASSERT( row >= 0 );
00560   Node *parentNode = d->m_childEntities.value( collection.parentCollection().id() ).at( row );
00561 
00562   return createIndex( row, 0, reinterpret_cast<void*>( parentNode ) );
00563 }
00564 
00565 int EntityTreeModel::rowCount( const QModelIndex & parent ) const
00566 {
00567   Q_D( const EntityTreeModel );
00568 
00569   const Node *node = reinterpret_cast<Node*>( parent.internalPointer() );
00570 
00571   qint64 id;
00572   if ( !parent.isValid() ) {
00573     // If we're showing the root collection then it will be the only child of the root.
00574     if ( d->m_showRootCollection )
00575       return d->m_childEntities.value( -1 ).size();
00576 
00577     id = d->m_rootCollection.id();
00578   } else {
00579 
00580     if ( !node )
00581       return 0;
00582 
00583     if ( Node::Item == node->type )
00584       return 0;
00585 
00586     id = node->id;
00587   }
00588 
00589   if ( parent.column() <= 0 )
00590     return d->m_childEntities.value( id ).size();
00591 
00592   return 0;
00593 }
00594 
00595 int EntityTreeModel::entityColumnCount( HeaderGroup headerGroup ) const
00596 {
00597   // Not needed in this model.
00598   Q_UNUSED( headerGroup );
00599 
00600   return 1;
00601 }
00602 
00603 QVariant EntityTreeModel::entityHeaderData( int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup ) const
00604 {
00605   Q_D( const EntityTreeModel );
00606   // Not needed in this model.
00607   Q_UNUSED( headerGroup );
00608 
00609   if ( section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole )
00610   {
00611     if ( d->m_rootCollection == Collection::root() )
00612       return i18nc( "@title:column Name of a thing", "Name" );
00613     return d->m_rootCollection.name();
00614   }
00615 
00616   return QAbstractItemModel::headerData( section, orientation, role );
00617 }
00618 
00619 QVariant EntityTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
00620 {
00621   const HeaderGroup headerGroup = static_cast<HeaderGroup>( (role / static_cast<int>( TerminalUserRole ) ) );
00622 
00623   role %= TerminalUserRole;
00624   return entityHeaderData( section, orientation, role, headerGroup );
00625 }
00626 
00627 QMimeData *EntityTreeModel::mimeData( const QModelIndexList &indexes ) const
00628 {
00629   Q_D( const EntityTreeModel );
00630 
00631   QMimeData *data = new QMimeData();
00632   KUrl::List urls;
00633   foreach( const QModelIndex &index, indexes ) {
00634     if ( index.column() != 0 )
00635       continue;
00636 
00637     if ( !index.isValid() )
00638       continue;
00639 
00640     const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
00641 
00642     if ( Node::Collection == node->type )
00643       urls << d->m_collections.value( node->id ).url();
00644     else if ( Node::Item == node->type )
00645       urls << d->m_items.value( node->id ).url( Item::UrlWithMimeType );
00646     else // if that happens something went horrible wrong
00647       Q_ASSERT( false );
00648   }
00649 
00650   urls.populateMimeData( data );
00651 
00652   return data;
00653 }
00654 
00655 // Always return false for actions which take place asyncronously, eg via a Job.
00656 bool EntityTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
00657 {
00658   Q_D( EntityTreeModel );
00659 
00660   const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
00661 
00662   if ( role == PendingCutRole ) {
00663     if ( index.isValid() && value.toBool() ) {
00664       if ( Node::Collection == node->type )
00665         d->m_pendingCutCollections.append( node->id );
00666 
00667       if ( Node::Item == node->type )
00668         d->m_pendingCutItems.append( node->id );
00669     } else {
00670       d->m_pendingCutCollections.clear();
00671       d->m_pendingCutItems.clear();
00672     }
00673     return true;
00674   }
00675 
00676   if ( index.isValid() && node->type == Node::Collection && (role == CollectionRefRole || role == CollectionDerefRole) ) {
00677     const Collection collection = index.data( CollectionRole ).value<Collection>();
00678     Q_ASSERT( collection.isValid() );
00679 
00680     if ( role == CollectionDerefRole )
00681       d->deref( collection.id() );
00682     else if ( role == CollectionRefRole )
00683       d->ref( collection.id() );
00684   }
00685 
00686   if ( index.column() == 0 && ( role & ( Qt::EditRole | ItemRole | CollectionRole ) ) ) {
00687     if ( Node::Collection == node->type ) {
00688 
00689       Collection collection = d->m_collections.value( node->id );
00690 
00691       if ( !collection.isValid() || !value.isValid() )
00692         return false;
00693 
00694       if ( Qt::EditRole == role ) {
00695         collection.setName( value.toString() );
00696 
00697         if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
00698           EntityDisplayAttribute *displayAttribute = collection.attribute<EntityDisplayAttribute>();
00699           displayAttribute->setDisplayName( value.toString() );
00700         }
00701       }
00702 
00703       if ( Qt::BackgroundRole == role )
00704       {
00705         QColor color = value.value<QColor>();
00706 
00707         if ( !color.isValid() )
00708           return false;
00709 
00710         EntityDisplayAttribute *eda = collection.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
00711         eda->setBackgroundColor( color );
00712       }
00713 
00714       if ( CollectionRole == role )
00715         collection = value.value<Collection>();
00716 
00717       CollectionModifyJob *job = new CollectionModifyJob( collection, d->m_session );
00718       connect( job, SIGNAL( result( KJob* ) ),
00719                SLOT( updateJobDone( KJob* ) ) );
00720 
00721       return false;
00722     } else if ( Node::Item == node->type ) {
00723 
00724       Item item = d->m_items.value( node->id );
00725 
00726       if ( !item.isValid() || !value.isValid() )
00727         return false;
00728 
00729       if ( Qt::EditRole == role ) {
00730         if ( item.hasAttribute<EntityDisplayAttribute>() ) {
00731           EntityDisplayAttribute *displayAttribute = item.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
00732           displayAttribute->setDisplayName( value.toString() );
00733         }
00734       }
00735 
00736       if ( Qt::BackgroundRole == role )
00737       {
00738         QColor color = value.value<QColor>();
00739 
00740         if ( !color.isValid() )
00741           return false;
00742 
00743         EntityDisplayAttribute *eda = item.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
00744         eda->setBackgroundColor( color );
00745       }
00746 
00747       if ( ItemRole == role )
00748       {
00749         item = value.value<Item>();
00750         Q_ASSERT( item.id() == node->id );
00751       }
00752 
00753       ItemModifyJob *itemModifyJob = new ItemModifyJob( item, d->m_session );
00754       connect( itemModifyJob, SIGNAL( result( KJob* ) ),
00755                SLOT( updateJobDone( KJob* ) ) );
00756 
00757       return false;
00758     }
00759   }
00760 
00761   return QAbstractItemModel::setData( index, value, role );
00762 }
00763 
00764 bool EntityTreeModel::canFetchMore( const QModelIndex & parent ) const
00765 {
00766   Q_D( const EntityTreeModel );
00767   const Item item = parent.data( ItemRole ).value<Item>();
00768 
00769   if ( item.isValid() ) {
00770     // items can't have more rows.
00771     // TODO: Should I use this for fetching more of an item, ie more payload parts?
00772     return false;
00773   } else {
00774     // but collections can...
00775     const Collection::Id colId = parent.data( CollectionIdRole ).toULongLong();
00776 
00777     // But the root collection can't...
00778     if ( Collection::root().id() == colId )
00779       return false;
00780 
00781     foreach ( Node *node, d->m_childEntities.value( colId ) ) {
00782       if ( Node::Item == node->type ) {
00783         // Only try to fetch more from a collection if we don't already have items in it.
00784         // Otherwise we'd spend all the time listing items in collections.
00785         // This means that collections which don't contain items get a lot of item fetch jobs started on them.
00786         // Will fix that later.
00787         return false;
00788       }
00789     }
00790 
00791     return true;
00792   }
00793 
00794   // TODO: It might be possible to get akonadi to tell us if a collection is empty
00795   //       or not and use that information instead of assuming all collections are not empty.
00796   //       Using Collection statistics?
00797 }
00798 
00799 void EntityTreeModel::fetchMore( const QModelIndex & parent )
00800 {
00801   Q_D( EntityTreeModel );
00802 
00803   if ( !canFetchMore( parent ) )
00804     return;
00805 
00806   if ( d->m_itemPopulation == ImmediatePopulation )
00807     // Nothing to do. The items are already in the model.
00808     return;
00809   else if ( d->m_itemPopulation == LazyPopulation ) {
00810     const Collection collection = parent.data( CollectionRole ).value<Collection>();
00811 
00812     if ( !collection.isValid() )
00813       return;
00814 
00815     d->fetchItems( collection );
00816   }
00817 }
00818 
00819 bool EntityTreeModel::hasChildren( const QModelIndex &parent ) const
00820 {
00821   Q_D( const EntityTreeModel );
00822   // TODO: Empty collections right now will return true and get a little + to expand.
00823   // There is probably no way to tell if a collection
00824   // has child items in akonadi without first attempting an itemFetchJob...
00825   // Figure out a way to fix this. (Statistics)
00826   return ((rowCount( parent ) > 0) || (canFetchMore( parent ) && d->m_itemPopulation == LazyPopulation));
00827 }
00828 
00829 bool EntityTreeModel::entityMatch( const Item &item, const QVariant &value, Qt::MatchFlags flags ) const
00830 {
00831   Q_UNUSED( item );
00832   Q_UNUSED( value );
00833   Q_UNUSED( flags );
00834   return false;
00835 }
00836 
00837 bool EntityTreeModel::entityMatch( const Collection &collection, const QVariant &value, Qt::MatchFlags flags ) const
00838 {
00839   Q_UNUSED( collection );
00840   Q_UNUSED( value );
00841   Q_UNUSED( flags );
00842   return false;
00843 }
00844 
00845 QModelIndexList EntityTreeModel::match( const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags ) const
00846 {
00847   Q_D( const EntityTreeModel );
00848 
00849   if ( role == CollectionIdRole || role == CollectionRole ) {
00850     Collection::Id id;
00851     if ( role == CollectionRole ) {
00852       const Collection collection = value.value<Collection>();
00853       id = collection.id();
00854     }
00855 
00856     id = value.toLongLong();
00857     QModelIndexList list;
00858 
00859     const Collection collection = d->m_collections.value( id );
00860 
00861     if ( !collection.isValid() )
00862       return list;
00863 
00864     const QModelIndex collectionIndex = d->indexForCollection( collection );
00865     Q_ASSERT( collectionIndex.isValid() );
00866     list << collectionIndex;
00867 
00868     return list;
00869   }
00870 
00871   if ( role == ItemIdRole ) {
00872     Item::Id id;
00873     if ( role == CollectionRole ) {
00874       const Item item = value.value<Item>();
00875       id = item.id();
00876     }
00877 
00878     id = value.toLongLong();
00879     QModelIndexList list;
00880 
00881     const Item item = d->m_items.value( id );
00882     if ( !item.isValid() )
00883       return list;
00884 
00885     return d->indexesForItem( item );
00886   }
00887 
00888   if ( role == EntityUrlRole ) {
00889     const KUrl url( value.toString() );
00890     const Item item = Item::fromUrl( url );
00891 
00892     if ( item.isValid() )
00893       return d->indexesForItem( d->m_items.value( item.id() ) );
00894 
00895     const Collection collection = Collection::fromUrl( url );
00896     QModelIndexList list;
00897     if ( collection.isValid() )
00898       list << d->indexForCollection( collection );
00899 
00900     return list;
00901   }
00902 
00903   if ( role != AmazingCompletionRole )
00904     return QAbstractItemModel::match( start, role, value, hits, flags );
00905 
00906   // Try to match names, and email addresses.
00907   QModelIndexList list;
00908 
00909   if ( role < 0 || !start.isValid() || !value.isValid() )
00910     return list;
00911 
00912   const int column = 0;
00913   int row = start.row();
00914   const QModelIndex parentIndex = start.parent();
00915   const int parentRowCount = rowCount( parentIndex );
00916 
00917   while ( row < parentRowCount && (hits == -1 || list.size() < hits) ) {
00918     const QModelIndex idx = index( row, column, parentIndex );
00919     const Item item = idx.data( ItemRole ).value<Item>();
00920 
00921     if ( !item.isValid() ) {
00922       const Collection collection = idx.data( CollectionRole ).value<Collection>();
00923       if ( !collection.isValid() )
00924         continue;
00925 
00926       if ( entityMatch( collection, value, flags ) )
00927         list << idx;
00928 
00929     } else {
00930       if ( entityMatch( item, value, flags ) )
00931         list << idx;
00932     }
00933 
00934     ++row;
00935   }
00936 
00937   return list;
00938 }
00939 
00940 bool EntityTreeModel::insertRows( int, int, const QModelIndex& )
00941 {
00942   return false;
00943 }
00944 
00945 bool EntityTreeModel::insertColumns( int, int, const QModelIndex& )
00946 {
00947   return false;
00948 }
00949 
00950 bool EntityTreeModel::removeRows( int, int, const QModelIndex& )
00951 {
00952   return false;
00953 }
00954 
00955 bool EntityTreeModel::removeColumns( int, int, const QModelIndex& )
00956 {
00957   return false;
00958 }
00959 
00960 void EntityTreeModel::setItemPopulationStrategy( ItemPopulationStrategy strategy )
00961 {
00962   Q_D( EntityTreeModel );
00963   d->beginResetModel();
00964   d->m_itemPopulation = strategy;
00965 
00966   if ( strategy == NoItemPopulation ) {
00967     disconnect( d->m_monitor, SIGNAL( itemAdded( const Akonadi::Item&, const Akonadi::Collection& ) ),
00968             this, SLOT( monitoredItemAdded( const Akonadi::Item&, const Akonadi::Collection& ) ) );
00969     disconnect( d->m_monitor, SIGNAL( itemChanged( const Akonadi::Item&, const QSet<QByteArray>& ) ),
00970             this, SLOT( monitoredItemChanged( const Akonadi::Item&, const QSet<QByteArray>& ) ) );
00971     disconnect( d->m_monitor, SIGNAL( itemRemoved( const Akonadi::Item& ) ),
00972             this, SLOT( monitoredItemRemoved( const Akonadi::Item& ) ) );
00973     disconnect( d->m_monitor, SIGNAL( itemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ),
00974             this, SLOT( monitoredItemMoved( const Akonadi::Item&, const Akonadi::Collection&, const Akonadi::Collection& ) ) );
00975 
00976     disconnect( d->m_monitor, SIGNAL( itemLinked( const Akonadi::Item&, const Akonadi::Collection& ) ),
00977             this, SLOT( monitoredItemLinked( const Akonadi::Item&, const Akonadi::Collection& ) ) );
00978     disconnect( d->m_monitor, SIGNAL( itemUnlinked( const Akonadi::Item&, const Akonadi::Collection& ) ),
00979             this, SLOT( monitoredItemUnlinked( const Akonadi::Item&, const Akonadi::Collection& ) ) );
00980   }
00981 
00982   d->m_monitor->d_ptr->useRefCounting = (strategy == LazyPopulation);
00983 
00984   d->endResetModel();
00985 }
00986 
00987 EntityTreeModel::ItemPopulationStrategy EntityTreeModel::itemPopulationStrategy() const
00988 {
00989   Q_D( const EntityTreeModel );
00990   return d->m_itemPopulation;
00991 }
00992 
00993 void EntityTreeModel::setIncludeRootCollection( bool include )
00994 {
00995   Q_D( EntityTreeModel );
00996   d->beginResetModel();
00997   d->m_showRootCollection = include;
00998   d->endResetModel();
00999 }
01000 
01001 bool EntityTreeModel::includeRootCollection() const
01002 {
01003   Q_D( const EntityTreeModel );
01004   return d->m_showRootCollection;
01005 }
01006 
01007 void EntityTreeModel::setRootCollectionDisplayName( const QString &displayName )
01008 {
01009   Q_D( EntityTreeModel );
01010   d->m_rootCollectionDisplayName = displayName;
01011 
01012   // TODO: Emit datachanged if it is being shown.
01013 }
01014 
01015 QString EntityTreeModel::rootCollectionDisplayName() const
01016 {
01017   Q_D( const EntityTreeModel );
01018   return d->m_rootCollectionDisplayName;
01019 }
01020 
01021 void EntityTreeModel::setCollectionFetchStrategy( CollectionFetchStrategy strategy )
01022 {
01023   Q_D( EntityTreeModel );
01024   d->beginResetModel();
01025   d->m_collectionFetchStrategy = strategy;
01026 
01027 
01028   if ( strategy == FetchNoCollections ) {
01029     disconnect( d->m_monitor, SIGNAL( collectionChanged( const Akonadi::Collection& ) ),
01030             this, SLOT( monitoredCollectionChanged( const Akonadi::Collection& ) ) );
01031     disconnect( d->m_monitor, SIGNAL( collectionAdded( const Akonadi::Collection&, const Akonadi::Collection& ) ),
01032             this, SLOT( monitoredCollectionAdded( const Akonadi::Collection&, const Akonadi::Collection& ) ) );
01033     disconnect( d->m_monitor, SIGNAL( collectionRemoved( const Akonadi::Collection& ) ),
01034             this, SLOT( monitoredCollectionRemoved( const Akonadi::Collection& ) ) );
01035     disconnect( d->m_monitor,
01036             SIGNAL( collectionMoved( const Akonadi::Collection&, const Akonadi::Collection&, const Akonadi::Collection& ) ),
01037             this, SLOT( monitoredCollectionMoved( const Akonadi::Collection&, const Akonadi::Collection&, const Akonadi::Collection& ) ) );
01038   }
01039   d->endResetModel();
01040 }
01041 
01042 EntityTreeModel::CollectionFetchStrategy EntityTreeModel::collectionFetchStrategy() const
01043 {
01044   Q_D( const EntityTreeModel );
01045   return d->m_collectionFetchStrategy;
01046 }
01047 
01048 #include "entitytreemodel.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
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
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