+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ if (!qFuzzyIsNull(offset)) {
+ Q_EMIT scrollTo(scrollOffset() + offset);
+ return;
+ }
+
+ Q_EMIT scrollingStopped();
+}
+
+void KItemListView::beginTransaction()
+{
+ ++m_activeTransactions;
+ if (m_activeTransactions == 1) {
+ onTransactionBegin();
+ }
+}
+
+void KItemListView::endTransaction()
+{
+ --m_activeTransactions;
+ if (m_activeTransactions < 0) {
+ m_activeTransactions = 0;
+ qCWarning(DolphinDebug) << "Mismatch between beginTransaction()/endTransaction()";
+ }
+
+ if (m_activeTransactions == 0) {
+ onTransactionEnd();
+ doLayout(m_endTransactionAnimationHint);
+ m_endTransactionAnimationHint = Animation;
+ }
+}
+
+bool KItemListView::isTransactionActive() const
+{
+ return m_activeTransactions > 0;
+}
+
+void KItemListView::setHeaderVisible(bool visible)
+{
+ if (visible && !m_headerWidget->isVisible()) {
+ QStyleOptionHeader option;
+ const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize());
+
+ m_headerWidget->setPos(0, 0);
+ m_headerWidget->resize(size().width(), headerSize.height());
+ m_headerWidget->setModel(m_model);
+ m_headerWidget->setColumns(m_visibleRoles);
+ m_headerWidget->setZValue(1);
+
+ connect(m_headerWidget, &KItemListHeaderWidget::columnWidthChanged, this, &KItemListView::slotHeaderColumnWidthChanged);
+ connect(m_headerWidget, &KItemListHeaderWidget::sidePaddingChanged, this, &KItemListView::slotSidePaddingChanged);
+ connect(m_headerWidget, &KItemListHeaderWidget::columnMoved, this, &KItemListView::slotHeaderColumnMoved);
+ connect(m_headerWidget, &KItemListHeaderWidget::sortOrderChanged, this, &KItemListView::sortOrderChanged);
+ connect(m_headerWidget, &KItemListHeaderWidget::sortRoleChanged, this, &KItemListView::sortRoleChanged);
+ connect(m_headerWidget, &KItemListHeaderWidget::columnHovered, this, &KItemListView::columnHovered);
+ connect(m_headerWidget, &KItemListHeaderWidget::columnUnHovered, this, &KItemListView::columnUnHovered);
+
+ m_layouter->setHeaderHeight(headerSize.height());
+ m_headerWidget->setVisible(true);
+ } else if (!visible && m_headerWidget->isVisible()) {
+ disconnect(m_headerWidget, &KItemListHeaderWidget::columnWidthChanged, this, &KItemListView::slotHeaderColumnWidthChanged);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::sidePaddingChanged, this, &KItemListView::slotSidePaddingChanged);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::columnMoved, this, &KItemListView::slotHeaderColumnMoved);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::sortOrderChanged, this, &KItemListView::sortOrderChanged);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::sortRoleChanged, this, &KItemListView::sortRoleChanged);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::columnHovered, this, &KItemListView::columnHovered);
+ disconnect(m_headerWidget, &KItemListHeaderWidget::columnUnHovered, this, &KItemListView::columnUnHovered);
+
+ m_layouter->setHeaderHeight(0);
+ m_headerWidget->setVisible(false);
+ }
+}
+
+bool KItemListView::isHeaderVisible() const
+{
+ return m_headerWidget->isVisible();
+}
+
+KItemListHeader *KItemListView::header() const
+{
+ return m_header;
+}
+
+QPixmap KItemListView::createDragPixmap(const KItemSet &indexes) const
+{
+ QPixmap pixmap;
+
+ if (indexes.count() == 1) {
+ KItemListWidget *item = m_visibleItems.value(indexes.first());
+ QGraphicsView *graphicsView = scene()->views()[0];
+ if (item && graphicsView) {
+ pixmap = item->createDragPixmap(nullptr, 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)
+{
+ KStandardItemListWidget *widget = qobject_cast<KStandardItemListWidget *>(m_visibleItems.value(index));
+ if (!widget || m_editingRole) {
+ return;
+ }
+
+ m_editingRole = true;
+ widget->setEditedRole(role);
+
+ connect(widget, &KItemListWidget::roleEditingCanceled, this, &KItemListView::slotRoleEditingCanceled);
+ connect(widget, &KItemListWidget::roleEditingFinished, this, &KItemListView::slotRoleEditingFinished);
+
+ connect(this, &KItemListView::scrollOffsetChanged, widget, &KStandardItemListWidget::finishRoleEditing);
+}
+
+void KItemListView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ QGraphicsWidget::paint(painter, option, widget);
+
+ for (auto animation : std::as_const(m_rubberBandAnimations)) {
+ QRectF rubberBandRect = animation->property(RubberPropertyName).toRectF();
+
+ const QPointF topLeft = rubberBandRect.topLeft();
+ if (scrollOrientation() == Qt::Vertical) {
+ rubberBandRect.moveTo(topLeft.x(), topLeft.y() - scrollOffset());
+ } else {
+ rubberBandRect.moveTo(topLeft.x() - scrollOffset(), topLeft.y());
+ }
+
+ QStyleOptionRubberBand opt;
+ initStyleOption(&opt);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ opt.rect = rubberBandRect.toRect();
+
+ painter->save();
+
+ painter->setOpacity(animation->currentValue().toReal());
+ style()->drawControl(QStyle::CE_RubberBand, &opt, painter);
+
+ painter->restore();
+ }
+
+ if (m_rubberBand->isActive()) {
+ QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(), m_rubberBand->endPosition()).normalized();
+
+ const QPointF topLeft = rubberBandRect.topLeft();
+ if (scrollOrientation() == Qt::Vertical) {
+ rubberBandRect.moveTo(topLeft.x(), topLeft.y() - scrollOffset());
+ } else {
+ rubberBandRect.moveTo(topLeft.x() - scrollOffset(), topLeft.y());
+ }
+
+ QStyleOptionRubberBand opt;
+ initStyleOption(&opt);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ opt.rect = rubberBandRect.toRect();
+ style()->drawControl(QStyle::CE_RubberBand, &opt, painter);
+ }
+
+ if (m_tapAndHoldIndicator->isActive()) {
+ const QPointF indicatorSize = m_tapAndHoldIndicator->endPosition();
+ const QRectF rubberBandRect =
+ QRectF(m_tapAndHoldIndicator->startPosition() - indicatorSize, (m_tapAndHoldIndicator->startPosition()) + indicatorSize).normalized();
+ QStyleOptionRubberBand opt;
+ initStyleOption(&opt);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ 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::Text).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);
+ }
+}
+
+QVariant KItemListView::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ if (change == QGraphicsItem::ItemSceneHasChanged && scene()) {
+ if (!scene()->views().isEmpty()) {
+ m_styleOption.palette = scene()->views().at(0)->palette();
+ }
+ }
+ return QGraphicsItem::itemChange(change, value);
+}
+
+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_alternateBackgrounds && ((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)
+{
+ if (m_styleOption == option) {
+ return;
+ }
+
+ 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);
+ }