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