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/KLocalizedString>
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:
191 const QString displayName = collection.
displayName();
192 if ( !displayName.isEmpty() )
195 return i18n(
"Loading..." );
198 case Qt::DecorationRole:
203 return KIcon( CollectionUtils::defaultIconName( collection ) );
211 QVariant EntityTreeModel::data(
const QModelIndex & index,
int role )
const
215 return QVariant::fromValue( qobject_cast<QObject *>( d->m_session ) );
222 if ( !index.isValid() ) {
227 return entityColumnCount( headerGroup );
231 return entityColumnCount( headerGroup );
234 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
238 const Collection parentCollection = d->m_collections.value( node->parent );
239 Q_ASSERT( parentCollection.
isValid() );
241 return QVariant::fromValue( parentCollection );
244 if ( Node::Collection == node->type ) {
246 const Collection collection = d->m_collections.value( node->id );
260 return collection.
id();
268 return QVariant::fromValue( collection );
271 return collection.
url().url();
284 return d->m_collectionSyncProgress.value( collection.
id() );
288 return d->m_populatedCols.contains( collection.
id() );
290 case Qt::BackgroundRole:
295 if ( color.isValid() ) {
302 return entityData( collection, index.column(), role );
306 }
else if ( Node::Item == node->type ) {
307 const Item item = d->m_items.value( node->id );
322 return QVariant::fromValue( item );
339 case Qt::BackgroundRole:
344 if ( color.isValid() ) {
351 return entityData( item, index.column(), role );
360 Qt::ItemFlags EntityTreeModel::flags(
const QModelIndex & index )
const
365 if ( !index.isValid() ) {
369 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
371 const Node *node =
reinterpret_cast<Node *
>( index.internalPointer() );
373 if ( Node::Collection == node->type ) {
375 if ( d->m_pendingCutCollections.contains( node->id ) ) {
376 return Qt::ItemIsSelectable;
379 const Collection collection = d->m_collections.value( node->id );
387 const int rights = collection.
rights();
390 if ( index.column() == 0 ) {
391 flags |= Qt::ItemIsEditable;
395 flags |= Qt::ItemIsDropEnabled;
399 flags |= Qt::ItemIsDropEnabled;
403 flags |= Qt::ItemIsDragEnabled;
406 }
else if ( Node::Item == node->type ) {
407 if ( d->m_pendingCutItems.contains( node->id ) ) {
408 return Qt::ItemIsSelectable;
414 if ( !index.parent().isValid() ) {
415 parentCollection = d->m_rootCollection;
417 const Node *parentNode =
reinterpret_cast<Node *
>( index.parent().internalPointer() );
419 parentCollection = d->m_collections.value( parentNode->id );
421 if ( parentCollection.
isValid() ) {
422 const int rights = parentCollection.
rights();
426 flags = flags | Qt::ItemIsEditable;
429 flags |= Qt::ItemIsDragEnabled;
436 Qt::DropActions EntityTreeModel::supportedDropActions()
const
438 return ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction );
441 QStringList EntityTreeModel::mimeTypes()
const
444 return QStringList() << QLatin1String(
"text/uri-list" );
447 bool EntityTreeModel::dropMimeData(
const QMimeData * data, Qt::DropAction action,
int row,
int column,
const QModelIndex & parent )
454 if ( !parent.isValid() ) {
472 if ( action == Qt::IgnoreAction ) {
480 Node *node =
reinterpret_cast<Node *
>( parent.internalId() );
484 if ( Node::Item == node->type ) {
485 if ( !parent.parent().isValid() ) {
488 kWarning() <<
"Dropped onto item with no parent collection";
493 node =
reinterpret_cast<Node *
>( parent.parent().internalId() );
496 if ( Node::Collection == node->type ) {
497 const Collection destCollection = d->m_collections.value( node->id );
505 if ( data->hasFormat( QLatin1String(
"text/uri-list" ) ) ) {
510 const KUrl::List urls = KUrl::List::fromMimeData( data );
511 foreach (
const KUrl &url, urls ) {
515 action != Qt::CopyAction ) {
516 kDebug() <<
"Error: source and destination of move are the same.";
525 if ( url.hasQueryItem( QLatin1String(
"name" ) ) ) {
526 const QString collectionName = url.queryItemValue( QLatin1String(
"name" ) );
529 if ( collectionNames.contains( collectionName ) ) {
530 KMessageBox::error( 0, i18n(
"The target collection '%1' contains already\na collection with name '%2'.",
531 destCollection.
name(), collection.
name() ) );
539 kDebug() <<
"Error: source and destination of move are the same.";
556 connect( job, SIGNAL(result(KJob*)), SLOT(pasteJobDone(KJob*)) );
570 QModelIndex EntityTreeModel::index(
int row,
int column,
const QModelIndex & parent )
const
575 if ( parent.column() > 0 ) {
576 return QModelIndex();
580 if ( column >= columnCount() ||
582 return QModelIndex();
585 QList<Node*> childEntities;
587 const Node *parentNode =
reinterpret_cast<Node*
>( parent.internalPointer() );
589 if ( !parentNode || !parent.isValid() ) {
590 if ( d->m_showRootCollection ) {
591 childEntities << d->m_childEntities.value( -1 );
593 childEntities = d->m_childEntities.value( d->m_rootCollection.
id() );
596 if ( parentNode->id >= 0 ) {
597 childEntities = d->m_childEntities.value( parentNode->id );
601 const int size = childEntities.size();
602 if ( row < 0 || row >= size ) {
603 return QModelIndex();
606 Node *node = childEntities.at( row );
608 return createIndex( row, column, reinterpret_cast<void*>( node ) );
611 QModelIndex EntityTreeModel::parent(
const QModelIndex & index )
const
615 if ( !index.isValid() ) {
616 return QModelIndex();
621 return QModelIndex();
624 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
627 return QModelIndex();
630 const Collection collection = d->m_collections.value( node->parent );
633 return QModelIndex();
636 if ( collection.
id() == d->m_rootCollection.
id() ) {
637 if ( !d->m_showRootCollection ) {
638 return QModelIndex();
640 return createIndex( 0, 0, reinterpret_cast<void *>( d->m_rootNode ) );
647 Q_ASSERT( row >= 0 );
648 Node *parentNode = d->m_childEntities.value( collection.
parentCollection().
id() ).at( row );
650 return createIndex( row, 0, reinterpret_cast<void*>( parentNode ) );
653 int EntityTreeModel::rowCount(
const QModelIndex & parent )
const
659 if ( parent.isValid() ) {
662 return d->m_items.size();
666 if ( !parent.isValid() ) {
668 if ( d->m_showRootCollection ) {
669 return d->m_childEntities.value( -1 ).size();
671 return d->m_childEntities.value( d->m_rootCollection.
id() ).size();
674 if ( parent.column() != 0 ) {
678 const Node *node =
reinterpret_cast<Node*
>( parent.internalPointer() );
684 if ( Node::Item == node->type ) {
688 Q_ASSERT( parent.isValid() );
689 return d->m_childEntities.value( node->id ).size();
692 int EntityTreeModel::entityColumnCount( HeaderGroup headerGroup )
const
695 Q_UNUSED( headerGroup );
704 Q_UNUSED( headerGroup );
707 orientation == Qt::Horizontal &&
708 role == Qt::DisplayRole ) {
710 return i18nc(
"@title:column Name of a thing",
"Name" );
712 return d->m_rootCollection.
name();
715 return QAbstractItemModel::headerData( section, orientation, role );
718 QVariant EntityTreeModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
726 QMimeData *EntityTreeModel::mimeData(
const QModelIndexList &indexes )
const
730 QMimeData *data =
new QMimeData();
732 foreach (
const QModelIndex &index, indexes ) {
733 if ( index.column() != 0 ) {
737 if ( !index.isValid() ) {
741 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
743 if ( Node::Collection == node->type ) {
745 }
else if ( Node::Item == node->type ) {
752 urls.populateMimeData( data );
758 bool EntityTreeModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
762 const Node *node =
reinterpret_cast<Node*
>( index.internalPointer() );
765 if ( index.isValid() && value.toBool() ) {
766 if ( Node::Collection == node->type ) {
767 d->m_pendingCutCollections.append( node->id );
770 if ( Node::Item == node->type ) {
771 d->m_pendingCutItems.append( node->id );
774 d->m_pendingCutCollections.clear();
775 d->m_pendingCutItems.clear();
780 if ( index.isValid() &&
781 node->type == Node::Collection &&
785 Q_ASSERT( collection.
isValid() );
788 d->deref( collection.
id() );
790 d->ref( collection.
id() );
794 if ( index.column() == 0 &&
796 if ( Node::Collection == node->type ) {
798 Collection collection = d->m_collections.value( node->id );
800 if ( !collection.
isValid() || !value.isValid() ) {
804 if ( Qt::EditRole == role ) {
805 collection.
setName( value.toString() );
813 if ( Qt::BackgroundRole == role ) {
814 QColor color = value.value<QColor>();
816 if ( !color.isValid() ) {
829 connect( job, SIGNAL(result(KJob*)),
830 SLOT(updateJobDone(KJob*)) );
833 }
else if ( Node::Item == node->type ) {
835 Item item = d->m_items.value( node->id );
837 if ( !item.
isValid() || !value.isValid() ) {
841 if ( Qt::EditRole == role ) {
848 if ( Qt::BackgroundRole == role ) {
849 QColor color = value.value<QColor>();
851 if ( !color.isValid() ) {
860 item = value.value<
Item>();
861 Q_ASSERT( item.
id() == node->id );
865 connect( itemModifyJob, SIGNAL(result(KJob*)),
866 SLOT(updateJobDone(KJob*)) );
872 return QAbstractItemModel::setData( index, value, role );
875 bool EntityTreeModel::canFetchMore(
const QModelIndex & parent )
const
885 if ( !d->canFetchMore( parent ) ) {
903 d->fetchItems( collection );
907 bool EntityTreeModel::hasChildren(
const QModelIndex &parent )
const
913 return parent.isValid() ?
false : !d->m_items.isEmpty();
920 return ( ( rowCount( parent ) > 0 ) ||
921 ( canFetchMore( parent ) && d->m_itemPopulation ==
LazyPopulation ) );
928 return d->m_collectionTreeFetched;
941 Q_UNUSED( collection );
947 QModelIndexList
EntityTreeModel::match(
const QModelIndex& start,
int role,
const QVariant& value,
int hits, Qt::MatchFlags flags )
const
955 id = collection.
id();
957 id = value.toLongLong();
960 QModelIndexList list;
962 const Collection collection = d->m_collections.value(
id );
969 Q_ASSERT( collectionIndex.isValid() );
970 list << collectionIndex;
978 const Item item = value.value<
Item>();
981 id = value.toLongLong();
983 QModelIndexList list;
985 const Item item = d->m_items.value(
id );
994 const KUrl url( value.toString() );
997 if ( item.isValid() ) {
1002 QModelIndexList list;
1011 return QAbstractItemModel::match( start, role, value, hits, flags );
1015 QModelIndexList list;
1019 !value.isValid() ) {
1023 const int column = 0;
1024 int row = start.row();
1025 const QModelIndex parentIndex = start.parent();
1026 const int parentRowCount = rowCount( parentIndex );
1028 while ( row < parentRowCount &&
1029 ( hits == -1 || list.size() < hits ) ) {
1030 const QModelIndex idx = index( row, column, parentIndex );
1035 if ( !collection.
isValid() ) {
1055 bool EntityTreeModel::insertRows(
int,
int,
const QModelIndex& )
1060 bool EntityTreeModel::insertColumns(
int,
int,
const QModelIndex& )
1065 bool EntityTreeModel::removeRows(
int,
int,
const QModelIndex& )
1070 bool EntityTreeModel::removeColumns(
int,
int,
const QModelIndex& )
1078 d->beginResetModel();
1079 d->m_itemPopulation = strategy;
1084 disconnect( d->m_monitor, SIGNAL(itemChanged(
Akonadi::Item,QSet<QByteArray>)),
1085 this, SLOT(monitoredItemChanged(
Akonadi::Item,QSet<QByteArray>)) );
1086 disconnect( d->m_monitor, SIGNAL(itemRemoved(
Akonadi::Item)),
1097 d->m_monitor->d_ptr->useRefCounting = ( strategy ==
LazyPopulation );
1105 return d->m_itemPopulation;
1111 d->beginResetModel();
1112 d->m_showRootCollection = include;
1119 return d->m_showRootCollection;
1125 d->m_rootCollectionDisplayName = displayName;
1133 return d->m_rootCollectionDisplayName;
1139 d->beginResetModel();
1140 d->m_collectionFetchStrategy = strategy;
1150 disconnect( d->m_monitor,
1163 return d->m_collectionFetchStrategy;
1166 static QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel *> proxiesAndModel(
const QAbstractItemModel *model )
1168 QList<const QAbstractProxyModel *> proxyChain;
1169 const QAbstractProxyModel *proxy = qobject_cast<
const QAbstractProxyModel *>( model );
1170 const QAbstractItemModel *_model = model;
1172 proxyChain.prepend( proxy );
1173 _model = proxy->sourceModel();
1174 proxy = qobject_cast<
const QAbstractProxyModel *>( _model );
1178 return qMakePair( proxyChain, etm );
1181 static QModelIndex proxiedIndex(
const QModelIndex &idx, QList<const QAbstractProxyModel *> proxyChain )
1183 QListIterator<const QAbstractProxyModel *> it( proxyChain );
1184 QModelIndex _idx = idx;
1185 while ( it.hasNext() ) {
1186 _idx = it.next()->mapFromSource( _idx );
1193 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1194 QModelIndex idx = pair.second->d_ptr->indexForCollection( collection );
1195 return proxiedIndex( idx, pair.first );
1200 QPair<QList<const QAbstractProxyModel *>,
const EntityTreeModel*> pair = proxiesAndModel( model );
1202 if ( !pair.second ) {
1203 kWarning() <<
"Couldn't find an EntityTreeModel";
1204 return QModelIndexList();
1207 QModelIndexList list = pair.second->d_ptr->indexesForItem( item );
1208 QModelIndexList proxyList;
1209 foreach (
const QModelIndex &idx, list ) {
1210 const QModelIndex pIdx = proxiedIndex( idx, pair.first );
1211 if ( pIdx.isValid() ) {
1218 #include "moc_entitytreemodel.cpp"