+ if (useAlternateBackgrounds()) {
+ // Changing the group mode requires to update the alternate backgrounds
+ // as with the enabled group mode the altering is done on base of the first
+ // group item.
+ updateAlternateBackgrounds();
+ }
+ updateSiblingsInformation();
+ doLayout(NoAnimation);
+}
+
+void KItemListView::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ if (m_grouped) {
+ updateVisibleGroupHeaders();
+ doLayout(NoAnimation);
+ }
+}
+
+void KItemListView::slotSortRoleChanged(const QByteArray ¤t, const QByteArray &previous)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ if (m_grouped) {
+ updateVisibleGroupHeaders();
+ doLayout(NoAnimation);
+ }
+}
+
+void KItemListView::slotCurrentChanged(int current, int previous)
+{
+ Q_UNUSED(previous)
+
+ // In SingleSelection mode (e.g., in the Places Panel), the current item is
+ // always the selected item. It is not necessary to highlight the current item then.
+ if (m_controller->selectionBehavior() != KItemListController::SingleSelection) {
+ KItemListWidget *previousWidget = m_visibleItems.value(previous, nullptr);
+ if (previousWidget) {
+ previousWidget->setCurrent(false);
+ }
+
+ KItemListWidget *currentWidget = m_visibleItems.value(current, nullptr);
+ if (currentWidget) {
+ currentWidget->setCurrent(true);
+ }
+ }
+
+ QAccessibleEvent ev(this, QAccessible::Focus);
+ ev.setChild(current);
+ QAccessible::updateAccessibility(&ev);
+}
+
+void KItemListView::slotSelectionChanged(const KItemSet ¤t, const KItemSet &previous)
+{
+ Q_UNUSED(previous)
+
+ QHashIterator<int, KItemListWidget *> it(m_visibleItems);
+ while (it.hasNext()) {
+ it.next();
+ const int index = it.key();
+ KItemListWidget *widget = it.value();
+ widget->setSelected(current.contains(index));
+ }
+}
+
+void KItemListView::slotAnimationFinished(QGraphicsWidget *widget, KItemListViewAnimation::AnimationType type)
+{
+ KItemListWidget *itemListWidget = qobject_cast<KItemListWidget *>(widget);
+ Q_ASSERT(itemListWidget);
+
+ if (type == KItemListViewAnimation::DeleteAnimation) {
+ // As we recycle the widget in this case it is important to assure that no
+ // other animation has been started. This is a convention in KItemListView and
+ // not a requirement defined by KItemListViewAnimation.
+ Q_ASSERT(!m_animation->isStarted(itemListWidget));
+
+ // All KItemListWidgets that are animated by the DeleteAnimation are not maintained
+ // by m_visibleWidgets and must be deleted manually after the animation has
+ // been finished.
+ recycleGroupHeaderForWidget(itemListWidget);
+ widgetCreator()->recycle(itemListWidget);
+ } else {
+ const int index = itemListWidget->index();
+ const bool invisible = (index < m_layouter->firstVisibleIndex()) || (index > m_layouter->lastVisibleIndex());
+ if (invisible && !m_animation->isStarted(itemListWidget)) {
+ recycleWidget(itemListWidget);
+ }
+ }
+}
+
+void KItemListView::slotRubberBandPosChanged()
+{
+ update();
+}
+
+void KItemListView::slotRubberBandActivationChanged(bool active)
+{
+ if (active) {
+ connect(m_rubberBand, &KItemListRubberBand::startPositionChanged, this, &KItemListView::slotRubberBandPosChanged);
+ connect(m_rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListView::slotRubberBandPosChanged);
+ m_skipAutoScrollForRubberBand = true;
+ } else {
+ QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(), m_rubberBand->endPosition()).normalized();
+
+ auto animation = new QVariantAnimation(this);
+ animation->setStartValue(1.0);
+ animation->setEndValue(0.0);
+ animation->setDuration(RubberFadeSpeed);
+ animation->setProperty(RubberPropertyName, rubberBandRect);
+
+ QEasingCurve curve;
+ curve.setType(QEasingCurve::BezierSpline);
+ curve.addCubicBezierSegment(QPointF(0.4, 0.0), QPointF(1.0, 1.0), QPointF(1.0, 1.0));
+ animation->setEasingCurve(curve);
+
+ connect(animation, &QVariantAnimation::valueChanged, this, [=](const QVariant &) {
+ update();
+ });
+ connect(animation, &QVariantAnimation::finished, this, [=]() {
+ m_rubberBandAnimations.removeAll(animation);
+ delete animation;
+ });
+ animation->start();
+ m_rubberBandAnimations << animation;
+
+ disconnect(m_rubberBand, &KItemListRubberBand::startPositionChanged, this, &KItemListView::slotRubberBandPosChanged);
+ disconnect(m_rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListView::slotRubberBandPosChanged);
+ m_skipAutoScrollForRubberBand = false;
+ }
+
+ update();
+}
+
+void KItemListView::slotHeaderColumnWidthChanged(const QByteArray &role, qreal currentWidth, qreal previousWidth)
+{
+ Q_UNUSED(role)
+ Q_UNUSED(currentWidth)
+ Q_UNUSED(previousWidth)
+
+ m_headerWidget->setAutomaticColumnResizing(false);
+ applyColumnWidthsFromHeader();
+ doLayout(NoAnimation);
+}
+
+void KItemListView::slotSidePaddingChanged(qreal width)
+{
+ Q_UNUSED(width)
+ if (m_headerWidget->automaticColumnResizing()) {
+ applyAutomaticColumnWidths();
+ }
+ applyColumnWidthsFromHeader();
+ doLayout(NoAnimation);
+}
+
+void KItemListView::slotHeaderColumnMoved(const QByteArray &role, int currentIndex, int previousIndex)
+{
+ Q_ASSERT(m_visibleRoles[previousIndex] == role);
+
+ const QList<QByteArray> previous = m_visibleRoles;
+
+ QList<QByteArray> current = m_visibleRoles;
+ current.removeAt(previousIndex);
+ current.insert(currentIndex, role);
+
+ setVisibleRoles(current);
+
+ Q_EMIT visibleRolesChanged(current, previous);
+}
+
+void KItemListView::triggerAutoScrolling()
+{
+ if (!m_autoScrollTimer) {
+ return;
+ }
+
+ int pos = 0;
+ int visibleSize = 0;
+ if (scrollOrientation() == Qt::Vertical) {
+ pos = m_mousePos.y();
+ visibleSize = size().height();
+ } else {
+ pos = m_mousePos.x();
+ visibleSize = size().width();
+ }
+
+ if (m_autoScrollTimer->interval() == InitialAutoScrollDelay) {
+ m_autoScrollIncrement = 0;
+ }
+
+ m_autoScrollIncrement = calculateAutoScrollingIncrement(pos, visibleSize, m_autoScrollIncrement);
+ if (m_autoScrollIncrement == 0) {
+ // The mouse position is not above an autoscroll margin (the autoscroll timer
+ // will be restarted in mouseMoveEvent())
+ m_autoScrollTimer->stop();
+ return;
+ }
+
+ if (m_rubberBand->isActive() && m_skipAutoScrollForRubberBand) {
+ // If a rubberband selection is ongoing the autoscrolling may only get triggered
+ // if the direction of the rubberband is similar to the autoscroll direction. This
+ // prevents that starting to create a rubberband within the autoscroll margins starts
+ // an autoscrolling.
+
+ const qreal minDiff = 4; // Ignore any autoscrolling if the rubberband is very small
+ const qreal diff = (scrollOrientation() == Qt::Vertical) ? m_rubberBand->endPosition().y() - m_rubberBand->startPosition().y()
+ : m_rubberBand->endPosition().x() - m_rubberBand->startPosition().x();
+ if (qAbs(diff) < minDiff || (m_autoScrollIncrement < 0 && diff > 0) || (m_autoScrollIncrement > 0 && diff < 0)) {
+ // The rubberband direction is different from the scroll direction (e.g. the rubberband has
+ // been moved up although the autoscroll direction might be down)
+ m_autoScrollTimer->stop();
+ return;
+ }
+ }
+
+ // As soon as the autoscrolling has been triggered at least once despite having an active rubberband,
+ // the autoscrolling may not get skipped anymore until a new rubberband is created
+ m_skipAutoScrollForRubberBand = false;
+
+ const qreal maxVisibleOffset = qMax(qreal(0), maximumScrollOffset() - visibleSize);
+ const qreal newScrollOffset = qMin(scrollOffset() + m_autoScrollIncrement, maxVisibleOffset);
+ setScrollOffset(newScrollOffset);
+
+ // Trigger the autoscroll timer which will periodically call
+ // triggerAutoScrolling()
+ m_autoScrollTimer->start(RepeatingAutoScrollDelay);
+}
+
+void KItemListView::slotGeometryOfGroupHeaderParentChanged()
+{
+ KItemListWidget *widget = qobject_cast<KItemListWidget *>(sender());
+ Q_ASSERT(widget);
+ KItemListGroupHeader *groupHeader = m_visibleGroups.value(widget);
+ Q_ASSERT(groupHeader);
+ updateGroupHeaderLayout(widget);
+}
+
+void KItemListView::slotRoleEditingCanceled(int index, const QByteArray &role, const QVariant &value)
+{
+ disconnectRoleEditingSignals(index);
+
+ m_editingRole = false;
+ Q_EMIT roleEditingCanceled(index, role, value);
+}
+
+void KItemListView::slotRoleEditingFinished(int index, const QByteArray &role, const QVariant &value)
+{
+ disconnectRoleEditingSignals(index);
+
+ m_editingRole = false;
+ Q_EMIT roleEditingFinished(index, role, value);
+}
+
+void KItemListView::setController(KItemListController *controller)
+{
+ if (m_controller != controller) {
+ KItemListController *previous = m_controller;
+ if (previous) {
+ KItemListSelectionManager *selectionManager = previous->selectionManager();
+ disconnect(selectionManager, &KItemListSelectionManager::currentChanged, this, &KItemListView::slotCurrentChanged);
+ disconnect(selectionManager, &KItemListSelectionManager::selectionChanged, this, &KItemListView::slotSelectionChanged);
+ }
+
+ m_controller = controller;
+
+ if (controller) {
+ KItemListSelectionManager *selectionManager = controller->selectionManager();
+ connect(selectionManager, &KItemListSelectionManager::currentChanged, this, &KItemListView::slotCurrentChanged);
+ connect(selectionManager, &KItemListSelectionManager::selectionChanged, this, &KItemListView::slotSelectionChanged);
+ }
+
+ onControllerChanged(controller, previous);
+ }
+}
+
+void KItemListView::setModel(KItemModelBase *model)
+{
+ if (m_model == model) {
+ return;
+ }
+
+ KItemModelBase *previous = m_model;
+
+ if (m_model) {
+ disconnect(m_model, &KItemModelBase::itemsChanged, this, &KItemListView::slotItemsChanged);
+ disconnect(m_model, &KItemModelBase::itemsInserted, this, &KItemListView::slotItemsInserted);
+ disconnect(m_model, &KItemModelBase::itemsRemoved, this, &KItemListView::slotItemsRemoved);
+ disconnect(m_model, &KItemModelBase::itemsMoved, this, &KItemListView::slotItemsMoved);
+ disconnect(m_model, &KItemModelBase::groupsChanged, this, &KItemListView::slotGroupsChanged);
+ disconnect(m_model, &KItemModelBase::groupedSortingChanged, this, &KItemListView::slotGroupedSortingChanged);
+ disconnect(m_model, &KItemModelBase::sortOrderChanged, this, &KItemListView::slotSortOrderChanged);
+ disconnect(m_model, &KItemModelBase::sortRoleChanged, this, &KItemListView::slotSortRoleChanged);
+
+ m_sizeHintResolver->itemsRemoved(KItemRangeList() << KItemRange(0, m_model->count()));
+ }
+
+ m_model = model;
+ m_layouter->setModel(model);
+ m_grouped = model->groupedSorting();
+
+ if (m_model) {
+ connect(m_model, &KItemModelBase::itemsChanged, this, &KItemListView::slotItemsChanged);
+ connect(m_model, &KItemModelBase::itemsInserted, this, &KItemListView::slotItemsInserted);
+ connect(m_model, &KItemModelBase::itemsRemoved, this, &KItemListView::slotItemsRemoved);
+ connect(m_model, &KItemModelBase::itemsMoved, this, &KItemListView::slotItemsMoved);
+ connect(m_model, &KItemModelBase::groupsChanged, this, &KItemListView::slotGroupsChanged);
+ connect(m_model, &KItemModelBase::groupedSortingChanged, this, &KItemListView::slotGroupedSortingChanged);
+ connect(m_model, &KItemModelBase::sortOrderChanged, this, &KItemListView::slotSortOrderChanged);
+ connect(m_model, &KItemModelBase::sortRoleChanged, this, &KItemListView::slotSortRoleChanged);
+
+ const int itemCount = m_model->count();
+ if (itemCount > 0) {
+ slotItemsInserted(KItemRangeList() << KItemRange(0, itemCount));