]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Details view: Fix indicator-branches
authorPeter Penz <peter.penz19@gmail.com>
Tue, 14 Feb 2012 17:04:47 +0000 (18:04 +0100)
committerPeter Penz <peter.penz19@gmail.com>
Tue, 14 Feb 2012 17:05:29 +0000 (18:05 +0100)
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

12 files changed:
src/kitemviews/kfileitemlistview.cpp
src/kitemviews/kfileitemlistwidget.cpp
src/kitemviews/kfileitemlistwidget.h
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodel.h
src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistview.h
src/kitemviews/kitemlistwidget.cpp
src/kitemviews/kitemlistwidget.h
src/kitemviews/kitemmodelbase.cpp
src/kitemviews/kitemmodelbase.h
src/tests/kfileitemmodeltest.cpp

index f8a58ae51874b543af5b51a91fbf67d955885c29..03f379837cfcba418f080584250a5a5f1f4c5987 100644 (file)
@@ -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
index 64cc8b4497800871e8bbd0e948fb68252cf48d12..dc1554dab9e3005be0e858407752ec55c2b87259 100644 (file)
@@ -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<QByteArray, QVariant>& current,
                                       const QSet<QByteArray>& roles)
 {
-    KItemListWidget::dataChanged(current, roles);
+    Q_UNUSED(current);
+
     m_dirtyContent = true;
 
     QSet<QByteArray> dirtyRoles;
@@ -331,7 +327,7 @@ void KFileItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current
 void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
                                               const QList<QByteArray>& previous)
 {
-    KItemListWidget::visibleRolesChanged(current, previous);
+    Q_UNUSED(previous);
     m_sortedVisibleRoles = current;
     m_dirtyLayout = true;
 }
@@ -339,14 +335,16 @@ void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
 void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
                                                    const QHash<QByteArray, QSizeF>& 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<QByteArray, QVariant> 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);
index 4a44a846889de014a8a128cfb6df889509948a6c..afb0f0171284ea734845a7eab3bc6bf4f6b86c28 100644 (file)
@@ -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);
index e0ae033026f94b8d42f9362dc004fce904bbae19..f9ba397fab630a69cdd3295c62317b2cd8b8afee 100644 (file)
@@ -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<QPair<int, QVariant> > 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<QByteArray>& 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<KUrl> 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::ItemData*> 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<QByteArray, QVariant> 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;
         }
index a792b089f19ae373791104f57811a00e34dafdc4..6cc53f12a11dd2f560af7632258af4f567124caf 100644 (file)
@@ -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<KUrl> 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<QPair<int, QVariant> > 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<KUrl> 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
index 323e6745296e276392b17c81d143781b5e381f11..de80819d7858704a15d152c424a49f6215da84c6 100644 (file)
@@ -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<int> 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<KItemListWidget*, KItemListGroupHeader*> 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;
index 6ed714b2050f4059a061e9f2c63e0defb53d7a1d..dbb746d92c3c53fcc31e6886f1d4239a817f5b73 100644 (file)
@@ -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.
index 3d593f76f54715dc71c267c2a5ec9a5105e87f3a..24a3f079a9005cd6dd790330cf7e3f92960ea1ef 100644 (file)
@@ -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<QByteArray, QVariant>& data,
         }
         dataChanged(m_data, roles);
     }
+    update();
 }
 
 QHash<QByteArray, QVariant> KItemListWidget::data() const
@@ -167,7 +169,9 @@ void KItemListWidget::setVisibleRoles(const QList<QByteArray>& roles)
 {
     const QList<QByteArray> previousRoles = m_visibleRoles;
     m_visibleRoles = roles;
+
     visibleRolesChanged(roles, previousRoles);
+    update();
 }
 
 QList<QByteArray> KItemListWidget::visibleRoles() const
@@ -179,7 +183,9 @@ void KItemListWidget::setVisibleRolesSizes(const QHash<QByteArray, QSizeF> roles
 {
     const QHash<QByteArray, QSizeF> previousRolesSizes = m_visibleRolesSizes;
     m_visibleRolesSizes = rolesSizes;
+
     visibleRolesSizesChanged(rolesSizes, previousRolesSizes);
+    update();
 }
 
 QHash<QByteArray, QSizeF> 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<QByteArray, QVariant>& current,
 {
     Q_UNUSED(current);
     Q_UNUSED(roles);
-    update();
 }
 
 void KItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
@@ -336,7 +352,6 @@ void KItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
 {
     Q_UNUSED(current);
     Q_UNUSED(previous);
-    update();
 }
 
 void KItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
@@ -344,7 +359,6 @@ void KItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>&
 {
     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);
index 02e5998f99596a319372f44084d5279cc4767ea7..20efa7c94475e6f3013d9bd44223a8399223f21b 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <kitemviews/kitemliststyleoption.h>
 
+#include <QBitArray>
 #include <QGraphicsWidget>
 #include <QStyle>
 
@@ -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<QByteArray> m_visibleRoles;
     QHash<QByteArray, QSizeF> m_visibleRolesSizes;
     KItemListStyleOption m_styleOption;
+    QBitArray m_siblingsInfo;
 
     qreal m_hoverOpacity;
     mutable QPixmap* m_hoverCache;
index e2b86d8a03c71575e9828cf4e53ced9c8444c7d8..c13c9f88c815430a9802f349f40491b2a261dbfb 100644 (file)
@@ -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<int>& indexes) const
 {
     Q_UNUSED(indexes);
index 1dffaf8cf786270b7782eafea7cb29715c016bf2..de847a9a45bccf0e6cccdf26dbaaae5eb15e8a55 100644 (file)
@@ -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
index d0accd68aa5176aede54a5fb7ff44d9b085197f9..ea67b85f41dc9d6a4e6eb660c90984c9a16898d4 100644 (file)
@@ -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<QByteArray> 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<QByteArray> 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()