X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/6c3d9acbc22ea9463ba40ef84c9e8c8419dfacf3..6a3f8086a372ca1c21ab474c7934e8f8e4b238f5:/src/kitemviews/kitemlistview.cpp diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 732ed24e4..6a68ae798 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -22,6 +22,8 @@ #include "kitemlistview.h" +#include +#include "kitemlistcontainer.h" #include "kitemlistcontroller.h" #include "kitemlistheader.h" #include "kitemlistselectionmanager.h" @@ -33,16 +35,17 @@ #include "private/kitemlistviewlayouter.h" #include "private/kitemlistviewanimation.h" -#include - #include #include +#include #include #include #include #include #include +#include "kitemlistviewaccessible.h" + namespace { // Time in ms until reaching the autoscroll margin triggers // an initial autoscrolling @@ -52,11 +55,27 @@ namespace { const int RepeatingAutoScrollDelay = 1000 / 60; } +#ifndef QT_NO_ACCESSIBILITY +QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object) +{ + Q_UNUSED(key) + + if (KItemListContainer* container = qobject_cast(object)) { + return new KItemListContainerAccessible(container); + } else if (KItemListView* view = qobject_cast(object)) { + return new KItemListViewAccessible(view); + } + + return 0; +} +#endif + KItemListView::KItemListView(QGraphicsWidget* parent) : QGraphicsWidget(parent), m_enabledSelectionToggles(false), m_grouped(false), m_supportsItemExpanding(false), + m_editingRole(false), m_activeTransactions(0), m_endTransactionAnimationHint(Animation), m_itemSize(), @@ -83,7 +102,8 @@ KItemListView::KItemListView(QGraphicsWidget* parent) : m_autoScrollIncrement(0), m_autoScrollTimer(0), m_header(0), - m_headerWidget(0) + m_headerWidget(0), + m_dropIndicator() { setAcceptHoverEvents(true); @@ -108,95 +128,26 @@ KItemListView::KItemListView(QGraphicsWidget* parent) : m_headerWidget->setVisible(false); m_header = new KItemListHeader(this); -} - -KItemListView::~KItemListView() -{ - delete m_sizeHintResolver; - m_sizeHintResolver = 0; -} - -void KItemListView::setScrollOrientation(Qt::Orientation orientation) -{ - const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); - if (orientation == previousOrientation) { - return; - } - - m_layouter->setScrollOrientation(orientation); - m_animation->setScrollOrientation(orientation); - m_sizeHintResolver->clearCache(); - - if (m_grouped) { - QMutableHashIterator it (m_visibleGroups); - while (it.hasNext()) { - it.next(); - it.value()->setScrollOrientation(orientation); - } - updateGroupHeaderHeight(); - - } - - doLayout(NoAnimation); - onScrollOrientationChanged(orientation, previousOrientation); - emit scrollOrientationChanged(orientation, previousOrientation); -} +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installFactory(accessibleInterfaceFactory); +#endif -Qt::Orientation KItemListView::scrollOrientation() const -{ - return m_layouter->scrollOrientation(); } -void KItemListView::setItemSize(const QSizeF& itemSize) +KItemListView::~KItemListView() { - const QSizeF previousSize = m_itemSize; - if (itemSize == previousSize) { - return; - } - - // Skip animations when the number of rows or columns - // are changed in the grid layout. Although the animation - // engine can handle this usecase, it looks obtrusive. - const bool animate = !changesItemGridLayout(m_layouter->size(), - itemSize, - m_layouter->itemMargin()); - - const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && - (( m_itemSize.isEmpty() && !itemSize.isEmpty()) || - (!m_itemSize.isEmpty() && itemSize.isEmpty())); + // The group headers are children of the widgets created by + // widgetCreator(). So it is mandatory to delete the group headers + // first. + delete m_groupHeaderCreator; + m_groupHeaderCreator = 0; - m_itemSize = itemSize; - - if (alternateBackgroundsChanged) { - // For an empty item size alternate backgrounds are drawn if more than - // one role is shown. Assure that the backgrounds for visible items are - // updated when changing the size in this context. - updateAlternateBackgrounds(); - } - - if (itemSize.isEmpty()) { - if (m_headerWidget->automaticColumnResizing()) { - updatePreferredColumnWidths(); - } else { - // Only apply the changed height and respect the header widths - // set by the user - const qreal currentWidth = m_layouter->itemSize().width(); - const QSizeF newSize(currentWidth, itemSize.height()); - m_layouter->setItemSize(newSize); - } - } else { - m_layouter->setItemSize(itemSize); - } - - m_sizeHintResolver->clearCache(); - doLayout(animate ? Animation : NoAnimation); - onItemSizeChanged(itemSize, previousSize); -} + delete m_widgetCreator; + m_widgetCreator = 0; -QSizeF KItemListView::itemSize() const -{ - return m_itemSize; + delete m_sizeHintResolver; + m_sizeHintResolver = 0; } void KItemListView::setScrollOffset(qreal offset) @@ -353,55 +304,39 @@ KItemModelBase* KItemListView::model() const void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator) { + if (m_widgetCreator) { + delete m_widgetCreator; + } m_widgetCreator = widgetCreator; } KItemListWidgetCreatorBase* KItemListView::widgetCreator() const { + if (!m_widgetCreator) { + m_widgetCreator = defaultWidgetCreator(); + } return m_widgetCreator; } void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator) { + if (m_groupHeaderCreator) { + delete m_groupHeaderCreator; + } m_groupHeaderCreator = groupHeaderCreator; } KItemListGroupHeaderCreatorBase* KItemListView::groupHeaderCreator() const { + if (!m_groupHeaderCreator) { + m_groupHeaderCreator = defaultGroupHeaderCreator(); + } return m_groupHeaderCreator; } -void KItemListView::setStyleOption(const KItemListStyleOption& option) +QSizeF KItemListView::itemSize() const { - const KItemListStyleOption previousOption = m_styleOption; - m_styleOption = option; - - bool animate = true; - const QSizeF margin(option.horizontalMargin, option.verticalMargin); - if (margin != m_layouter->itemMargin()) { - // Skip animations when the number of rows or columns - // are changed in the grid layout. Although the animation - // engine can handle this usecase, it looks obtrusive. - animate = !changesItemGridLayout(m_layouter->size(), - m_layouter->itemSize(), - margin); - m_layouter->setItemMargin(margin); - } - - if (m_grouped) { - updateGroupHeaderHeight(); - } - - QHashIterator it(m_visibleItems); - while (it.hasNext()) { - it.next(); - it.value()->setStyleOption(option); - } - - m_sizeHintResolver->clearCache(); - doLayout(animate ? Animation : NoAnimation); - - onStyleOptionChanged(option, previousOption); + return m_itemSize; } const KItemListStyleOption& KItemListView::styleOption() const @@ -511,7 +446,7 @@ int KItemListView::lastVisibleIndex() const QSizeF KItemListView::itemSizeHint(int index) const { - return m_widgetCreator->itemSizeHint(index, this); + return widgetCreator()->itemSizeHint(index, this); } void KItemListView::setSupportsItemExpanding(bool supportsExpanding) @@ -656,8 +591,37 @@ KItemListHeader* KItemListView::header() const QPixmap KItemListView::createDragPixmap(const QSet& indexes) const { - Q_UNUSED(indexes); - return QPixmap(); + QPixmap pixmap; + + if (indexes.count() == 1) { + KItemListWidget* item = m_visibleItems.value(indexes.toList().first()); + QGraphicsView* graphicsView = scene()->views()[0]; + if (item && graphicsView) { + pixmap = item->createDragPixmap(0, graphicsView); + } + } else { + // TODO: Not implemented yet. Probably extend the interface + // from KItemListWidget::createDragPixmap() to return a pixmap + // that can be used for multiple indexes. + } + + return pixmap; +} + +void KItemListView::editRole(int index, const QByteArray& role) +{ + KItemListWidget* widget = m_visibleItems.value(index); + if (!widget || m_editingRole) { + return; + } + + m_editingRole = true; + widget->setEditedRole(role); + + connect(widget, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); + connect(widget, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); } void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) @@ -682,6 +646,154 @@ void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt opt.rect = rubberBandRect.toRect(); style()->drawControl(QStyle::CE_RubberBand, &opt, painter); } + + if (!m_dropIndicator.isEmpty()) { + const QRectF r = m_dropIndicator.toRect(); + + QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); + painter->setPen(color); + + // TODO: The following implementation works only for a vertical scroll-orientation + // and assumes a height of the m_draggingInsertIndicator of 1. + Q_ASSERT(r.height() == 1); + painter->drawLine(r.left() + 1, r.top(), r.right() - 1, r.top()); + + color.setAlpha(128); + painter->setPen(color); + painter->drawRect(r.left(), r.top() - 1, r.width() - 1, 2); + } +} + +void KItemListView::setItemSize(const QSizeF& size) +{ + const QSizeF previousSize = m_itemSize; + if (size == previousSize) { + return; + } + + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + const bool animate = !changesItemGridLayout(m_layouter->size(), + size, + m_layouter->itemMargin()); + + const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && + (( m_itemSize.isEmpty() && !size.isEmpty()) || + (!m_itemSize.isEmpty() && size.isEmpty())); + + m_itemSize = size; + + if (alternateBackgroundsChanged) { + // For an empty item size alternate backgrounds are drawn if more than + // one role is shown. Assure that the backgrounds for visible items are + // updated when changing the size in this context. + updateAlternateBackgrounds(); + } + + if (size.isEmpty()) { + if (m_headerWidget->automaticColumnResizing()) { + updatePreferredColumnWidths(); + } else { + // Only apply the changed height and respect the header widths + // set by the user + const qreal currentWidth = m_layouter->itemSize().width(); + const QSizeF newSize(currentWidth, size.height()); + m_layouter->setItemSize(newSize); + } + } else { + m_layouter->setItemSize(size); + } + + m_sizeHintResolver->clearCache(); + doLayout(animate ? Animation : NoAnimation); + onItemSizeChanged(size, previousSize); +} + +void KItemListView::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previousOption = m_styleOption; + m_styleOption = option; + + bool animate = true; + const QSizeF margin(option.horizontalMargin, option.verticalMargin); + if (margin != m_layouter->itemMargin()) { + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + animate = !changesItemGridLayout(m_layouter->size(), + m_layouter->itemSize(), + margin); + m_layouter->setItemMargin(margin); + } + + if (m_grouped) { + updateGroupHeaderHeight(); + } + + if (animate && previousOption.maxTextSize != option.maxTextSize) { + // Animating a change of the maximum text size just results in expensive + // temporary eliding and clipping operations and does not look good visually. + animate = false; + } + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + it.value()->setStyleOption(option); + } + + m_sizeHintResolver->clearCache(); + m_layouter->markAsDirty(); + doLayout(animate ? Animation : NoAnimation); + + if (m_itemSize.isEmpty()) { + updatePreferredColumnWidths(); + } + + onStyleOptionChanged(option, previousOption); +} + +void KItemListView::setScrollOrientation(Qt::Orientation orientation) +{ + const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); + if (orientation == previousOrientation) { + return; + } + + m_layouter->setScrollOrientation(orientation); + m_animation->setScrollOrientation(orientation); + m_sizeHintResolver->clearCache(); + + if (m_grouped) { + QMutableHashIterator it (m_visibleGroups); + while (it.hasNext()) { + it.next(); + it.value()->setScrollOrientation(orientation); + } + updateGroupHeaderHeight(); + + } + + doLayout(NoAnimation); + + onScrollOrientationChanged(orientation, previousOrientation); + emit scrollOrientationChanged(orientation, previousOrientation); +} + +Qt::Orientation KItemListView::scrollOrientation() const +{ + return m_layouter->scrollOrientation(); +} + +KItemListWidgetCreatorBase* KItemListView::defaultWidgetCreator() const +{ + return 0; +} + +KItemListGroupHeaderCreatorBase* KItemListView::defaultGroupHeaderCreator() const +{ + return 0; } void KItemListView::initializeItemListWidget(KItemListWidget* item) @@ -753,7 +865,7 @@ void KItemListView::onTransactionEnd() bool KItemListView::event(QEvent* event) { // Forward all events to the controller and handle them there - if (m_controller && m_controller->processEvent(event, transform())) { + if (!m_editingRole && m_controller && m_controller->processEvent(event, transform())) { event->accept(); return true; } @@ -902,9 +1014,17 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) #endif m_endTransactionAnimationHint = NoAnimation; endTransaction(); + updateSiblingsInformation(); } + if (m_grouped && (hasMultipleRanges || itemRanges.first().count < m_model->count())) { + // In case if items of the same group have been inserted before an item that + // currently represents the first item of the group, the group header of + // this item must be removed. + updateVisibleGroupHeaders(); + } + if (useAlternateBackgrounds()) { updateAlternateBackgrounds(); } @@ -1020,6 +1140,12 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) updateSiblingsInformation(); } + if (m_grouped && (hasMultipleRanges || m_model->count() > 0)) { + // In case if the first item of a group has been removed, the group header + // must be applied to the next visible item. + updateVisibleGroupHeaders(); + } + if (useAlternateBackgrounds()) { updateAlternateBackgrounds(); } @@ -1086,6 +1212,7 @@ void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges, doLayout(NoAnimation); } } + QAccessible::updateAccessibility(this, 0, QAccessible::TableModelChanged); } void KItemListView::slotGroupedSortingChanged(bool current) @@ -1148,6 +1275,7 @@ void KItemListView::slotCurrentChanged(int current, int previous) if (currentWidget) { currentWidget->setCurrent(true); } + QAccessible::updateAccessibility(this, current+1, QAccessible::Focus); } void KItemListView::slotSelectionChanged(const QSet& current, const QSet& previous) @@ -1180,7 +1308,7 @@ void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, // by m_visibleWidgets and must be deleted manually after the animation has // been finished. recycleGroupHeaderForWidget(itemListWidget); - m_widgetCreator->recycle(itemListWidget); + widgetCreator()->recycle(itemListWidget); break; } @@ -1310,9 +1438,9 @@ void KItemListView::triggerAutoScrolling() const qreal newScrollOffset = qMin(scrollOffset() + m_autoScrollIncrement, maxVisibleOffset); setScrollOffset(newScrollOffset); - // Trigger the autoscroll timer which will periodically call - // triggerAutoScrolling() - m_autoScrollTimer->start(RepeatingAutoScrollDelay); + // Trigger the autoscroll timer which will periodically call + // triggerAutoScrolling() + m_autoScrollTimer->start(RepeatingAutoScrollDelay); } void KItemListView::slotGeometryOfGroupHeaderParentChanged() @@ -1324,6 +1452,22 @@ void KItemListView::slotGeometryOfGroupHeaderParentChanged() updateGroupHeaderLayout(widget); } +void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value) +{ + disconnectRoleEditingSignals(index); + + emit roleEditingCanceled(index, role, value); + m_editingRole = false; +} + +void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) +{ + disconnectRoleEditingSignals(index); + + emit roleEditingFinished(index, role, value); + m_editingRole = false; +} + void KItemListView::setController(KItemListController* controller) { if (m_controller != controller) { @@ -1371,6 +1515,8 @@ void KItemListView::setModel(KItemModelBase* model) this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); } + m_sizeHintResolver->clearCache(); + m_model = model; m_layouter->setModel(model); m_grouped = model->groupedSorting(); @@ -1390,6 +1536,12 @@ void KItemListView::setModel(KItemModelBase* model) this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + + const int itemCount = m_model->count(); + if (itemCount > 0) { + m_sizeHintResolver->itemsInserted(0, itemCount); + slotItemsInserted(KItemRangeList() << KItemRange(0, itemCount)); + } } onModelChanged(model, previous); @@ -1493,6 +1645,11 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha } if (animate) { + if (m_animation->isStarted(widget, KItemListViewAnimation::MovingAnimation)) { + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + applyNewPos = false; + } + const bool itemsRemoved = (changedCount < 0); const bool itemsInserted = (changedCount > 0); if (itemsRemoved && (i >= changedIndex + changedCount + 1)) { @@ -1623,17 +1780,22 @@ bool KItemListView::moveWidget(KItemListWidget* widget,const QPointF& newPos) bool startMovingAnim = false; - // When having a grid the moving-animation should only be started, if it is done within - // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation. - // Otherwise instead of a moving-animation a create-animation on the new position will be used - // instead. This is done to prevent overlapping (and confusing) moving-animations. - const int index = widget->index(); - const Cell cell = m_visibleCells.value(index); - if (cell.column >= 0 && cell.row >= 0) { - if (scrollOrientation() == Qt::Vertical) { - startMovingAnim = (cell.row == m_layouter->itemRow(index)); - } else { - startMovingAnim = (cell.column == m_layouter->itemColumn(index)); + if (m_itemSize.isEmpty()) { + // The items are not aligned in a grid but either as columns or rows. + startMovingAnim = true; + } else { + // When having a grid the moving-animation should only be started, if it is done within + // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation. + // Otherwise instead of a moving-animation a create-animation on the new position will be used + // instead. This is done to prevent overlapping (and confusing) moving-animations. + const int index = widget->index(); + const Cell cell = m_visibleCells.value(index); + if (cell.column >= 0 && cell.row >= 0) { + if (scrollOrientation() == Qt::Vertical) { + startMovingAnim = (cell.row == m_layouter->itemRow(index)); + } else { + startMovingAnim = (cell.column == m_layouter->itemColumn(index)); + } } } @@ -1676,7 +1838,7 @@ void KItemListView::emitOffsetChanges() KItemListWidget* KItemListView::createWidget(int index) { - KItemListWidget* widget = m_widgetCreator->create(this); + KItemListWidget* widget = widgetCreator()->create(this); widget->setFlag(QGraphicsItem::ItemStacksBehindParent); m_visibleItems.insert(index, widget); @@ -1696,7 +1858,7 @@ void KItemListView::recycleWidget(KItemListWidget* widget) m_visibleItems.remove(index); m_visibleCells.remove(index); - m_widgetCreator->recycle(widget); + widgetCreator()->recycle(widget); } void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) @@ -1770,13 +1932,13 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) } const QList > groups = model()->groups(); - if (groups.isEmpty()) { + if (groups.isEmpty() || !groupHeaderCreator()) { return; } KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget); if (!groupHeader) { - groupHeader = m_groupHeaderCreator->create(this); + groupHeader = groupHeaderCreator()->create(this); groupHeader->setParentItem(widget); m_visibleGroups.insert(widget, groupHeader); connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); @@ -1809,8 +1971,10 @@ void KItemListView::updateGroupHeaderLayout(KItemListWidget* widget) // In the vertical scroll orientation the group header should always span // the whole width no matter which temporary position the parent widget // has. In this case the x-position and width will be adjusted manually. - groupHeader->setPos(-widget->x(), -groupHeaderRect.height()); - groupHeader->resize(size().width(), groupHeaderRect.size().height()); + const qreal x = -widget->x() - itemOffset(); + const qreal width = maximumItemOffset(); + groupHeader->setPos(x, -groupHeaderRect.height()); + groupHeader->resize(width, groupHeaderRect.size().height()); } else { groupHeader->setPos(groupHeaderRect.x() - itemRect.x(), -widget->y()); groupHeader->resize(groupHeaderRect.size()); @@ -1822,7 +1986,7 @@ void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget) KItemListGroupHeader* header = m_visibleGroups.value(widget); if (header) { header->setParentItem(0); - m_groupHeaderCreator->recycle(header); + groupHeaderCreator()->recycle(header); m_visibleGroups.remove(widget); disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); } @@ -1923,6 +2087,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi // Calculate the preferred column withs for each item and ignore values // smaller than the width for showing the headline unclipped. + const KItemListWidgetCreatorBase* creator = widgetCreator(); int calculatedItemCount = 0; bool maxTimeExceeded = false; foreach (const KItemRange& itemRange, itemRanges) { @@ -1932,7 +2097,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi for (int i = startIndex; i <= endIndex; ++i) { foreach (const QByteArray& visibleRole, visibleRoles()) { qreal maxWidth = widths.value(visibleRole, 0); - const qreal width = m_widgetCreator->preferredRoleColumnWidth(visibleRole, i, this); + const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this); maxWidth = qMax(width, maxWidth); widths.insert(visibleRole, maxWidth); } @@ -2034,6 +2199,9 @@ void KItemListView::applyAutomaticColumnWidths() { Q_ASSERT(m_itemSize.isEmpty()); Q_ASSERT(m_headerWidget->automaticColumnResizing()); + if (m_visibleRoles.isEmpty()) { + return; + } // Calculate the maximum size of an item by considering the // visible role sizes and apply them to the layouter. If the @@ -2055,7 +2223,7 @@ void KItemListView::applyAutomaticColumnWidths() // Stretch the first column to use the whole remaining width firstColumnWidth += availableWidth - requiredWidth; m_headerWidget->setColumnWidth(firstRole, firstColumnWidth); - } else if (requiredWidth > availableWidth) { + } else if (requiredWidth > availableWidth && m_visibleRoles.count() > 1) { // Shrink the first column to be able to show as much other // columns as possible qreal shrinkedFirstColumnWidth = firstColumnWidth - requiredWidth + availableWidth; @@ -2139,6 +2307,12 @@ bool KItemListView::changesItemGridLayout(const QSizeF& newGridSize, bool KItemListView::animateChangedItemCount(int changedItemCount) const { + if (m_itemSize.isEmpty()) { + // We have only columns or only rows, but no grid: An animation is usually + // welcome when inserting or removing items. + return !supportsItemExpanding(); + } + if (m_layouter->size().isEmpty() || m_layouter->itemSize().isEmpty()) { return false; } @@ -2163,6 +2337,52 @@ bool KItemListView::scrollBarRequired(const QSizeF& size) const : maxOffset > size.width(); } +int KItemListView::showDropIndicator(const QPointF& pos) +{ + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + const KItemListWidget* widget = it.value(); + + const QPointF mappedPos = widget->mapFromItem(this, pos); + const QRectF rect = itemRect(widget->index()); + if (mappedPos.y() >= 0 && mappedPos.y() <= rect.height()) { + if (m_model->supportsDropping(widget->index())) { + const int gap = qMax(4, m_styleOption.padding); + if (mappedPos.y() >= gap && mappedPos.y() <= rect.height() - gap) { + return -1; + } + } + + const bool isAboveItem = (mappedPos.y () < rect.height() / 2); + const qreal y = isAboveItem ? rect.top() : rect.bottom(); + + const QRectF draggingInsertIndicator(rect.left(), y, rect.width(), 1); + if (m_dropIndicator != draggingInsertIndicator) { + m_dropIndicator = draggingInsertIndicator; + update(); + } + + int index = widget->index(); + if (!isAboveItem) { + ++index; + } + return index; + } + } + + const QRectF firstItemRect = itemRect(firstVisibleIndex()); + return (pos.y() <= firstItemRect.top()) ? 0 : -1; +} + +void KItemListView::hideDropIndicator() +{ + if (!m_dropIndicator.isNull()) { + m_dropIndicator = QRectF(); + update(); + } +} + void KItemListView::updateGroupHeaderHeight() { qreal groupHeaderHeight = m_styleOption.fontMetrics.height(); @@ -2314,6 +2534,17 @@ bool KItemListView::hasSiblingSuccessor(int index) const return hasSuccessor; } +void KItemListView::disconnectRoleEditingSignals(int index) +{ + KItemListWidget* widget = m_visibleItems.value(index); + if (!widget) { + return; + } + + widget->disconnect(SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), this); + widget->disconnect(SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this); +} + int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc) { int inc = 0;