diff --git a/akonadi/kdescendantsproxymodel.cpp b/akonadi/kdescendantsproxymodel.cpp index ab3969eab..ac638c5f5 100644 --- a/akonadi/kdescendantsproxymodel.cpp +++ b/akonadi/kdescendantsproxymodel.cpp @@ -1,916 +1,922 @@ /* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdescendantsproxymodel_p.h" #include "kdebug.h" #include /** @internal Private implementation of DescendantsProxyModel. */ class KDescendantsProxyModelPrivate { public: KDescendantsProxyModelPrivate(KDescendantsProxyModel *model) : q_ptr(model), m_displayAncestorData( false ), m_ancestorSeparator( QLatin1String( " / " ) ) { } Q_DECLARE_PUBLIC( KDescendantsProxyModel ) KDescendantsProxyModel *q_ptr; /** @internal Returns the @p row -th descendant of sourceParent. For example, if the source model looks like: @code -> Item 0-0 (this is row-depth) -> -> Item 0-1 -> -> Item 1-1 -> -> -> Item 0-2 -> -> -> Item 1-2 -> Item 1-0 @endcode Then findSourceIndexForRow(3, index(Item 0-0)) will return an index for Item 1-2 @returns The model index in the source model. */ QModelIndex findSourceIndexForRow( int row, QModelIndex sourceParent) const; void processPendingParents() { } /** @internal Returns true if @p sourceIndex has a ancestor that is m_rootDescendIndex. Note that isDescended(m_rootDescendIndex) is false; @param sourceIndex The index in the source model. @returns Whether sourceIndex is an ancestor of rootIndex */ bool isDescended(const QModelIndex &sourceIndex) const; /** @internal Returns the number of descendants below @p sourceIndex. For example, if the source model looks like: @code -> Item 0-0 (this is row-depth) -> -> Item 0-1 -> -> Item 1-1 -> -> -> Item 0-2 -> -> -> Item 1-2 -> Item 1-0 @endcode The descendant count of the rootIndex would be 6, of Item 0-0 would be 4, of Item 1-1 would be 2, of Item 1-2 would be 0 etc. */ int descendantCount(const QModelIndex &sourceIndex, int ignoreTerminals=ObserveTerminals) const; enum TerminalIgnorance { ObserveTerminals, IgnoreTerminals }; /** @internal Returns the row of @p sourceIndex below the rootDescendIndex. For example, if the source model looks like: @code -> Item 0-0 (this is row-depth) -> -> Item 0-1 -> -> Item 1-1 -> -> -> Item 0-2 -> -> -> Item 1-2 -> Item 1-0 @endcode Then descendedRow(index(Item 0-0) would be 0, descendedRow(index(Item 0-1) would be 1, descendedRow(index(Item 0-2) would be 3, descendedRow(index(Item 1-0) would be 5 etc. @returns The row in the proxy model where @p sourceIndex is represented. */ int descendedRow(const QModelIndex &sourceIndex) const; QModelIndexList matchDescendants(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags, int until, const bool matchAll) const; enum Operation { InsertOperation, RemoveOperation }; void insertOrRemoveRows(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, int type); void sourceRowsAboutToBeInserted(const QModelIndex &, int start, int end); void sourceRowsInserted(const QModelIndex &, int start, int end); void sourceRowsAboutToBeRemoved(const QModelIndex &, int start, int end); void sourceRowsRemoved(const QModelIndex &, int start, int end); void sourceRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow); void sourceRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow); void sourceModelAboutToBeReset(); void sourceModelReset(); void sourceLayoutAboutToBeChanged(); void sourceLayoutChanged(); void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceModelDestroyed(); QPersistentModelIndex m_rootDescendIndex; // Hmm, if I make this QHash instead then moves are // automatically handled. Nope, they're not. deeper levels than base would // still need to be updated or calculated. mutable QHash m_descendantsCount; bool m_displayAncestorData; QString m_ancestorSeparator; QList m_terminalIndexes; void descendNewIndexes(); QList m_layoutChangePersistentIndexes; QModelIndexList m_proxyIndexes; }; KDescendantsProxyModel::KDescendantsProxyModel( QObject *parent ) : QAbstractProxyModel( parent ), d_ptr( new KDescendantsProxyModelPrivate(this) ) { Q_D(KDescendantsProxyModel); d->m_rootDescendIndex = QModelIndex(); } void KDescendantsProxyModel::setRootIndex(const QModelIndex &index) { Q_D(KDescendantsProxyModel); if (index.isValid()) Q_ASSERT(index.model() == sourceModel()); d->m_rootDescendIndex = index; d->m_descendantsCount.clear(); reset(); } KDescendantsProxyModel::~KDescendantsProxyModel() { Q_D(KDescendantsProxyModel); d->m_descendantsCount.clear(); delete d_ptr; } QModelIndex KDescendantsProxyModelPrivate::findSourceIndexForRow( int row, QModelIndex idx ) const { Q_Q( const KDescendantsProxyModel ); int childCount = q->sourceModel()->rowCount(idx); for (int childRow = 0; childRow < childCount; childRow++) { QModelIndex childIndex = q->sourceModel()->index(childRow, 0, idx); if (row == 0) { return childIndex; } row--; if (q->sourceModel()->hasChildren(childIndex)) { int childDesc = descendantCount(childIndex); if (childDesc > row) { return findSourceIndexForRow(row, childIndex); } row -= childDesc; } } // This should probably never happen if you use descendantCount before calling this method. return QModelIndex(); } void KDescendantsProxyModel::setSourceModel(QAbstractItemModel * sourceModel) { Q_D(KDescendantsProxyModel); disconnect( sourceModel, SIGNAL(modelReset()), this, SLOT( sourceModelReset() ) ); disconnect( sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset() ) ); disconnect( sourceModel, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()) ); disconnect( sourceModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()) ); disconnect( sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex & ) ) ); disconnect( sourceModel, SIGNAL(rowsInserted(const QModelIndex, int, int)), this, SLOT(sourceRowsInserted(const QModelIndex, int, int)) ); disconnect( sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex, int, int)), this, SLOT(sourceRowsAboutToBeInserted(const QModelIndex, int, int)) ); disconnect( sourceModel, SIGNAL(rowsRemoved(const QModelIndex, int, int)), this, SLOT(sourceRowsRemoved(const QModelIndex, int, int)) ); disconnect( sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex, int, int)), this, SLOT(sourceRowsAboutToBeRemoved(const QModelIndex, int, int)) ); disconnect( sourceModel, SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), this, SLOT(sourceRowsMoved(const QModelIndex, int, int, const QModelIndex, int)) ); disconnect( sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex, int, int, const QModelIndex, int)), this, SLOT(sourceRowsAboutToBeMoved(const QModelIndex, int, int, const QModelIndex, int) ) ); QAbstractProxyModel::setSourceModel( sourceModel ); connect( sourceModel, SIGNAL(modelReset()), SLOT( sourceModelReset() ) ); connect( sourceModel, SIGNAL(modelAboutToBeReset()), SLOT(sourceModelAboutToBeReset() ) ); connect( sourceModel, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()) ); connect( sourceModel, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()) ); connect( sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex & ) ) ); connect( sourceModel, SIGNAL(rowsInserted(const QModelIndex, int, int)), SLOT(sourceRowsInserted(const QModelIndex, int, int)) ); connect( sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex, int, int)), SLOT(sourceRowsAboutToBeInserted(const QModelIndex, int, int)) ); connect( sourceModel, SIGNAL(rowsRemoved(const QModelIndex, int, int)), SLOT(sourceRowsRemoved(const QModelIndex, int, int)) ); connect( sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex, int, int)), SLOT(sourceRowsAboutToBeRemoved(const QModelIndex, int, int)) ); connect( sourceModel, SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), SLOT(sourceRowsMoved(const QModelIndex, int, int, const QModelIndex, int)) ); connect( sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex, int, int, const QModelIndex, int)), SLOT(sourceRowsAboutToBeMoved(const QModelIndex, int, int, const QModelIndex, int) ) ); d->m_descendantsCount.clear(); reset(); } bool KDescendantsProxyModelPrivate::isDescended(const QModelIndex &sourceIndex) const { Q_Q(const KDescendantsProxyModel); if (sourceIndex == m_rootDescendIndex) { return false; } QModelIndex parentIndex = q->sourceModel()->parent(sourceIndex); if (parentIndex == m_rootDescendIndex) { return true; } bool found = false; forever { parentIndex = parentIndex.parent(); if (parentIndex == m_rootDescendIndex) { found = true; break; } if (!parentIndex.isValid()) break; } return found; } int KDescendantsProxyModelPrivate::descendedRow(const QModelIndex &sourceIndex) const { Q_Q(const KDescendantsProxyModel); QModelIndex parentIndex = sourceIndex.parent(); int row = sourceIndex.row(); for (int childRow = 0; childRow < sourceIndex.row(); childRow++ ) { QModelIndex childIndex = q->sourceModel()->index( childRow, sourceIndex.column(), parentIndex ); if (q->sourceModel()->hasChildren(childIndex)) row += descendantCount(childIndex); } if (parentIndex == m_rootDescendIndex) { // Return 0 instead of -1 for an invalid index. if (row < 0) { return 0; } return row; } else if(!parentIndex.isValid()) { // Should never happen. // Someone must have called this with sourceIndex outside of m_rootDescendIndex return 0; } else { int dr = descendedRow(parentIndex); return row + dr + 1; } } QModelIndex KDescendantsProxyModel::mapFromSource(const QModelIndex & sourceIndex) const { Q_D(const KDescendantsProxyModel); if (sourceIndex == d->m_rootDescendIndex) { return QModelIndex(); } if ( d->isDescended( sourceIndex ) ) { int row = d->descendedRow( sourceIndex ); if (row < 0) return QModelIndex(); return createIndex( row, sourceIndex.column() ); } else { return QModelIndex(); } } void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &sourceParentIndex, int start, int end) { insertOrRemoveRows(sourceParentIndex, start, end, InsertOperation); } void KDescendantsProxyModelPrivate::insertOrRemoveRows(const QModelIndex &sourceParentIndex, int start, int end, int operationType) { Q_Q(KDescendantsProxyModel); int c = descendedRow(sourceParentIndex); // Can't simply get the descendantCount of sourceModel()->index(start, 0, sourceParent), // because start might not exist as a child of sourceParent yet. // Maybe I should special case (!sourceParent.hasChildren) instead. // Only the first column can have child items. It is only those we need to count. const int column = 0; for (int childRow = 0; childRow < start; childRow++) { QModelIndex childIndex = q->sourceModel()->index( childRow, column, sourceParentIndex ); // kDebug() << childIndex << descendantCount(childIndex); if (q->sourceModel()->hasChildren(childIndex)) c += descendantCount(childIndex); } // @code // -> Item 0-0 (this is row-depth) // -> -> Item 0-1 // -> -> Item 1-1 // -> -> -> Item 0-2 // -> -> -> Item 1-2 // -> Item 1-0 // @endcode // // If the sourceModel reports that a row is inserted between Item 0-2 Item 1-2, // this methods receives a sourceParent of Item 1-1, and a start of 1. // It has a descendedRow of 2. The proxied start is the number of rows above parent, // and the start below parent. The parent itself must also be accounted for if it // is part of the model. // Check if it's descended instead? int proxy_start = c + start; int proxy_end = c + end; if (isDescended(sourceParentIndex)) { proxy_start++; proxy_end++; } if (operationType == InsertOperation) q->beginInsertRows(m_rootDescendIndex, proxy_start, proxy_end); else if (operationType == RemoveOperation) { // need to notify that we're also removing the descendants. for (int childRow = start; childRow <= end; childRow++) { QModelIndex childIndex = q->sourceModel()->index(childRow, column, sourceParentIndex); if (q->sourceModel()->hasChildren(childIndex)) proxy_end += descendantCount(childIndex); } Q_ASSERT( proxy_end <= q->rowCount() ); q->beginRemoveRows(m_rootDescendIndex, proxy_start, proxy_end); } } void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &sourceParentIndex, int start, int end) { Q_Q(KDescendantsProxyModel); m_descendantsCount.clear(); // Don't count the descendants of newly inserted indexes until after endInsertRows. // The reason for this is that at the time we emitted beginInsertRows, we couldn't count the descendants // of new items (they weren't known to the sourceModel), and now we can. const int column = 0; for (int childRow = start; childRow <= end; childRow++) { QModelIndex childIndex = q->sourceModel()->index( childRow, column, sourceParentIndex ); m_terminalIndexes << QPersistentModelIndex(childIndex); } q->endInsertRows(); descendNewIndexes(); } void KDescendantsProxyModelPrivate::descendNewIndexes() { Q_Q(KDescendantsProxyModel); QMutableListIterator i(m_terminalIndexes); while (i.hasNext()) { QModelIndex idx = i.next(); int descCount = descendantCount(idx, IgnoreTerminals); if (descCount <= 0) { i.remove(); continue; } // if descCount > 0, this is a new index which has new child indexes. int proxyStart = descendedRow(idx); // Need to ignore terminals so we know how many rows will be inserted by removing them. int proxyEnd = proxyStart + descendantCount(idx, IgnoreTerminals); if (isDescended(idx)) { proxyStart++; proxyEnd++; } q->beginInsertRows(QModelIndex(), proxyStart, proxyEnd - 1 ); i.remove(); m_descendantsCount.clear(); q->endInsertRows(); } } void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow) { Q_Q(KDescendantsProxyModel); int c = descendedRow(parent); int d = descendedRow(destParent); bool allowMove = q->beginMoveRows(QModelIndex(), c+1+start, c+1+end, QModelIndex(), d+1+destRow); Q_UNUSED(allowMove); Q_ASSERT(allowMove); } void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &sourceParentIndex, int start, int end, const QModelIndex &destParentIndex, int destRow) { Q_Q(KDescendantsProxyModel); Q_UNUSED(sourceParentIndex); Q_UNUSED(start); Q_UNUSED(end); Q_UNUSED(destParentIndex); Q_UNUSED(destRow); m_descendantsCount.clear(); q->endMoveRows(); } void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { insertOrRemoveRows(parent, start, end, RemoveOperation); } void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &sourceParentIndex, int start, int end) { Q_Q(KDescendantsProxyModel); Q_UNUSED(sourceParentIndex); Q_UNUSED(start); Q_UNUSED(end); m_descendantsCount.clear(); q->endRemoveRows(); } void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset() { Q_Q(KDescendantsProxyModel); q->beginResetModel(); } void KDescendantsProxyModelPrivate::sourceModelReset() { Q_Q(KDescendantsProxyModel); m_descendantsCount.clear(); q->endResetModel(); } void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged() { Q_Q(KDescendantsProxyModel); emit q->layoutAboutToBeChanged(); foreach(const QPersistentModelIndex &proxyPersistentIndex, q->persistentIndexList()) { m_proxyIndexes << proxyPersistentIndex; m_layoutChangePersistentIndexes << QPersistentModelIndex(q->mapToSource(proxyPersistentIndex)); } } void KDescendantsProxyModelPrivate::sourceLayoutChanged() { Q_Q(KDescendantsProxyModel); for(int i = 0; i < m_proxyIndexes.size(); ++i) { q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i))); } m_layoutChangePersistentIndexes.clear(); m_proxyIndexes.clear(); m_descendantsCount.clear(); emit q->layoutChanged(); } QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const { Q_D(const KDescendantsProxyModel); if (!proxyIndex.isValid()) return d->m_rootDescendIndex; if (proxyIndex.column() >= sourceModel()->columnCount()) return QModelIndex(); QModelIndex idx = d->findSourceIndexForRow( proxyIndex.row(), d->m_rootDescendIndex ); if (proxyIndex.column() > 0) { return sourceModel()->index(idx.row(), proxyIndex.column(), idx.parent()); } return idx; } QVariant KDescendantsProxyModel::data(const QModelIndex & index, int role) const { Q_D(const KDescendantsProxyModel ); if (!sourceModel()) return QVariant(); if (!index.isValid()) return sourceModel()->data(index, role); QModelIndex sourceIndex = mapToSource( index ); if ((d->m_displayAncestorData) && ( role == Qt::DisplayRole ) ) { if (!sourceIndex.isValid()) { return QVariant(); } QString displayData = sourceIndex.data().toString(); sourceIndex = sourceIndex.parent(); while (sourceIndex.isValid()) { displayData.prepend(d->m_ancestorSeparator); displayData.prepend(sourceIndex.data().toString()); sourceIndex = sourceIndex.parent(); } return displayData; } else { return sourceIndex.data(role); } } QMimeData* KDescendantsProxyModel::mimeData( const QModelIndexList & indexes ) const { Q_ASSERT(sourceModel()); QModelIndexList sourceIndexes; foreach(const QModelIndex& index, indexes) sourceIndexes << mapToSource(index); return sourceModel()->mimeData(sourceIndexes); } QStringList KDescendantsProxyModel::mimeTypes() const { Q_ASSERT(sourceModel()); return sourceModel()->mimeTypes(); } bool KDescendantsProxyModel::hasChildren ( const QModelIndex & parent ) const { return rowCount(parent) > 0; } int KDescendantsProxyModel::rowCount(const QModelIndex & proxyIndex) const { Q_D(const KDescendantsProxyModel ); if (!sourceModel() || proxyIndex.column() > 0) return 0; QModelIndex sourceIndex = mapToSource(proxyIndex); if (sourceIndex == d->m_rootDescendIndex) { int c = d->descendantCount(sourceIndex); return c; } return 0; } void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { Q_Q( KDescendantsProxyModel ); int topRow = topLeft.row(); int bottomRow = bottomRight.row(); for(int i = topRow; i <= bottomRow; ++i) { QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent()); QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft); // TODO. If an index does not have any descendants, then we can emit in blocks of rows. // As it is we emit once for each row. QModelIndex sourceBottomRight = q->sourceModel()->index(i, bottomRight.column(), bottomRight.parent()); QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight); emit q->dataChanged(proxyTopLeft, proxyBottomRight); } } int KDescendantsProxyModelPrivate::descendantCount(const QModelIndex &sourceIndex, int ignoreTerminals) const { if (sourceIndex.column() > 0) return 0; if (ObserveTerminals == ignoreTerminals) { if (m_terminalIndexes.contains(sourceIndex)) { return 0; } } Q_Q( const KDescendantsProxyModel ); if (m_descendantsCount.contains(sourceIndex.internalId())) { return m_descendantsCount.value(sourceIndex.internalId()); } int sourceIndexRowCount = q->sourceModel()->rowCount(sourceIndex); if (sourceIndexRowCount == 0) return 0; int c = 0; c += sourceIndexRowCount; int childRow = 0; QModelIndex childIndex = q->sourceModel()->index(childRow, 0, sourceIndex); while (childIndex.isValid()) { c += descendantCount(childIndex); childRow++; childIndex = q->sourceModel()->index(childRow, 0, sourceIndex); } m_descendantsCount.insert( sourceIndex.internalId(), c ); return c; } QModelIndex KDescendantsProxyModel::index(int r, int c, const QModelIndex& parent) const { Q_D(const KDescendantsProxyModel ); if (!sourceModel()) return QModelIndex(); if ( (r < 0) || (c < 0) || (c >= sourceModel()->columnCount() ) ) return QModelIndex(); if ( r >= d->descendantCount(parent) ) return QModelIndex(); // TODO: Use is decended instead? if (parent.isValid()) return QModelIndex(); return createIndex(r, c); } QModelIndex KDescendantsProxyModel::parent(const QModelIndex& proxyIndex) const { Q_UNUSED(proxyIndex); return QModelIndex(); } int KDescendantsProxyModel::columnCount(const QModelIndex &index) const { Q_UNUSED( index ); if (!sourceModel()) return 0; return sourceModel()->columnCount(); } void KDescendantsProxyModel::setDisplayAncestorData( bool display ) { Q_D(KDescendantsProxyModel); d->m_displayAncestorData = display; } bool KDescendantsProxyModel::displayAncestorData() const { Q_D(const KDescendantsProxyModel ); return d->m_displayAncestorData; } void KDescendantsProxyModel::setAncestorSeparator( const QString &separator ) { Q_D(KDescendantsProxyModel); d->m_ancestorSeparator = separator; } QString KDescendantsProxyModel::ancestorSeparator() const { Q_D(const KDescendantsProxyModel ); return d->m_ancestorSeparator; } Qt::ItemFlags KDescendantsProxyModel::flags( const QModelIndex &index ) const { // if index is invalid, it might be mapped to a valid source index with more flags. // Can't allow that... if (!index.isValid()) return 0; return QAbstractProxyModel::flags(index); } QModelIndexList KDescendantsProxyModelPrivate::matchDescendants(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags, int until, const bool matchAll) const { Q_Q(const KDescendantsProxyModel); QModelIndexList matches; if (!start.isValid()) return matches; const int column = start.column(); const int firstRow = 0; QModelIndex idx = start; while (idx.row() <= until) { Q_ASSERT(idx.isValid()); if (q->sourceModel()->hasChildren(idx)) { QModelIndex firstChild = idx.child(firstRow, column); matches << q->match(q->mapFromSource(firstChild), role, value, hits, flags); if (!matchAll && (matches.size() >= hits)) { return matches.mid(0, hits); } } int row = idx.row(); if (row == until) { break; } idx = idx.sibling(row + 1, column); } return matches; } QModelIndexList KDescendantsProxyModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const { Q_D(const KDescendantsProxyModel ); // We only really need to do all this for the AmazingCompletionRole, but there's no clean way to // determine what that is. QModelIndexList sourceList; QModelIndexList proxyList; QModelIndex beginIndex = start; QModelIndex sourceStart = mapToSource(start); QModelIndex parent = sourceModel()->parent(sourceStart); int parentRowCount = sourceModel()->rowCount(sourceModel()->parent(sourceStart)); const bool matchAll = (hits == -1); const int firstHit = 1; const int column = start.column(); const int proxyRowCount = rowCount(); Q_ASSERT(sourceStart.column() == start.column()); sourceList = sourceModel()->match(sourceStart, role, value, firstHit, flags); int lastRow; if (sourceList.isEmpty()) { lastRow = parentRowCount - 1; proxyList = d->matchDescendants(mapToSource(start), role, value, hits, flags, lastRow, matchAll); if (matchAll) return proxyList; return proxyList.mid(0, hits); } else { forever { QModelIndex firstIndexHit; if (sourceList.isEmpty()) { lastRow = parentRowCount - 1; } else { firstIndexHit = sourceList.first(); Q_ASSERT(firstIndexHit.column() == start.column()); lastRow = firstIndexHit.row() - 1; } proxyList << d->matchDescendants(sourceStart, role, value, hits, flags, lastRow, matchAll); if (sourceList.isEmpty()) break; QModelIndex proxyFirst = mapFromSource(firstIndexHit); proxyList << proxyFirst; if (!matchAll && ( proxyList.size() >= hits)) { return proxyList.mid(0, hits); } if (proxyFirst.row() == proxyRowCount - 1) break; sourceStart = mapToSource(index(proxyFirst.row() + 1, proxyFirst.column())); Q_ASSERT(sourceStart.isValid()); sourceList = sourceModel()->match(sourceStart, role, value, firstHit, flags); } } QModelIndex nextStart = start.sibling(start.row() + parentRowCount + 1, column); if (nextStart.isValid()) proxyList << match(nextStart, role, value, hits, flags); if (matchAll) return proxyList; return proxyList.mid(0, hits); } Qt::DropActions KDescendantsProxyModel::supportedDropActions() const { Q_ASSERT(sourceModel()); return sourceModel()->supportedDropActions(); } +void KDescendantsProxyModelPrivate::sourceModelDestroyed() +{ + +} + #include "moc_kdescendantsproxymodel_p.cpp" diff --git a/akonadi/kdescendantsproxymodel_p.h b/akonadi/kdescendantsproxymodel_p.h index b3c10b638..a6b9c994d 100644 --- a/akonadi/kdescendantsproxymodel_p.h +++ b/akonadi/kdescendantsproxymodel_p.h @@ -1,213 +1,214 @@ /* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDESCENDANTSPROXYMODEL_P_H #define KDESCENDANTSPROXYMODEL_P_H #include class KDescendantsProxyModelPrivate; /** @brief Proxy Model for restructuring a Tree into a list. A KDescendantsProxyModel may be used to alter how the items in the tree are presented. Given a model which is represented as a tree: \image html entitytreemodel.png "A plain EntityTreeModel in a view" The KDescendantsProxyModel restructures the sourceModel to represent it as a flat list. @code // ... Create an entityTreeModel KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this); descProxy->setSourceModel(entityTree); view->setModel(descProxy); @endcode \image html descendantentitiesproxymodel.png "A KDescendantsProxyModel." KDescendantEntitiesProxyModel can also display the ancestors of the index in the source model as part of its display. @code // ... Create an entityTreeModel KDescendantsProxyModel *descProxy = new KDescendantesProxyModel(this); descProxy->setSourceModel(entityTree); // #### This is new descProxy->setDisplayAncestorData(true, QString( " / " )); view->setModel(descProxy); @endcode \image html descendantentitiesproxymodel-withansecnames.png "A KDescendantsProxyModel with ancestor names." @since 4.4 @author Stephen Kelly */ class KDescendantsProxyModel : public QAbstractProxyModel { Q_OBJECT public: /** * Creates a new descendant entities proxy model. * * @param parent The parent object. */ explicit KDescendantsProxyModel( QObject *parent = 0 ); /** * Destroys the descendant entities proxy model. */ virtual ~KDescendantsProxyModel(); /** * Sets the source @p model of the proxy. */ virtual void setSourceModel( QAbstractItemModel *model ); /** * Sets the root index to @p index. This is the root of the proxy model. * * @param index The root index in the *source* model which will be shown in this model. * If the index is invalid, the model is empty. * * \note You must set the model before setting the root index. */ void setRootIndex( const QModelIndex &index); /** * Set whether to show ancestor data in the model. If @p display is true, then * a source model which is displayed as * * @code * -> "Item 0-0" (this is row-depth) * -> -> "Item 0-1" * -> -> "Item 1-1" * -> -> -> "Item 0-2" * -> -> -> "Item 1-2" * -> "Item 1-0" * @endcode * * will be displayed as * * @code * -> *Item 0-0" * -> "Item 0-0 / Item 0-1" * -> "Item 0-0 / Item 1-1" * -> "Item 0-0 / Item 1-1 / Item 0-2" * -> "Item 0-0 / Item 1-1 / Item 1-2" * -> "Item 1-0" * @endcode * * If @p display is false, the proxy will show * * @code * -> *Item 0-0" * -> "Item 0-1" * -> "Item 1-1" * -> "Item 0-2" * -> "Item 1-2" * -> "Item 1-0" * @endcode * * Default is false. */ void setDisplayAncestorData( bool display ); /** * Whether ancestor data will be displayed. */ bool displayAncestorData() const; /** * Sets the ancestor @p separator used between data of ancestors. */ void setAncestorSeparator( const QString &separator ); /** * Separator used between data of ancestors. */ QString ancestorSeparator() const; /** * Returns the number of descendant entries for the given model @p index. */ int descendantCount( const QModelIndex &index ) const; QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const; QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const; virtual Qt::ItemFlags flags( const QModelIndex &index ) const; QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; virtual QMimeData* mimeData( const QModelIndexList & indexes ) const; virtual QStringList mimeTypes() const; virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex index(int, int, const QModelIndex &parent = QModelIndex() ) const; virtual QModelIndex parent(const QModelIndex&) const; virtual int columnCount(const QModelIndex& index = QModelIndex()) const; virtual Qt::DropActions supportedDropActions() const; /** Reimplemented to match all descendants. */ virtual QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags( Qt::MatchStartsWith | Qt::MatchWrap ) ) const; private: Q_DECLARE_PRIVATE( KDescendantsProxyModel ) //@cond PRIVATE KDescendantsProxyModelPrivate *d_ptr; Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &, int, int)) Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &, int, int)) Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int)) Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &, int, int)) Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)) Q_PRIVATE_SLOT(d_func(), void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)) Q_PRIVATE_SLOT(d_func(), void sourceModelAboutToBeReset()) Q_PRIVATE_SLOT(d_func(), void sourceModelReset()) Q_PRIVATE_SLOT(d_func(), void sourceLayoutAboutToBeChanged()) Q_PRIVATE_SLOT(d_func(), void sourceLayoutChanged()) Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &, const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void sourceModelDestroyed()) Q_PRIVATE_SLOT(d_func(), void processPendingParents()) // Make these private, they shouldn't be called by applications // virtual bool insertRows(int , int, const QModelIndex & = QModelIndex()); // virtual bool insertColumns(int, int, const QModelIndex & = QModelIndex()); // virtual bool removeRows(int, int, const QModelIndex & = QModelIndex()); // virtual bool removeColumns(int, int, const QModelIndex & = QModelIndex()); //@endcond }; #endif