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