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 <QtGui/QAbstractProxyModel>
29 #include <QtGui/QApplication>
30 #include <QtGui/QPalette>
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() && parent.column() != 0 )
140 case Qt::DisplayRole:
148 return QString(QLatin1String(
"<") + QString::number( item.
id() ) + QLatin1String(
">"));
151 case Qt::DecorationRole:
173 if ( role == Qt::DisplayRole )
174 return d->m_rootCollectionDisplayName;
176 if ( role == Qt::EditRole )
181 case Qt::DisplayRole:
188 if ( !collection.
name().isEmpty() )
189 return collection.
name();
190 return i18n(
"Loading..." );
193 case Qt::DecorationRole:
198 return KIcon( CollectionUtils::defaultIconName( collection ) );
206 QVariant EntityTreeModel::data(
const QModelIndex & index,
int role )
const
210 return QVariant::fromValue( qobject_cast<QObject *>( d->m_session ) );
216 if ( !index.isValid() ) {
220 return entityColumnCount( headerGroup );
224 return entityColumnCount( headerGroup );
226 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
229 const Collection parentCollection = d->m_collections.value( node->parent );
230 Q_ASSERT( parentCollection.
isValid() );
232 return QVariant::fromValue( parentCollection );
235 if ( Node::Collection == node->type ) {
237 const Collection collection = d->m_collections.value( node->id );
250 return collection.
id();
258 return QVariant::fromValue( collection );
261 return collection.
url().url();
274 return d->m_collectionSyncProgress.value( collection.
id() );
276 case Qt::BackgroundRole:
282 if ( color.isValid() )
288 return entityData( collection, index.column(), role );
292 }
else if ( Node::Item == node->type ) {
293 const Item item = d->m_items.value( node->id );
307 return QVariant::fromValue( item );
324 case Qt::BackgroundRole:
330 if ( color.isValid() )
336 return entityData( item, index.column(), role );
345 Qt::ItemFlags EntityTreeModel::flags(
const QModelIndex & index )
const
350 if ( !index.isValid() )
353 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
355 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
357 if ( Node::Collection == node->type ) {
359 if ( d->m_pendingCutCollections.contains( node->id ) )
360 return Qt::ItemIsSelectable;
362 const Collection collection = d->m_collections.value( node->id );
370 const int rights = collection.
rights();
373 if ( index.column() == 0 )
374 flags |= Qt::ItemIsEditable;
377 flags |= Qt::ItemIsDropEnabled;
381 flags |= Qt::ItemIsDropEnabled;
385 flags |= Qt::ItemIsDragEnabled;
388 }
else if ( Node::Item == node->type ) {
389 if ( d->m_pendingCutItems.contains( node->id ) )
390 return Qt::ItemIsSelectable;
395 if ( !index.parent().isValid() ) {
396 parentCollection = d->m_rootCollection;
398 const Node *parentNode =
reinterpret_cast<Node *
>( index.parent().internalPointer() );
400 parentCollection = d->m_collections.value( parentNode->id );
402 if ( parentCollection.
isValid() ) {
403 const int rights = parentCollection.
rights();
407 flags = flags | Qt::ItemIsEditable;
410 flags |= Qt::ItemIsDragEnabled;
417 Qt::DropActions EntityTreeModel::supportedDropActions()
const
419 return (Qt::CopyAction | Qt::MoveAction | Qt::LinkAction);
422 QStringList EntityTreeModel::mimeTypes()
const
425 return QStringList() << QLatin1String(
"text/uri-list" );
428 bool EntityTreeModel::dropMimeData(
const QMimeData * data, Qt::DropAction action,
int row,
int column,
const QModelIndex & parent )
435 if ( !parent.isValid() )
452 if ( action == Qt::IgnoreAction )
459 Node *node =
reinterpret_cast<Node *
>( parent.internalId() );
463 if ( Node::Item == node->type ) {
464 if ( !parent.parent().isValid() ) {
467 kWarning() <<
"Dropped onto item with no parent collection";
472 node =
reinterpret_cast<Node *
>( parent.parent().internalId() );
475 if ( Node::Collection == node->type ) {
476 const Collection destCollection = d->m_collections.value( node->id );
483 if ( data->hasFormat( QLatin1String(
"text/uri-list" ) ) ) {
488 const KUrl::List urls = KUrl::List::fromMimeData( data );
489 foreach (
const KUrl &url, urls ) {
493 kDebug() <<
"Error: source and destination of move are the same.";
502 if ( url.hasQueryItem( QLatin1String(
"name" ) ) ) {
503 const QString collectionName = url.queryItemValue( QLatin1String(
"name" ) );
506 if ( collectionNames.contains( collectionName ) ) {
507 KMessageBox::error( 0, i18n(
"The target collection '%1' contains already\na collection with name '%2'.",
508 destCollection.
name(), collection.
name() ) );
516 kDebug() <<
"Error: source and destination of move are the same.";
532 connect( job, SIGNAL(result(KJob*)), SLOT(pasteJobDone(KJob*)) );
546 QModelIndex EntityTreeModel::index(
int row,
int column,
const QModelIndex & parent )
const
551 if ( parent.column() > 0 )
552 return QModelIndex();
555 if ( column >= columnCount() || column < 0 )
556 return QModelIndex();
558 QList<Node*> childEntities;
560 const Node *parentNode =
reinterpret_cast<Node*
>( parent.internalPointer() );
562 if ( !parentNode || !parent.isValid() ) {
563 if ( d->m_showRootCollection )
564 childEntities << d->m_childEntities.value( -1 );
566 childEntities = d->m_childEntities.value( d->m_rootCollection.
id() );
568 if ( parentNode->id >= 0 )
569 childEntities = d->m_childEntities.value( parentNode->id );
572 const int size = childEntities.size();
573 if ( row < 0 || row >= size )
574 return QModelIndex();
576 Node *node = childEntities.at( row );
578 return createIndex( row, column, reinterpret_cast<void*>( node ) );
581 QModelIndex EntityTreeModel::parent(
const QModelIndex & index )
const
585 if ( !index.isValid() )
586 return QModelIndex();
589 return QModelIndex();
591 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
594 return QModelIndex();
596 const Collection collection = d->m_collections.value( node->parent );
599 return QModelIndex();
601 if ( collection.
id() == d->m_rootCollection.
id() ) {
602 if ( !d->m_showRootCollection )
603 return QModelIndex();
605 return createIndex( 0, 0, reinterpret_cast<void *>( d->m_rootNode ) );
611 Q_ASSERT( row >= 0 );
612 Node *parentNode = d->m_childEntities.value( collection.
parentCollection().
id() ).at( row );
614 return createIndex( row, 0, reinterpret_cast<void*>( parentNode ) );
617 int EntityTreeModel::rowCount(
const QModelIndex & parent )
const
622 if ( parent.isValid() )
625 return d->m_items.size();
628 if ( !parent.isValid() ) {
630 if ( d->m_showRootCollection )
631 return d->m_childEntities.value( -1 ).size();
632 return d->m_childEntities.value( d->m_rootCollection.
id() ).size();
635 if ( parent.column() != 0 )
638 const Node *node =
reinterpret_cast<Node*
>( parent.internalPointer() );
643 if ( Node::Item == node->type )
646 Q_ASSERT( parent.isValid() );
647 return d->m_childEntities.value( node->id ).size();
650 int EntityTreeModel::entityColumnCount( HeaderGroup headerGroup )
const
653 Q_UNUSED( headerGroup );
662 Q_UNUSED( headerGroup );
664 if ( section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
666 return i18nc(
"@title:column Name of a thing",
"Name" );
667 return d->m_rootCollection.
name();
670 return QAbstractItemModel::headerData( section, orientation, role );
673 QVariant EntityTreeModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
681 QMimeData *EntityTreeModel::mimeData(
const QModelIndexList &indexes )
const
685 QMimeData *data =
new QMimeData();
687 foreach (
const QModelIndex &index, indexes ) {
688 if ( index.column() != 0 )
691 if ( !index.isValid() )
694 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
696 if ( Node::Collection == node->type )
698 else if ( Node::Item == node->type )
704 urls.populateMimeData( data );
710 bool EntityTreeModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
714 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
717 if ( index.isValid() && value.toBool() ) {
718 if ( Node::Collection == node->type )
719 d->m_pendingCutCollections.append( node->id );
721 if ( Node::Item == node->type )
722 d->m_pendingCutItems.append( node->id );
724 d->m_pendingCutCollections.clear();
725 d->m_pendingCutItems.clear();
732 Q_ASSERT( collection.
isValid() );
735 d->deref( collection.
id() );
737 d->ref( collection.
id() );
741 if ( Node::Collection == node->type ) {
743 Collection collection = d->m_collections.value( node->id );
745 if ( !collection.
isValid() || !value.isValid() )
748 if ( Qt::EditRole == role ) {
749 collection.
setName( value.toString() );
757 if ( Qt::BackgroundRole == role )
759 QColor color = value.value<QColor>();
761 if ( !color.isValid() )
772 connect( job, SIGNAL(result(KJob*)),
773 SLOT(updateJobDone(KJob*)) );
776 }
else if ( Node::Item == node->type ) {
778 Item item = d->m_items.value( node->id );
780 if ( !item.
isValid() || !value.isValid() )
783 if ( Qt::EditRole == role ) {
790 if ( Qt::BackgroundRole == role )
792 QColor color = value.value<QColor>();
794 if ( !color.isValid() )
803 item = value.value<
Item>();
804 Q_ASSERT( item.
id() == node->id );
808 connect( itemModifyJob, SIGNAL(result(KJob*)),
809 SLOT(updateJobDone(KJob*)) );
815 return QAbstractItemModel::setData( index, value, role );
818 bool EntityTreeModel::canFetchMore(
const QModelIndex & parent )
const
828 if ( !d->canFetchMore( parent ) )
843 d->fetchItems( collection );
847 bool EntityTreeModel::hasChildren(
const QModelIndex &parent )
const
852 return parent.isValid() ?
false : !d->m_items.isEmpty();
858 return ((rowCount( parent ) > 0) || (canFetchMore( parent ) && d->m_itemPopulation ==
LazyPopulation));
871 Q_UNUSED( collection );
877 QModelIndexList
EntityTreeModel::match(
const QModelIndex& start,
int role,
const QVariant& value,
int hits, Qt::MatchFlags flags )
const
885 id = collection.
id();
887 id = value.toLongLong();
890 QModelIndexList list;
892 const Collection collection = d->m_collections.value(
id );
898 Q_ASSERT( collectionIndex.isValid() );
899 list << collectionIndex;
907 const Item item = value.value<
Item>();
910 id = value.toLongLong();
912 QModelIndexList list;
914 const Item item = d->m_items.value(
id );
922 const KUrl url( value.toString() );
925 if ( item.isValid() )
929 QModelIndexList list;
940 QModelIndexList list;
942 if ( role < 0 || !start.isValid() || !value.isValid() )
945 const int column = 0;
946 int row = start.row();
947 const QModelIndex parentIndex = start.parent();
948 const int parentRowCount = rowCount( parentIndex );
950 while ( row < parentRowCount && (hits == -1 || list.size() < hits) ) {
951 const QModelIndex idx = index( row, column, parentIndex );
973 bool EntityTreeModel::insertRows(
int,
int,
const QModelIndex& )
978 bool EntityTreeModel::insertColumns(
int,
int,
const QModelIndex& )
983 bool EntityTreeModel::removeRows(
int,
int,
const QModelIndex& )
988 bool EntityTreeModel::removeColumns(
int,
int,
const QModelIndex& )
996 d->beginResetModel();
997 d->m_itemPopulation = strategy;
1002 disconnect( d->m_monitor, SIGNAL(itemChanged(
Akonadi::Item,QSet<QByteArray>)),
1003 this, SLOT(monitoredItemChanged(
Akonadi::Item,QSet<QByteArray>)) );
1004 disconnect( d->m_monitor, SIGNAL(itemRemoved(
Akonadi::Item)),
1015 d->m_monitor->d_ptr->useRefCounting = (strategy ==
LazyPopulation);
1023 return d->m_itemPopulation;
1029 d->beginResetModel();
1030 d->m_showRootCollection = include;
1037 return d->m_showRootCollection;
1043 d->m_rootCollectionDisplayName = displayName;
1051 return d->m_rootCollectionDisplayName;
1057 d->beginResetModel();
1058 d->m_collectionFetchStrategy = strategy;
1067 disconnect( d->m_monitor,
1080 return d->m_collectionFetchStrategy;
1083 static QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel *> proxiesAndModel(
const QAbstractItemModel *model )
1085 QList<const QAbstractProxyModel *> proxyChain;
1086 const QAbstractProxyModel *proxy = qobject_cast<
const QAbstractProxyModel *>( model );
1087 const QAbstractItemModel *_model = model;
1090 proxyChain.prepend( proxy );
1091 _model = proxy->sourceModel();
1092 proxy = qobject_cast<
const QAbstractProxyModel *>( _model );
1096 return qMakePair(proxyChain, etm);
1099 static QModelIndex proxiedIndex(
const QModelIndex &idx, QList<const QAbstractProxyModel *> proxyChain )
1101 QListIterator<const QAbstractProxyModel *> it( proxyChain );
1102 QModelIndex _idx = idx;
1103 while ( it.hasNext() )
1104 _idx = it.next()->mapFromSource( _idx );
1110 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1111 QModelIndex idx = pair.second->d_ptr->indexForCollection( collection );
1112 return proxiedIndex( idx, pair.first );
1117 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1118 QModelIndexList list = pair.second->d_ptr->indexesForItem( item );
1119 QModelIndexList proxyList;
1120 foreach(
const QModelIndex &idx, list )
1122 const QModelIndex pIdx = proxiedIndex( idx, pair.first );
1123 if ( pIdx.isValid() )
1129 #include "entitytreemodel.moc"