+void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
+{
+ if (!m_view) {
+ return;
+ }
+
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
+ if (rubberBand->isActive()) {
+ const qreal diff = current - previous;
+ // TODO: Ideally just QCursor::pos() should be used as
+ // new end-position but it seems there is no easy way
+ // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
+ // (... or I just missed an easy way to do the mapping)
+ QPointF endPos = rubberBand->endPosition();
+ if (m_view->scrollOrientation() == Qt::Vertical) {
+ endPos.ry() += diff;
+ } else {
+ endPos.rx() += diff;
+ }
+
+ rubberBand->setEndPosition(endPos);
+ }
+}
+
+void KItemListController::slotRubberBandChanged()
+{
+ if (!m_view || !m_model || m_model->count() <= 0) {
+ return;
+ }
+
+ const KItemListRubberBand* rubberBand = m_view->rubberBand();
+ const QPointF startPos = rubberBand->startPosition();
+ const QPointF endPos = rubberBand->endPosition();
+ QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
+
+ const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
+ if (scrollVertical) {
+ rubberBandRect.translate(0, -m_view->scrollOffset());
+ } else {
+ rubberBandRect.translate(-m_view->scrollOffset(), 0);
+ }
+
+ if (!m_oldSelection.isEmpty()) {
+ // Clear the old selection that was available before the rubberband has
+ // been activated in case if no Shift- or Control-key are pressed
+ const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
+ QApplication::keyboardModifiers() & Qt::ControlModifier;
+ if (!shiftOrControlPressed) {
+ m_oldSelection.clear();
+ }
+ }
+
+ KItemSet selectedItems;
+
+ // Select all visible items that intersect with the rubberband
+ foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ const int index = widget->index();
+
+ const QRectF widgetRect = m_view->itemRect(index);
+ if (widgetRect.intersects(rubberBandRect)) {
+ const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
+ const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
+ if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
+ selectedItems.insert(index);
+ }
+ }
+ }
+
+ // Select all invisible items that intersect with the rubberband. Instead of
+ // iterating all items only the area which might be touched by the rubberband
+ // will be checked.
+ const bool increaseIndex = scrollVertical ?
+ startPos.y() > endPos.y(): startPos.x() > endPos.x();
+
+ int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
+ bool selectionFinished = false;
+ do {
+ const QRectF widgetRect = m_view->itemRect(index);
+ if (widgetRect.intersects(rubberBandRect)) {
+ selectedItems.insert(index);
+ }
+
+ if (increaseIndex) {
+ ++index;
+ selectionFinished = (index >= m_model->count()) ||
+ ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
+ (!scrollVertical && widgetRect.left() > rubberBandRect.right());
+ } else {
+ --index;
+ selectionFinished = (index < 0) ||
+ ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
+ (!scrollVertical && widgetRect.right() < rubberBandRect.left());
+ }
+ } while (!selectionFinished);
+
+ if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
+ // If Control is pressed, the selection state of all items in the rubberband is toggled.
+ // Therefore, the new selection contains:
+ // 1. All previously selected items which are not inside the rubberband, and
+ // 2. all items inside the rubberband which have not been selected previously.
+ m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems);
+ }
+ else {
+ m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
+ }
+}
+
+void KItemListController::startDragging()
+{
+ if (!m_view || !m_model) {
+ return;
+ }
+
+ const KItemSet selectedItems = m_selectionManager->selectedItems();
+ if (selectedItems.isEmpty()) {
+ return;
+ }
+
+ QMimeData* data = m_model->createMimeData(selectedItems);
+ if (!data) {
+ return;
+ }
+
+ // The created drag object will be owned and deleted
+ // by QApplication::activeWindow().
+ QDrag* drag = new QDrag(QApplication::activeWindow());
+ drag->setMimeData(data);
+
+ const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
+ drag->setPixmap(pixmap);
+
+ const QPoint hotSpot((pixmap.width() / pixmap.devicePixelRatio()) / 2, 0);
+ drag->setHotSpot(hotSpot);
+
+ drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::MoveAction);
+
+ QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropStart);
+ QAccessible::updateAccessibility(&accessibilityEvent);
+}
+
+KItemListWidget* KItemListController::hoveredWidget() const
+{
+ Q_ASSERT(m_view);
+
+ foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ if (widget->isHovered()) {
+ return widget;
+ }
+ }
+
+ return nullptr;
+}
+
+KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
+{
+ Q_ASSERT(m_view);
+
+ foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ const QPointF mappedPos = widget->mapFromItem(m_view, pos);
+
+ const bool hovered = widget->contains(mappedPos) &&
+ !widget->expansionToggleRect().contains(mappedPos);
+ if (hovered) {
+ return widget;
+ }
+ }
+
+ return nullptr;
+}
+
+void KItemListController::updateKeyboardAnchor()
+{
+ const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
+ m_keyboardAnchorIndex < m_model->count() &&
+ keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
+ if (!validAnchor) {
+ const int index = m_selectionManager->currentItem();
+ m_keyboardAnchorIndex = index;
+ m_keyboardAnchorPos = keyboardAnchorPos(index);
+ }
+}
+
+int KItemListController::nextRowIndex(int index) const
+{
+ if (m_keyboardAnchorIndex < 0) {
+ return index;
+ }
+
+ const int maxIndex = m_model->count() - 1;
+ if (index == maxIndex) {
+ return index;
+ }
+
+ // Calculate the index of the last column inside the row of the current index
+ int lastColumnIndex = index;
+ while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
+ ++lastColumnIndex;
+ if (lastColumnIndex >= maxIndex) {
+ return index;
+ }
+ }
+
+ // Based on the last column index go to the next row and calculate the nearest index
+ // that is below the current index
+ int nextRowIndex = lastColumnIndex + 1;
+ int searchIndex = nextRowIndex;
+ qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
+ while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
+ ++searchIndex;
+ const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
+ if (searchDiff < minDiff) {
+ minDiff = searchDiff;
+ nextRowIndex = searchIndex;
+ }
+ }
+
+ return nextRowIndex;
+}
+
+int KItemListController::previousRowIndex(int index) const
+{
+ if (m_keyboardAnchorIndex < 0 || index == 0) {
+ return index;
+ }
+
+ // Calculate the index of the first column inside the row of the current index
+ int firstColumnIndex = index;
+ while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
+ --firstColumnIndex;
+ if (firstColumnIndex <= 0) {
+ return index;
+ }
+ }
+
+ // Based on the first column index go to the previous row and calculate the nearest index
+ // that is above the current index
+ int previousRowIndex = firstColumnIndex - 1;
+ int searchIndex = previousRowIndex;
+ qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
+ while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
+ --searchIndex;
+ const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
+ if (searchDiff < minDiff) {
+ minDiff = searchDiff;
+ previousRowIndex = searchIndex;
+ }
+ }
+
+ return previousRowIndex;
+}
+
+qreal KItemListController::keyboardAnchorPos(int index) const
+{
+ const QRectF itemRect = m_view->itemRect(index);
+ if (!itemRect.isEmpty()) {
+ return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y();
+ }
+
+ return 0;
+}
+
+void KItemListController::updateExtendedSelectionRegion()
+{
+ if (m_view) {
+ const bool extend = (m_selectionBehavior != MultiSelection);
+ KItemListStyleOption option = m_view->styleOption();
+ if (option.extendedSelectionRegion != extend) {
+ option.extendedSelectionRegion = extend;
+ m_view->setStyleOption(option);
+ }
+ }
+}
+