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;
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
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();
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;
void KFileItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
const QList<QByteArray>& previous)
{
- KItemListWidget::visibleRolesChanged(current, previous);
+ Q_UNUSED(previous);
m_sortedVisibleRoles = current;
m_dirtyLayout = true;
}
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;
}
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);
{
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;
}
}
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);
}
}
+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);
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);
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);
m_pendingItemsToInsert(),
m_pendingEmitLoadingCompleted(false),
m_groups(),
- m_rootExpansionLevel(UninitializedRootExpansionLevel),
+ m_expandedParentsCountRoot(UninitializedExpandedParentsCountRoot),
m_expandedUrls(),
m_urlsToExpand()
{
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;
}
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;
}
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).
}
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;
}
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;
{
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();
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));
m_resortAllItemsTimer->stop();
m_pendingItemsToInsert.clear();
- m_rootExpansionLevel = UninitializedRootExpansionLevel;
+ m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
const int removedCount = m_itemData.count();
if (removedCount > 0) {
}
if (count() <= 0) {
- m_rootExpansionLevel = UninitializedRootExpansionLevel;
+ m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
}
itemRanges << KItemRange(removedAtIndex, removedCount);
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);
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();
}
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);
}
"isDir",
"isExpanded",
"isExpandable",
- "expansionLevel"
+ "expandedParentsCount"
};
return roles[role];
}
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);
}
}
{
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;
: 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();
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;
}
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;
IsDirRole,
IsExpandedRole,
IsExpandableRole,
- ExpansionLevelRole,
+ ExpandedParentsCountRole,
RolesCount // Mandatory last entry
};
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.
*/
* 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,
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;
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
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
}
applyNewPos = false;
}
+
+ if (supportsExpanding && changedCount == 0) {
+ if (firstExpansionIndex < 0) {
+ firstExpansionIndex = i;
+ }
+ lastExpansionIndex = i;
+ }
}
if (animate) {
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);
widget->setEnabledSelectionToggle(enabledSelectionToggles());
widget->setIndex(index);
widget->setData(m_model->data(index));
+ widget->setSiblingsInformation(QBitArray());
}
void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
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;
*/
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.
m_visibleRoles(),
m_visibleRolesSizes(),
m_styleOption(),
+ m_siblingsInfo(),
m_hoverOpacity(0),
m_hoverCache(0),
m_hoverAnimation(0),
}
dataChanged(m_data, roles);
}
+ update();
}
QHash<QByteArray, QVariant> KItemListWidget::data() const
{
const QList<QByteArray> previousRoles = m_visibleRoles;
m_visibleRoles = roles;
+
visibleRolesChanged(roles, previousRoles);
+ update();
}
QList<QByteArray> KItemListWidget::visibleRoles() const
{
const QHash<QByteArray, QSizeF> previousRolesSizes = m_visibleRolesSizes;
m_visibleRolesSizes = rolesSizes;
+
visibleRolesSizesChanged(rolesSizes, previousRolesSizes);
+ update();
}
QHash<QByteArray, QSizeF> KItemListWidget::visibleRolesSizes() const
m_styleOption = option;
styleOptionChanged(option, previous);
+ update();
}
const KItemListStyleOption& KItemListWidget::styleOption() const
if (m_selectionToggle) {
m_selectionToggle->setChecked(selected);
}
-
selectedChanged(selected);
update();
}
{
if (m_current != current) {
m_current = current;
-
currentChanged(current);
update();
}
m_hoverAnimation->start();
hoveredChanged(hovered);
-
update();
}
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)) {
{
Q_UNUSED(current);
Q_UNUSED(roles);
- update();
}
void KItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
{
Q_UNUSED(current);
Q_UNUSED(previous);
- update();
}
void KItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
{
Q_UNUSED(current);
Q_UNUSED(previous);
- update();
}
void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
{
Q_UNUSED(current);
Q_UNUSED(previous);
- update();
}
void KItemListWidget::currentChanged(bool current)
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);
#include <kitemviews/kitemliststyleoption.h>
+#include <QBitArray>
#include <QGraphicsWidget>
#include <QStyle>
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()
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);
/**
QList<QByteArray> m_visibleRoles;
QHash<QByteArray, QSizeF> m_visibleRolesSizes;
KItemListStyleOption m_styleOption;
+ QBitArray m_siblingsInfo;
qreal m_hoverOpacity;
mutable QPixmap* m_hoverCache;
return false;
}
+int KItemModelBase::expandedParentsCount(int index) const
+{
+ Q_UNUSED(index);
+ return 0;
+}
+
QMimeData* KItemModelBase::createMimeData(const QSet<int>& indexes) const
{
Q_UNUSED(indexes);
*/
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
// 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;
// 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;
b.item = itemB;
b.parent = 0;
- QCOMPARE(m_model->expansionLevelsCompare(&a, &b), result);
+ QCOMPARE(m_model->expandedParentsCountCompare(&a, &b), result);
}
void KFileItemModelTest::testIndexForKeyboardSearch()