From: Peter Penz Date: Tue, 14 Feb 2012 17:04:47 +0000 (+0100) Subject: Details view: Fix indicator-branches X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/0397658b81ce371047d9ce6979aa37d8112f1a2c?ds=inline Details view: Fix indicator-branches Up to now no indicator-branches have been drawn when showing a tree. The patch fixes this so that that the style-dependent branches are drawn. The main part of the patch is the implementation of KItemListView::updateSiblingsInformation(). Most of the other changes are related due to an internal renaming of the expansionsLevel-role to expandedParentsCount and some related cleanups. BUG: 290276 FIXED-IN: 4.8.1 --- diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index f8a58ae51..03f379837 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -515,8 +515,8 @@ QSizeF KFileItemListView::visibleRoleSizeHint(int index, const QByteArray& role) if (role == "name") { // Increase the width by the expansion-toggle and the current expansion level - const int expansionLevel = values.value("expansionLevel", 0).toInt(); - width += option.padding + expansionLevel * itemSize().height() + KIconLoader::SizeSmall; + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + width += option.padding + expandedParentsCount * itemSize().height() + KIconLoader::SizeSmall; // Increase the width by the required space for the icon width += option.padding * 2 + option.iconSize; @@ -582,7 +582,7 @@ void KFileItemListView::applyRolesToModel() if (m_itemLayout == DetailsLayout) { roles.insert("isExpanded"); roles.insert("isExpandable"); - roles.insert("expansionLevel"); + roles.insert("expandedParentsCount"); } // Assure that the role that is used for sorting will be determined diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 64cc8b449..dc1554dab 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -94,13 +94,8 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte KItemListWidget::paint(painter, option, widget); - // Draw expansion toggle '>' or 'V' - if (m_isExpandable && !m_expansionArea.isEmpty()) { - QStyleOption arrowOption; - arrowOption.rect = m_expansionArea.toRect(); - const QStyle::PrimitiveElement arrow = data()["isExpanded"].toBool() - ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight; - style()->drawPrimitive(arrow, &arrowOption, painter); + if (!m_expansionArea.isEmpty()) { + drawSiblingsInformation(painter); } const KItemListStyleOption& itemListStyleOption = styleOption(); @@ -309,7 +304,8 @@ QPixmap KFileItemListWidget::overlay() const void KFileItemListWidget::dataChanged(const QHash& current, const QSet& roles) { - KItemListWidget::dataChanged(current, roles); + Q_UNUSED(current); + m_dirtyContent = true; QSet dirtyRoles; @@ -331,7 +327,7 @@ void KFileItemListWidget::dataChanged(const QHash& current void KFileItemListWidget::visibleRolesChanged(const QList& current, const QList& previous) { - KItemListWidget::visibleRolesChanged(current, previous); + Q_UNUSED(previous); m_sortedVisibleRoles = current; m_dirtyLayout = true; } @@ -339,14 +335,16 @@ void KFileItemListWidget::visibleRolesChanged(const QList& current, void KFileItemListWidget::visibleRolesSizesChanged(const QHash& current, const QHash& previous) { - KItemListWidget::visibleRolesSizesChanged(current, previous); + Q_UNUSED(current); + Q_UNUSED(previous); m_dirtyLayout = true; } void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) { - KItemListWidget::styleOptionChanged(current, previous); + Q_UNUSED(current); + Q_UNUSED(previous); updateAdditionalInfoTextColor(); m_dirtyLayout = true; } @@ -363,6 +361,14 @@ void KFileItemListWidget::selectedChanged(bool selected) updateAdditionalInfoTextColor(); } +void KFileItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + + void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) { KItemListWidget::resizeEvent(event); @@ -428,15 +434,14 @@ void KFileItemListWidget::updateExpansionArea() { if (m_layout == DetailsLayout) { const QHash values = data(); - Q_ASSERT(values.contains("expansionLevel")); - const KItemListStyleOption& option = styleOption(); - const int expansionLevel = values.value("expansionLevel", 0).toInt(); - if (expansionLevel >= 0) { + Q_ASSERT(values.contains("expandedParentsCount")); + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + if (expandedParentsCount >= 0) { const qreal widgetHeight = size().height(); - const qreal expansionLevelSize = KIconLoader::SizeSmall; - const qreal x = option.padding + expansionLevel * widgetHeight; - const qreal y = (widgetHeight - expansionLevelSize) / 2; - m_expansionArea = QRectF(x, y, expansionLevelSize, expansionLevelSize); + const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2; + const qreal x = expandedParentsCount * widgetHeight + inc; + const qreal y = inc; + m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall); return; } } @@ -746,7 +751,8 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() const int fontHeight = option.fontMetrics.height(); const qreal columnPadding = option.padding * 3; - const qreal firstColumnInc = m_expansionArea.right() + option.padding * 2 + scaledIconSize; + const qreal firstColumnInc = (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2 + + scaledIconSize; qreal x = firstColumnInc; const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); @@ -831,6 +837,37 @@ void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap) } } +void KFileItemListWidget::drawSiblingsInformation(QPainter* painter) +{ + const int siblingSize = size().height(); + const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; + QRect siblingRect(x, 0, siblingSize, siblingSize); + + QStyleOption option; + bool isItemSibling = true; + + const QBitArray siblings = siblingsInformation(); + for (int i = siblings.count() - 1; i >= 0; --i) { + option.rect = siblingRect; + option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; + + if (isItemSibling) { + option.state |= QStyle::State_Item; + if (m_isExpandable) { + option.state |= QStyle::State_Children; + } + if (data()["isExpanded"].toBool()) { + option.state |= QStyle::State_Open; + } + isItemSibling = false; + } + + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); + + siblingRect.translate(-siblingRect.width(), 0); + } +} + QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size) { const KIcon icon(name); diff --git a/src/kitemviews/kfileitemlistwidget.h b/src/kitemviews/kfileitemlistwidget.h index 4a44a8468..afb0f0171 100644 --- a/src/kitemviews/kfileitemlistwidget.h +++ b/src/kitemviews/kfileitemlistwidget.h @@ -86,6 +86,7 @@ protected: virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); virtual void hoveredChanged(bool hovered); virtual void selectedChanged(bool selected); + virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); virtual void resizeEvent(QGraphicsSceneResizeEvent* event); virtual void showEvent(QShowEvent* event); virtual void hideEvent(QHideEvent* event); @@ -119,6 +120,7 @@ private: void updateAdditionalInfoTextColor(); void drawPixmap(QPainter* painter, const QPixmap& pixmap); + void drawSiblingsInformation(QPainter* painter); static QPixmap pixmapForIcon(const QString& name, int size); static TextId roleTextId(const QByteArray& role); diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index e0ae03302..f9ba397fa 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -50,7 +50,7 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : m_pendingItemsToInsert(), m_pendingEmitLoadingCompleted(false), m_groups(), - m_rootExpansionLevel(UninitializedRootExpansionLevel), + m_expandedParentsCountRoot(UninitializedExpandedParentsCountRoot), m_expandedUrls(), m_urlsToExpand() { @@ -265,22 +265,22 @@ QString KFileItemModel::roleDescription(const QByteArray& role) const QString descr; switch (roleIndex(role)) { - case NameRole: descr = i18nc("@item:intable", "Name"); break; - case SizeRole: descr = i18nc("@item:intable", "Size"); break; - case DateRole: descr = i18nc("@item:intable", "Date"); break; - case PermissionsRole: descr = i18nc("@item:intable", "Permissions"); break; - case OwnerRole: descr = i18nc("@item:intable", "Owner"); break; - case GroupRole: descr = i18nc("@item:intable", "Group"); break; - case TypeRole: descr = i18nc("@item:intable", "Type"); break; - case DestinationRole: descr = i18nc("@item:intable", "Destination"); break; - case PathRole: descr = i18nc("@item:intable", "Path"); break; - case CommentRole: descr = i18nc("@item:intable", "Comment"); break; - case TagsRole: descr = i18nc("@item:intable", "Tags"); break; - case RatingRole: descr = i18nc("@item:intable", "Rating"); break; - case NoRole: break; - case IsDirRole: break; - case IsExpandedRole: break; - case ExpansionLevelRole: break; + case NameRole: descr = i18nc("@item:intable", "Name"); break; + case SizeRole: descr = i18nc("@item:intable", "Size"); break; + case DateRole: descr = i18nc("@item:intable", "Date"); break; + case PermissionsRole: descr = i18nc("@item:intable", "Permissions"); break; + case OwnerRole: descr = i18nc("@item:intable", "Owner"); break; + case GroupRole: descr = i18nc("@item:intable", "Group"); break; + case TypeRole: descr = i18nc("@item:intable", "Type"); break; + case DestinationRole: descr = i18nc("@item:intable", "Destination"); break; + case PathRole: descr = i18nc("@item:intable", "Path"); break; + case CommentRole: descr = i18nc("@item:intable", "Comment"); break; + case TagsRole: descr = i18nc("@item:intable", "Tags"); break; + case RatingRole: descr = i18nc("@item:intable", "Rating"); break; + case NoRole: break; + case IsDirRole: break; + case IsExpandedRole: break; + case ExpandedParentsCountRole: break; default: Q_ASSERT(false); break; } @@ -295,22 +295,22 @@ QList > KFileItemModel::groups() const timer.start(); #endif switch (roleIndex(sortRole())) { - case NameRole: m_groups = nameRoleGroups(); break; - case SizeRole: m_groups = sizeRoleGroups(); break; - case DateRole: m_groups = dateRoleGroups(); break; - case PermissionsRole: m_groups = permissionRoleGroups(); break; - case OwnerRole: m_groups = genericStringRoleGroups("owner"); break; - case GroupRole: m_groups = genericStringRoleGroups("group"); break; - case TypeRole: m_groups = genericStringRoleGroups("type"); break; - case DestinationRole: m_groups = genericStringRoleGroups("destination"); break; - case PathRole: m_groups = genericStringRoleGroups("path"); break; - case CommentRole: m_groups = genericStringRoleGroups("comment"); break; - case TagsRole: m_groups = genericStringRoleGroups("tags"); break; - case RatingRole: m_groups = ratingRoleGroups(); break; - case NoRole: break; - case IsDirRole: break; - case IsExpandedRole: break; - case ExpansionLevelRole: break; + case NameRole: m_groups = nameRoleGroups(); break; + case SizeRole: m_groups = sizeRoleGroups(); break; + case DateRole: m_groups = dateRoleGroups(); break; + case PermissionsRole: m_groups = permissionRoleGroups(); break; + case OwnerRole: m_groups = genericStringRoleGroups("owner"); break; + case GroupRole: m_groups = genericStringRoleGroups("group"); break; + case TypeRole: m_groups = genericStringRoleGroups("type"); break; + case DestinationRole: m_groups = genericStringRoleGroups("destination"); break; + case PathRole: m_groups = genericStringRoleGroups("path"); break; + case CommentRole: m_groups = genericStringRoleGroups("comment"); break; + case TagsRole: m_groups = genericStringRoleGroups("tags"); break; + case RatingRole: m_groups = ratingRoleGroups(); break; + case NoRole: break; + case IsDirRole: break; + case IsExpandedRole: break; + case ExpandedParentsCountRole: break; default: Q_ASSERT(false); break; } @@ -375,8 +375,8 @@ void KFileItemModel::setRoles(const QSet& roles) m_roles = roles; if (count() > 0) { - const bool supportedExpanding = m_requestRole[ExpansionLevelRole]; - const bool willSupportExpanding = roles.contains("expansionLevel"); + const bool supportedExpanding = m_requestRole[ExpandedParentsCountRole]; + const bool willSupportExpanding = roles.contains("expandedParentsCount"); if (supportedExpanding && !willSupportExpanding) { // No expanding is supported anymore. Take care to delete all items that have an expansion level // that is not 0 (and hence are part of an expanded item). @@ -439,9 +439,9 @@ bool KFileItemModel::setExpanded(int index, bool expanded) } KFileItemList itemsToRemove; - const int expansionLevel = data(index)["expansionLevel"].toInt(); + const int expandedParentsCount = data(index)["expandedParentsCount"].toInt(); ++index; - while (index < count() && data(index)["expansionLevel"].toInt() > expansionLevel) { + while (index < count() && data(index)["expandedParentsCount"].toInt() > expandedParentsCount) { itemsToRemove.append(m_itemData.at(index)->item); ++index; } @@ -468,6 +468,14 @@ bool KFileItemModel::isExpandable(int index) const return false; } +int KFileItemModel::expandedParentsCount(int index) const +{ + if (index >= 0 && index < count()) { + return m_itemData.at(index)->values.value("expandedParentsCount").toInt(); + } + return 0; +} + QSet KFileItemModel::expandedUrls() const { return m_expandedUrls; @@ -687,7 +695,7 @@ void KFileItemModel::slotNewItems(const KFileItemList& items) { Q_ASSERT(!items.isEmpty()); - if (m_requestRole[ExpansionLevelRole] && m_rootExpansionLevel >= 0) { + if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) { // To be able to compare whether the new items may be inserted as children // of a parent item the pending items must be added to the model first. dispatchPendingItemsToInsert(); @@ -747,7 +755,7 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items) dispatchPendingItemsToInsert(); KFileItemList itemsToRemove = items; - if (m_requestRole[ExpansionLevelRole] && m_rootExpansionLevel >= 0) { + if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) { // Assure that removing a parent item also results in removing all children foreach (const KFileItem& item, items) { itemsToRemove.append(childItems(item)); @@ -842,7 +850,7 @@ void KFileItemModel::slotClear() m_resortAllItemsTimer->stop(); m_pendingItemsToInsert.clear(); - m_rootExpansionLevel = UninitializedRootExpansionLevel; + m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot; const int removedCount = m_itemData.count(); if (removedCount > 0) { @@ -1031,7 +1039,7 @@ void KFileItemModel::removeItems(const KFileItemList& items) } if (count() <= 0) { - m_rootExpansionLevel = UninitializedRootExpansionLevel; + m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot; } itemRanges << KItemRange(removedAtIndex, removedCount); @@ -1049,8 +1057,8 @@ QList KFileItemModel::createItemDataList(const KFileI itemData->values = retrieveData(item); itemData->parent = 0; - const bool determineParent = m_requestRole[ExpansionLevelRole] - && itemData->values["expansionLevel"].toInt() > 0; + const bool determineParent = m_requestRole[ExpandedParentsCountRole] + && itemData->values["expandedParentsCount"].toInt() > 0; if (determineParent) { KUrl parentUrl = item.url().upUrl(); parentUrl.adjustPath(KUrl::RemoveTrailingSlash); @@ -1075,17 +1083,17 @@ void KFileItemModel::removeExpandedItems() const int maxIndex = m_itemData.count() - 1; for (int i = 0; i <= maxIndex; ++i) { const ItemData* itemData = m_itemData.at(i); - if (itemData->values.value("expansionLevel").toInt() > 0) { + if (itemData->values.value("expandedParentsCount").toInt() > 0) { expandedItems.append(itemData->item); } } - // The m_rootExpansionLevel may not get reset before all items with - // a bigger expansionLevel have been removed. - Q_ASSERT(m_rootExpansionLevel >= 0); + // The m_expandedParentsCountRoot may not get reset before all items with + // a bigger count have been removed. + Q_ASSERT(m_expandedParentsCountRoot >= 0); removeItems(expandedItems); - m_rootExpansionLevel = UninitializedRootExpansionLevel; + m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot; m_expandedUrls.clear(); } @@ -1115,7 +1123,7 @@ KFileItemModel::Role KFileItemModel::roleIndex(const QByteArray& role) const rolesHash.insert("isDir", IsDirRole); rolesHash.insert("isExpanded", IsExpandedRole); rolesHash.insert("isExpandable", IsExpandableRole); - rolesHash.insert("expansionLevel", ExpansionLevelRole); + rolesHash.insert("expandedParentsCount", ExpandedParentsCountRole); } return rolesHash.value(role, NoRole); } @@ -1139,7 +1147,7 @@ QByteArray KFileItemModel::roleByteArray(Role role) const "isDir", "isExpanded", "isExpandable", - "expansionLevel" + "expandedParentsCount" }; return roles[role]; } @@ -1219,28 +1227,28 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) data.insert("isExpandable", item.isDir() && item.url() == item.targetUrl()); } - if (m_requestRole[ExpansionLevelRole]) { - if (m_rootExpansionLevel == UninitializedRootExpansionLevel && m_dirLister.data()) { + if (m_requestRole[ExpandedParentsCountRole]) { + if (m_expandedParentsCountRoot == UninitializedExpandedParentsCountRoot && m_dirLister.data()) { const KUrl rootUrl = m_dirLister.data()->url(); const QString protocol = rootUrl.protocol(); - const bool forceRootExpansionLevel = (protocol == QLatin1String("trash") || - protocol == QLatin1String("nepomuk") || - protocol == QLatin1String("remote") || - protocol.contains(QLatin1String("search"))); - if (forceRootExpansionLevel) { - m_rootExpansionLevel = ForceRootExpansionLevel; + const bool forceExpandedParentsCountRoot = (protocol == QLatin1String("trash") || + protocol == QLatin1String("nepomuk") || + protocol == QLatin1String("remote") || + protocol.contains(QLatin1String("search"))); + if (forceExpandedParentsCountRoot) { + m_expandedParentsCountRoot = ForceExpandedParentsCountRoot; } else { const QString rootDir = rootUrl.path(KUrl::AddTrailingSlash); - m_rootExpansionLevel = rootDir.count('/'); + m_expandedParentsCountRoot = rootDir.count('/'); } } - if (m_rootExpansionLevel == ForceRootExpansionLevel) { - data.insert("expansionLevel", -1); + if (m_expandedParentsCountRoot == ForceExpandedParentsCountRoot) { + data.insert("expandedParentsCount", -1); } else { const QString dir = item.url().directory(KUrl::AppendTrailingSlash); - const int level = dir.count('/') - m_rootExpansionLevel; - data.insert("expansionLevel", level); + const int level = dir.count('/') - m_expandedParentsCountRoot; + data.insert("expandedParentsCount", level); } } @@ -1259,8 +1267,8 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const { int result = 0; - if (m_rootExpansionLevel >= 0) { - result = expansionLevelsCompare(a, b); + if (m_expandedParentsCountRoot >= 0) { + result = expandedParentsCountCompare(a, b); if (result != 0) { // The items have parents with different expansion levels return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; @@ -1528,7 +1536,7 @@ int KFileItemModel::stringCompare(const QString& a, const QString& b) const : QString::compare(a, b, Qt::CaseSensitive); } -int KFileItemModel::expansionLevelsCompare(const ItemData* a, const ItemData* b) const +int KFileItemModel::expandedParentsCountCompare(const ItemData* a, const ItemData* b) const { const KUrl urlA = a->item.url(); const KUrl urlB = b->item.url(); @@ -1935,9 +1943,9 @@ KFileItemList KFileItemModel::childItems(const KFileItem& item) const int index = m_items.value(item.url(), -1); if (index >= 0) { - const int parentLevel = m_itemData.at(index)->values.value("expansionLevel").toInt(); + const int parentLevel = m_itemData.at(index)->values.value("expandedParentsCount").toInt(); ++index; - while (index < m_itemData.count() && m_itemData.at(index)->values.value("expansionLevel").toInt() > parentLevel) { + while (index < m_itemData.count() && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > parentLevel) { items.append(m_itemData.at(index)->item); ++index; } diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index a792b089f..6cc53f12a 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -139,6 +139,7 @@ public: virtual bool setExpanded(int index, bool expanded); virtual bool isExpanded(int index) const; virtual bool isExpandable(int index) const; + virtual int expandedParentsCount(int index) const; QSet expandedUrls() const; @@ -208,7 +209,7 @@ private: IsDirRole, IsExpandedRole, IsExpandableRole, - ExpansionLevelRole, + ExpandedParentsCountRole, RolesCount // Mandatory last entry }; @@ -258,7 +259,7 @@ private: bool lessThan(const ItemData* a, const ItemData* b) const; /** - * Helper method for lessThan() and expansionLevelsCompare(): Compares + * Helper method for lessThan() and expandedParentsCountCompare(): Compares * the passed item-data using m_sortRole as criteria. Both items must * have the same parent item, otherwise the comparison will be wrong. */ @@ -296,10 +297,10 @@ private: * is not sufficient, it is also important to check the hierarchy for having * a correct order like shown in a tree. */ - int expansionLevelsCompare(const ItemData* a, const ItemData* b) const; + int expandedParentsCountCompare(const ItemData* a, const ItemData* b) const; /** - * Helper method for expansionLevelCompare(). + * Helper method for expandedParentsCountCompare(). */ QString subPath(const KFileItem& item, const QString& itemPath, @@ -358,19 +359,19 @@ private: mutable QList > m_groups; // Stores the smallest expansion level of the root-URL. Is required to calculate - // the "expansionLevel" role in an efficient way. A value < 0 indicates a + // the "expandedParentsCount" role in an efficient way. A value < 0 indicates a // special meaning: - enum RootExpansionLevelTypes + enum ExpandedParentsCountRootTypes { - // m_rootExpansionLevel is uninitialized and must be determined by checking + // m_expandedParentsCountRoot is uninitialized and must be determined by checking // the root URL from the KDirLister. - UninitializedRootExpansionLevel = -1, - // All items should be forced to get an expansion level of 0 even if they + UninitializedExpandedParentsCountRoot = -1, + // All items should be forced to get an expanded parents count of 0 even if they // represent child items. This is useful for slaves that provide no parent items // for child items like e.g. the search IO slaves. - ForceRootExpansionLevel = -2 + ForceExpandedParentsCountRoot = -2 }; - mutable int m_rootExpansionLevel; + mutable int m_expandedParentsCountRoot; // Stores the URLs of the expanded folders. QSet m_expandedUrls; @@ -384,7 +385,7 @@ private: inline bool KFileItemModel::isChildItem(int index) const { - return m_requestRole[ExpansionLevelRole] && m_itemData.at(index)->values.value("expansionLevel").toInt() > 0; + return m_requestRole[ExpandedParentsCountRole] && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > 0; } #endif diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 323e67452..de80819d7 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -1346,6 +1346,16 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha const int lastVisibleIndex = m_layouter->lastVisibleIndex(); + int firstExpansionIndex = -1; + int lastExpansionIndex = -1; + const bool supportsExpanding = supportsItemExpanding(); + if (supportsExpanding && changedCount != 0) { + // Any inserting or removing of items might result in changing the siblings-information + // of other visible items. + firstExpansionIndex = firstVisibleIndex; + lastExpansionIndex = lastVisibleIndex; + } + QList reusableItems = recycleInvisibleItems(firstVisibleIndex, lastVisibleIndex, hint); // Assure that for each visible item a KItemListWidget is available. KItemListWidget @@ -1390,6 +1400,13 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha } applyNewPos = false; } + + if (supportsExpanding && changedCount == 0) { + if (firstExpansionIndex < 0) { + firstExpansionIndex = i; + } + lastExpansionIndex = i; + } } if (animate) { @@ -1452,6 +1469,10 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha recycleWidget(m_visibleItems.value(index)); } + if (supportsExpanding) { + updateSiblingsInformation(firstExpansionIndex, lastExpansionIndex); + } + if (m_grouped) { // Update the layout of all visible group headers QHashIterator it(m_visibleGroups); @@ -1627,6 +1648,7 @@ void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index) widget->setEnabledSelectionToggle(enabledSelectionToggles()); widget->setIndex(index); widget->setData(m_model->data(index)); + widget->setSiblingsInformation(QBitArray()); } void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) @@ -1968,6 +1990,112 @@ void KItemListView::updateGroupHeaderHeight() updateVisibleGroupHeaders(); } +void KItemListView::updateSiblingsInformation(int firstIndex, int lastIndex) +{ + const int firstVisibleIndex = m_layouter->firstVisibleIndex(); + const int lastVisibleIndex = m_layouter->lastVisibleIndex(); + const bool isRangeVisible = firstIndex >= 0 && + lastIndex >= firstIndex && + lastIndex >= firstVisibleIndex && + firstIndex <= lastVisibleIndex; + if (!isRangeVisible) { + return; + } + + int previousParents = 0; + QBitArray previousSiblings; + + // The rootIndex describes the first index where the siblings get + // calculated from. For the calculation the upper most parent item + // is required. For performance reasons it is checked first whether + // the visible items before or after the current range already + // contain a siblings information which can be used as base. + int rootIndex = firstIndex; + + KItemListWidget* widget = m_visibleItems.value(firstIndex - 1); + if (!widget) { + // There is no visible widget before the range, check whether there + // is one after the range: + widget = m_visibleItems.value(lastIndex + 1); + if (widget) { + // The sibling information of the widget may only be used if + // all items of the range have the same number of parents. + const int parents = m_model->expandedParentsCount(lastIndex + 1); + for (int i = lastIndex; i >= firstIndex; --i) { + if (m_model->expandedParentsCount(i) != parents) { + widget = 0; + break; + } + } + } + } + + if (widget) { + // Performance optimization: Use the sibling information of the visible + // widget beside the given range. + previousSiblings = widget->siblingsInformation(); + if (previousSiblings.isEmpty()) { + return; + } + previousParents = previousSiblings.count() - 1; + previousSiblings.truncate(previousParents); + } else { + // Potentially slow path: Go back to the upper most parent of firstIndex + // to be able to calculate the initial value for the siblings. + while (rootIndex > 0 && m_model->expandedParentsCount(rootIndex) > 0) { + --rootIndex; + } + } + + for (int i = rootIndex; i <= lastIndex; ++i) { + // Update the parent-siblings in case if the current item represents + // a child or an upper parent. + const int currentParents = m_model->expandedParentsCount(i); + if (previousParents < currentParents) { + previousParents = currentParents; + previousSiblings.resize(currentParents); + previousSiblings.setBit(currentParents - 1, hasSiblingSuccessor(i - 1)); + } else if (previousParents > currentParents) { + previousParents = currentParents; + previousSiblings.truncate(currentParents); + } + + if (i >= firstIndex) { + // The index represents a visible item. Apply the parent-siblings + // and update the sibling of the current item. + KItemListWidget* widget = m_visibleItems.value(i); + if (!widget) { + continue; + } + + QBitArray siblings = previousSiblings; + siblings.resize(siblings.count() + 1); + siblings.setBit(siblings.count() - 1, hasSiblingSuccessor(i)); + + widget->setSiblingsInformation(siblings); + } + } +} + +bool KItemListView::hasSiblingSuccessor(int index) const +{ + const int parentsCount = m_model->expandedParentsCount(index); + ++index; + + const int itemCount = m_model->count(); + while (index < itemCount) { + const int currentParentsCount = m_model->expandedParentsCount(index); + if (currentParentsCount == parentsCount) { + return true; + } else if (currentParentsCount < parentsCount) { + return false; + } + ++index; + } + + return false; +} + int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc) { int inc = 0; diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index 6ed714b20..dbb746d92 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -496,6 +496,20 @@ private: */ void updateGroupHeaderHeight(); + /** + * Updates the siblings-information for all visible items that are inside + * the range of \p firstIndex and \p lastIndex. + * @see KItemListWidget::setSiblingsInformation() + */ + void updateSiblingsInformation(int firstIndex, int lastIndex); + + /** + * Helper method for updateExpansionIndicators(). + * @return True if the item with the index \a index has a sibling successor + * (= the item is not the last item of the current hierarchy). + */ + bool hasSiblingSuccessor(int index) const; + /** * Helper function for triggerAutoScrolling(). * @param pos Logical position of the mouse relative to the range. diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 3d593f76f..24a3f079a 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -46,6 +46,7 @@ KItemListWidget::KItemListWidget(QGraphicsItem* parent) : m_visibleRoles(), m_visibleRolesSizes(), m_styleOption(), + m_siblingsInfo(), m_hoverOpacity(0), m_hoverCache(0), m_hoverAnimation(0), @@ -92,6 +93,7 @@ void KItemListWidget::setData(const QHash& data, } dataChanged(m_data, roles); } + update(); } QHash KItemListWidget::data() const @@ -167,7 +169,9 @@ void KItemListWidget::setVisibleRoles(const QList& roles) { const QList previousRoles = m_visibleRoles; m_visibleRoles = roles; + visibleRolesChanged(roles, previousRoles); + update(); } QList KItemListWidget::visibleRoles() const @@ -179,7 +183,9 @@ void KItemListWidget::setVisibleRolesSizes(const QHash roles { const QHash previousRolesSizes = m_visibleRolesSizes; m_visibleRolesSizes = rolesSizes; + visibleRolesSizesChanged(rolesSizes, previousRolesSizes); + update(); } QHash KItemListWidget::visibleRolesSizes() const @@ -194,6 +200,7 @@ void KItemListWidget::setStyleOption(const KItemListStyleOption& option) m_styleOption = option; styleOptionChanged(option, previous); + update(); } const KItemListStyleOption& KItemListWidget::styleOption() const @@ -208,7 +215,6 @@ void KItemListWidget::setSelected(bool selected) if (m_selectionToggle) { m_selectionToggle->setChecked(selected); } - selectedChanged(selected); update(); } @@ -223,7 +229,6 @@ void KItemListWidget::setCurrent(bool current) { if (m_current != current) { m_current = current; - currentChanged(current); update(); } @@ -265,7 +270,6 @@ void KItemListWidget::setHovered(bool hovered) m_hoverAnimation->start(); hoveredChanged(hovered); - update(); } @@ -301,6 +305,19 @@ bool KItemListWidget::enabledSelectionToggle() const return m_enabledSelectionToggle; } +void KItemListWidget::setSiblingsInformation(const QBitArray& siblings) +{ + const QBitArray previous = m_siblingsInfo; + m_siblingsInfo = siblings; + siblingsInformationChanged(m_siblingsInfo, previous); + update(); +} + +QBitArray KItemListWidget::siblingsInformation() const +{ + return m_siblingsInfo; +} + bool KItemListWidget::contains(const QPointF& point) const { if (!QGraphicsWidget::contains(point)) { @@ -328,7 +345,6 @@ void KItemListWidget::dataChanged(const QHash& current, { Q_UNUSED(current); Q_UNUSED(roles); - update(); } void KItemListWidget::visibleRolesChanged(const QList& current, @@ -336,7 +352,6 @@ void KItemListWidget::visibleRolesChanged(const QList& current, { Q_UNUSED(current); Q_UNUSED(previous); - update(); } void KItemListWidget::visibleRolesSizesChanged(const QHash& current, @@ -344,7 +359,6 @@ void KItemListWidget::visibleRolesSizesChanged(const QHash& { Q_UNUSED(current); Q_UNUSED(previous); - update(); } void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current, @@ -352,7 +366,6 @@ void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current, { Q_UNUSED(current); Q_UNUSED(previous); - update(); } void KItemListWidget::currentChanged(bool current) @@ -375,6 +388,12 @@ void KItemListWidget::alternatingBackgroundColorsChanged(bool enabled) Q_UNUSED(enabled); } +void KItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) { QGraphicsWidget::resizeEvent(event); diff --git a/src/kitemviews/kitemlistwidget.h b/src/kitemviews/kitemlistwidget.h index 02e5998f9..20efa7c94 100644 --- a/src/kitemviews/kitemlistwidget.h +++ b/src/kitemviews/kitemlistwidget.h @@ -27,6 +27,7 @@ #include +#include #include #include @@ -87,6 +88,16 @@ public: void setEnabledSelectionToggle(bool enabled); bool enabledSelectionToggle() const; + /** + * Sets the sibling information for the item and all of its parents. + * The sibling information of the upper most parent is represented by + * the first bit, the sibling information of the item by the last bit. + * The sibling information is useful for drawing the branches in + * tree views. + */ + void setSiblingsInformation(const QBitArray& siblings); + QBitArray siblingsInformation() const; + /** * @return True if \a point is inside KItemListWidget::hoverRect(), * KItemListWidget::textRect(), KItemListWidget::selectionToggleRect() @@ -128,6 +139,7 @@ protected: virtual void selectedChanged(bool selected); virtual void hoveredChanged(bool hovered); virtual void alternatingBackgroundColorsChanged(bool enabled); + virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); virtual void resizeEvent(QGraphicsSceneResizeEvent* event); /** @@ -158,6 +170,7 @@ private: QList m_visibleRoles; QHash m_visibleRolesSizes; KItemListStyleOption m_styleOption; + QBitArray m_siblingsInfo; qreal m_hoverOpacity; mutable QPixmap* m_hoverCache; diff --git a/src/kitemviews/kitemmodelbase.cpp b/src/kitemviews/kitemmodelbase.cpp index e2b86d8a0..c13c9f88c 100644 --- a/src/kitemviews/kitemmodelbase.cpp +++ b/src/kitemviews/kitemmodelbase.cpp @@ -128,6 +128,12 @@ bool KItemModelBase::isExpandable(int index) const return false; } +int KItemModelBase::expandedParentsCount(int index) const +{ + Q_UNUSED(index); + return 0; +} + QMimeData* KItemModelBase::createMimeData(const QSet& indexes) const { Q_UNUSED(indexes); diff --git a/src/kitemviews/kitemmodelbase.h b/src/kitemviews/kitemmodelbase.h index 1dffaf8cf..de847a9a4 100644 --- a/src/kitemviews/kitemmodelbase.h +++ b/src/kitemviews/kitemmodelbase.h @@ -145,6 +145,12 @@ public: */ virtual bool isExpandable(int index) const; + /** + * @return Number of expanded parent items for the item with the given index. + * Per default 0 is returned. + */ + virtual int expandedParentsCount(int index) const; + /** * @return MIME-data for the items given by \a indexes. The default implementation * returns 0. The ownership of the returned instance is in the hand of the diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index d0accd68a..ea67b85f4 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -373,7 +373,7 @@ void KFileItemModelTest::testExpandItems() // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the // first three characters. QSet modelRoles = m_model->roles(); - modelRoles << "isExpanded" << "isExpandable" << "expansionLevel"; + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; m_model->setRoles(modelRoles); QStringList files; @@ -490,7 +490,7 @@ void KFileItemModelTest::testExpandParentItems() // a2/b2/c2/ // a2/b2/c2/d2/ QSet modelRoles = m_model->roles(); - modelRoles << "isExpanded" << "isExpandable" << "expansionLevel"; + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; m_model->setRoles(modelRoles); QStringList files; @@ -665,7 +665,7 @@ void KFileItemModelTest::testExpansionLevelsCompare() b.item = itemB; b.parent = 0; - QCOMPARE(m_model->expansionLevelsCompare(&a, &b), result); + QCOMPARE(m_model->expandedParentsCountCompare(&a, &b), result); } void KFileItemModelTest::testIndexForKeyboardSearch()