20 #include "entitytreemodel.h"
21 #include "entitytreemodel_p.h"
23 #include "monitor_p.h"
25 #include <QtCore/QHash>
26 #include <QtCore/QMimeData>
27 #include <QtCore/QTimer>
28 #include <QAbstractProxyModel>
29 #include <QApplication>
33 #include <KDE/KLocale>
34 #include <KDE/KMessageBox>
37 #include <akonadi/attributefactory.h>
38 #include <akonadi/changerecorder.h>
39 #include <akonadi/collectionmodifyjob.h>
40 #include <akonadi/entitydisplayattribute.h>
41 #include <akonadi/transactionsequence.h>
42 #include <akonadi/itemmodifyjob.h>
43 #include <akonadi/session.h>
44 #include "collectionfetchscope.h"
46 #include "collectionutils_p.h"
49 #include "pastehelper_p.h"
51 Q_DECLARE_METATYPE( QSet<QByteArray> )
53 using namespace Akonadi;
58 : QAbstractItemModel( parent ),
68 : QAbstractItemModel( parent ),
78 foreach (
const QList<Node*> &list, d->m_childEntities ) {
79 QList<Node*>::const_iterator it = list.constBegin();
80 const QList<Node*>::const_iterator end = list.constEnd();
81 for ( ; it != end; ++it ) {
94 return d->m_includeUnsubscribed;
100 d->beginResetModel();
101 d->m_includeUnsubscribed = show;
110 return d->m_showSystemEntities;
116 d->m_showSystemEntities = show;
122 d->beginResetModel();
126 int EntityTreeModel::columnCount(
const QModelIndex & parent )
const
129 if ( parent.isValid() &&
130 parent.column() != 0 ) {
142 case Qt::DisplayRole:
151 return QString( QLatin1String(
"<" ) + QString::number( item.
id() ) + QLatin1String(
">" ) );
154 case Qt::DecorationRole:
178 if ( role == Qt::DisplayRole ) {
179 return d->m_rootCollectionDisplayName;
182 if ( role == Qt::EditRole ) {
188 case Qt::DisplayRole:
195 if ( !collection.
name().isEmpty() ) {
196 return collection.
name();
198 return i18n(
"Loading..." );
201 case Qt::DecorationRole:
206 return KIcon( CollectionUtils::defaultIconName( collection ) );
214 QVariant EntityTreeModel::data(
const QModelIndex & index,
int role )
const
218 return QVariant::fromValue( qobject_cast<QObject *>( d->m_session ) );
225 if ( !index.isValid() ) {
230 return entityColumnCount( headerGroup );
234 return entityColumnCount( headerGroup );
237 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
241 const Collection parentCollection = d->m_collections.value( node->parent );
242 Q_ASSERT( parentCollection.
isValid() );
244 return QVariant::fromValue( parentCollection );
247 if ( Node::Collection == node->type ) {
249 const Collection collection = d->m_collections.value( node->id );
263 return collection.
id();
271 return QVariant::fromValue( collection );
274 return collection.
url().url();
287 return d->m_collectionSyncProgress.value( collection.
id() );
291 return d->m_populatedCols.contains( collection.
id() );
293 case Qt::BackgroundRole:
298 if ( color.isValid() ) {
305 return entityData( collection, index.column(), role );
309 }
else if ( Node::Item == node->type ) {
310 const Item item = d->m_items.value( node->id );
325 return QVariant::fromValue( item );
342 case Qt::BackgroundRole:
347 if ( color.isValid() ) {
354 return entityData( item, index.column(), role );
363 Qt::ItemFlags EntityTreeModel::flags(
const QModelIndex & index )
const
368 if ( !index.isValid() ) {
372 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
374 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
376 if ( Node::Collection == node->type ) {
378 if ( d->m_pendingCutCollections.contains( node->id ) ) {
379 return Qt::ItemIsSelectable;
382 const Collection collection = d->m_collections.value( node->id );
390 const int rights = collection.
rights();
393 if ( index.column() == 0 ) {
394 flags |= Qt::ItemIsEditable;
398 flags |= Qt::ItemIsDropEnabled;
402 flags |= Qt::ItemIsDropEnabled;
406 flags |= Qt::ItemIsDragEnabled;
409 }
else if ( Node::Item == node->type ) {
410 if ( d->m_pendingCutItems.contains( node->id ) ) {
411 return Qt::ItemIsSelectable;
417 if ( !index.parent().isValid() ) {
418 parentCollection = d->m_rootCollection;
420 const Node *parentNode =
reinterpret_cast<Node *
>( index.parent().internalPointer() );
422 parentCollection = d->m_collections.value( parentNode->id );
424 if ( parentCollection.
isValid() ) {
425 const int rights = parentCollection.
rights();
429 flags = flags | Qt::ItemIsEditable;
432 flags |= Qt::ItemIsDragEnabled;
439 Qt::DropActions EntityTreeModel::supportedDropActions()
const
441 return ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction );
444 QStringList EntityTreeModel::mimeTypes()
const
447 return QStringList() << QLatin1String(
"text/uri-list" );
450 bool EntityTreeModel::dropMimeData(
const QMimeData * data, Qt::DropAction action,
int row,
int column,
const QModelIndex & parent )
457 if ( !parent.isValid() ) {
475 if ( action == Qt::IgnoreAction ) {
483 Node *node =
reinterpret_cast<Node *
>( parent.internalId() );
487 if ( Node::Item == node->type ) {
488 if ( !parent.parent().isValid() ) {
491 kWarning() <<
"Dropped onto item with no parent collection";
496 node =
reinterpret_cast<Node *
>( parent.parent().internalId() );
499 if ( Node::Collection == node->type ) {
500 const Collection destCollection = d->m_collections.value( node->id );
508 if ( data->hasFormat( QLatin1String(
"text/uri-list" ) ) ) {
513 const KUrl::List urls = KUrl::List::fromMimeData( data );
514 foreach (
const KUrl &url, urls ) {
518 action != Qt::CopyAction ) {
519 kDebug() <<
"Error: source and destination of move are the same.";
528 if ( url.hasQueryItem( QLatin1String(
"name" ) ) ) {
529 const QString collectionName = url.queryItemValue( QLatin1String(
"name" ) );
532 if ( collectionNames.contains( collectionName ) ) {
533 KMessageBox::error( 0, i18n(
"The target collection '%1' contains already\na collection with name '%2'.",
534 destCollection.
name(), collection.
name() ) );
542 kDebug() <<
"Error: source and destination of move are the same.";
559 connect( job, SIGNAL(result(KJob*)), SLOT(pasteJobDone(KJob*)) );
573 QModelIndex EntityTreeModel::index(
int row,
int column,
const QModelIndex & parent )
const
578 if ( parent.column() > 0 ) {
579 return QModelIndex();
583 if ( column >= columnCount() ||
585 return QModelIndex();
588 QList<Node*> childEntities;
590 const Node *parentNode =
reinterpret_cast<Node*
>( parent.internalPointer() );
592 if ( !parentNode || !parent.isValid() ) {
593 if ( d->m_showRootCollection ) {
594 childEntities << d->m_childEntities.value( -1 );
596 childEntities = d->m_childEntities.value( d->m_rootCollection.
id() );
599 if ( parentNode->id >= 0 ) {
600 childEntities = d->m_childEntities.value( parentNode->id );
604 const int size = childEntities.size();
605 if ( row < 0 || row >= size ) {
606 return QModelIndex();
609 Node *node = childEntities.at( row );
611 return createIndex( row, column, reinterpret_cast<void*>( node ) );
614 QModelIndex EntityTreeModel::parent(
const QModelIndex & index )
const
618 if ( !index.isValid() ) {
619 return QModelIndex();
624 return QModelIndex();
627 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
630 return QModelIndex();
633 const Collection collection = d->m_collections.value( node->parent );
636 return QModelIndex();
639 if ( collection.
id() == d->m_rootCollection.
id() ) {
640 if ( !d->m_showRootCollection ) {
641 return QModelIndex();
643 return createIndex( 0, 0, reinterpret_cast<void *>( d->m_rootNode ) );
650 Q_ASSERT( row >= 0 );
651 Node *parentNode = d->m_childEntities.value( collection.
parentCollection().
id() ).at( row );
653 return createIndex( row, 0, reinterpret_cast<void*>( parentNode ) );
656 int EntityTreeModel::rowCount(
const QModelIndex & parent )
const
662 if ( parent.isValid() ) {
665 return d->m_items.size();
669 if ( !parent.isValid() ) {
671 if ( d->m_showRootCollection ) {
672 return d->m_childEntities.value( -1 ).size();
674 return d->m_childEntities.value( d->m_rootCollection.
id() ).size();
677 if ( parent.column() != 0 ) {
681 const Node *node =
reinterpret_cast<Node*
>( parent.internalPointer() );
687 if ( Node::Item == node->type ) {
691 Q_ASSERT( parent.isValid() );
692 return d->m_childEntities.value( node->id ).size();
695 int EntityTreeModel::entityColumnCount( HeaderGroup headerGroup )
const
698 Q_UNUSED( headerGroup );
707 Q_UNUSED( headerGroup );
710 orientation == Qt::Horizontal &&
711 role == Qt::DisplayRole ) {
713 return i18nc(
"@title:column Name of a thing",
"Name" );
715 return d->m_rootCollection.
name();
718 return QAbstractItemModel::headerData( section, orientation, role );
721 QVariant EntityTreeModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
729 QMimeData *EntityTreeModel::mimeData(
const QModelIndexList &indexes )
const
733 QMimeData *data =
new QMimeData();
735 foreach (
const QModelIndex &index, indexes ) {
736 if ( index.column() != 0 ) {
740 if ( !index.isValid() ) {
744 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
746 if ( Node::Collection == node->type ) {
748 }
else if ( Node::Item == node->type ) {
755 urls.populateMimeData( data );
761 bool EntityTreeModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
765 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
768 if ( index.isValid() && value.toBool() ) {
769 if ( Node::Collection == node->type ) {
770 d->m_pendingCutCollections.append( node->id );
773 if ( Node::Item == node->type ) {
774 d->m_pendingCutItems.append( node->id );
777 d->m_pendingCutCollections.clear();
778 d->m_pendingCutItems.clear();
783 if ( index.isValid() &&
784 node->type == Node::Collection &&
788 Q_ASSERT( collection.
isValid() );
791 d->deref( collection.
id() );
793 d->ref( collection.
id() );
797 if ( index.column() == 0 &&
799 if ( Node::Collection == node->type ) {
801 Collection collection = d->m_collections.value( node->id );
803 if ( !collection.
isValid() || !value.isValid() ) {
807 if ( Qt::EditRole == role ) {
808 collection.
setName( value.toString() );
816 if ( Qt::BackgroundRole == role ) {
817 QColor color = value.value<QColor>();
819 if ( !color.isValid() ) {
832 connect( job, SIGNAL(result(KJob*)),
833 SLOT(updateJobDone(KJob*)) );
836 }
else if ( Node::Item == node->type ) {
838 Item item = d->m_items.value( node->id );
840 if ( !item.
isValid() || !value.isValid() ) {
844 if ( Qt::EditRole == role ) {
851 if ( Qt::BackgroundRole == role ) {
852 QColor color = value.value<QColor>();
854 if ( !color.isValid() ) {
863 item = value.value<
Item>();
864 Q_ASSERT( item.
id() == node->id );
868 connect( itemModifyJob, SIGNAL(result(KJob*)),
869 SLOT(updateJobDone(KJob*)) );
875 return QAbstractItemModel::setData( index, value, role );
878 bool EntityTreeModel::canFetchMore(
const QModelIndex & parent )
const
888 if ( !d->canFetchMore( parent ) ) {
906 d->fetchItems( collection );
910 bool EntityTreeModel::hasChildren(
const QModelIndex &parent )
const
916 return parent.isValid() ?
false : !d->m_items.isEmpty();
923 return ( ( rowCount( parent ) > 0 ) ||
924 ( canFetchMore( parent ) && d->m_itemPopulation ==
LazyPopulation ) );
931 return d->m_collectionTreeFetched;
944 Q_UNUSED( collection );
950 QModelIndexList
EntityTreeModel::match(
const QModelIndex& start,
int role,
const QVariant& value,
int hits, Qt::MatchFlags flags )
const
958 id = collection.
id();
960 id = value.toLongLong();
963 QModelIndexList list;
965 const Collection collection = d->m_collections.value(
id );
972 Q_ASSERT( collectionIndex.isValid() );
973 list << collectionIndex;
981 const Item item = value.value<
Item>();
984 id = value.toLongLong();
986 QModelIndexList list;
988 const Item item = d->m_items.value(
id );
997 const KUrl url( value.toString() );
1000 if ( item.isValid() ) {
1005 QModelIndexList list;
1014 return QAbstractItemModel::match( start, role, value, hits, flags );
1018 QModelIndexList list;
1022 !value.isValid() ) {
1026 const int column = 0;
1027 int row = start.row();
1028 const QModelIndex parentIndex = start.parent();
1029 const int parentRowCount = rowCount( parentIndex );
1031 while ( row < parentRowCount &&
1032 ( hits == -1 || list.size() < hits ) ) {
1033 const QModelIndex idx = index( row, column, parentIndex );
1038 if ( !collection.
isValid() ) {
1058 bool EntityTreeModel::insertRows(
int,
int,
const QModelIndex& )
1063 bool EntityTreeModel::insertColumns(
int,
int,
const QModelIndex& )
1068 bool EntityTreeModel::removeRows(
int,
int,
const QModelIndex& )
1073 bool EntityTreeModel::removeColumns(
int,
int,
const QModelIndex& )
1081 d->beginResetModel();
1082 d->m_itemPopulation = strategy;
1087 disconnect( d->m_monitor, SIGNAL(itemChanged(
Akonadi::Item,QSet<QByteArray>)),
1088 this, SLOT(monitoredItemChanged(
Akonadi::Item,QSet<QByteArray>)) );
1089 disconnect( d->m_monitor, SIGNAL(itemRemoved(
Akonadi::Item)),
1100 d->m_monitor->d_ptr->useRefCounting = ( strategy ==
LazyPopulation );
1108 return d->m_itemPopulation;
1114 d->beginResetModel();
1115 d->m_showRootCollection = include;
1122 return d->m_showRootCollection;
1128 d->m_rootCollectionDisplayName = displayName;
1136 return d->m_rootCollectionDisplayName;
1142 d->beginResetModel();
1143 d->m_collectionFetchStrategy = strategy;
1153 disconnect( d->m_monitor,
1166 return d->m_collectionFetchStrategy;
1169 static QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel *> proxiesAndModel(
const QAbstractItemModel *model )
1171 QList<const QAbstractProxyModel *> proxyChain;
1172 const QAbstractProxyModel *proxy = qobject_cast<
const QAbstractProxyModel *>( model );
1173 const QAbstractItemModel *_model = model;
1175 proxyChain.prepend( proxy );
1176 _model = proxy->sourceModel();
1177 proxy = qobject_cast<
const QAbstractProxyModel *>( _model );
1181 return qMakePair( proxyChain, etm );
1184 static QModelIndex proxiedIndex(
const QModelIndex &idx, QList<const QAbstractProxyModel *> proxyChain )
1186 QListIterator<const QAbstractProxyModel *> it( proxyChain );
1187 QModelIndex _idx = idx;
1188 while ( it.hasNext() ) {
1189 _idx = it.next()->mapFromSource( _idx );
1196 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1197 QModelIndex idx = pair.second->d_ptr->indexForCollection( collection );
1198 return proxiedIndex( idx, pair.first );
1203 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1205 if ( !pair.second ) {
1206 kWarning() <<
"Couldn't find an EntityTreeModel";
1207 return QModelIndexList();
1210 QModelIndexList list = pair.second->d_ptr->indexesForItem( item );
1211 QModelIndexList proxyList;
1212 foreach (
const QModelIndex &idx, list ) {
1213 const QModelIndex pIdx = proxiedIndex( idx, pair.first );
1214 if ( pIdx.isValid() ) {
1221 #include "moc_entitytreemodel.cpp"