#include <QApplication>
#include <QDrag>
#include <QEvent>
+#include <QGraphicsScene>
#include <QGraphicsSceneEvent>
+#include <QGraphicsView>
#include <QMimeData>
+#include <QTimer>
#include <KGlobalSettings>
#include <KDebug>
KItemListController::KItemListController(QObject* parent) :
QObject(parent),
- m_dragging(false),
+ m_selectionTogglePressed(false),
m_selectionBehavior(NoSelection),
m_model(0),
m_view(0),
m_keyboardManager(new KItemListKeyboardSearchManager(this)),
m_pressedIndex(-1),
m_pressedMousePos(),
- m_oldSelection()
+ m_autoActivationTimer(0),
+ m_oldSelection(),
+ m_keyboardAnchorIndex(-1),
+ m_keyboardAnchorPos(0)
{
connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)),
this, SLOT(slotChangeCurrentItem(QString,bool)));
+
+ m_autoActivationTimer = new QTimer(this);
+ m_autoActivationTimer->setSingleShot(true);
+ m_autoActivationTimer->setInterval(-1);
+ connect(m_autoActivationTimer, SIGNAL(timeout()), this, SLOT(slotAutoActivationTimeout()));
}
KItemListController::~KItemListController()
KItemListView* oldView = m_view;
if (oldView) {
- disconnect(oldView, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+ disconnect(oldView, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
}
m_view = view;
if (m_view) {
m_view->setController(this);
m_view->setModel(m_model);
- connect(m_view, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+ connect(m_view, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
}
emit viewChanged(m_view, oldView);
return m_selectionBehavior;
}
+void KItemListController::setAutoActivationDelay(int delay)
+{
+ m_autoActivationTimer->setInterval(delay);
+}
+
+int KItemListController::autoActivationDelay() const
+{
+ return m_autoActivationTimer->interval();
+}
+
bool KItemListController::showEvent(QShowEvent* event)
{
Q_UNUSED(event);
bool KItemListController::keyPressEvent(QKeyEvent* event)
{
+ int index = m_selectionManager->currentItem();
+ int key = event->key();
+
+ // Handle the expanding/collapsing of items
+ if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
+ if (key == Qt::Key_Right) {
+ if (m_model->setExpanded(index, true)) {
+ return true;
+ }
+ } else if (key == Qt::Key_Left) {
+ if (m_model->setExpanded(index, false)) {
+ return true;
+ }
+ }
+ }
+
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
const bool controlPressed = event->modifiers() & Qt::ControlModifier;
const bool shiftOrControlPressed = shiftPressed || controlPressed;
- int index = m_selectionManager->currentItem();
const int itemCount = m_model->count();
- const int itemsPerRow = m_view->itemsPerOffset();
// For horizontal scroll orientation, transform
// the arrow keys to simplify the event handling.
- int key = event->key();
if (m_view->scrollOrientation() == Qt::Horizontal) {
switch (key) {
case Qt::Key_Up: key = Qt::Key_Left; break;
default: break;
}
}
+
+ const bool selectSingleItem = itemCount == 1 &&
+ (key == Qt::Key_Home || key == Qt::Key_End ||
+ key == Qt::Key_Up || key == Qt::Key_Down ||
+ key == Qt::Key_Left || key == Qt::Key_Right);
+ if (selectSingleItem) {
+ const int current = m_selectionManager->currentItem();
+ m_selectionManager->setSelected(current);
+ return true;
+ }
switch (key) {
case Qt::Key_Home:
case Qt::Key_Left:
if (index > 0) {
- index--;
+ --index;
+ m_keyboardAnchorIndex = index;
+ m_keyboardAnchorPos = keyboardAnchorPos(index);
}
break;
case Qt::Key_Right:
if (index < itemCount - 1) {
- index++;
+ ++index;
+ m_keyboardAnchorIndex = index;
+ m_keyboardAnchorPos = keyboardAnchorPos(index);
}
break;
case Qt::Key_Up:
- if (index >= itemsPerRow) {
- index -= itemsPerRow;
- }
+ updateKeyboardAnchor();
+ index = previousRowIndex();
break;
case Qt::Key_Down:
- if (index + itemsPerRow < itemCount) {
- // We are not in the last row yet.
- index += itemsPerRow;
- }
- else {
- // We are either in the last row already, or we are in the second-last row,
- // and there is no item below the current item.
- // In the latter case, we jump to the very last item.
- const int currentColumn = index % itemsPerRow;
- const int lastItemColumn = (itemCount - 1) % itemsPerRow;
- const bool inLastRow = currentColumn < lastItemColumn;
- if (!inLastRow) {
- index = itemCount - 1;
- }
- }
+ updateKeyboardAnchor();
+ index = nextRowIndex();
break;
case Qt::Key_Enter:
- case Qt::Key_Return:
- emit itemActivated(index);
+ case Qt::Key_Return: {
+ const QSet<int> selectedItems = m_selectionManager->selectedItems();
+ if (selectedItems.count() >= 2) {
+ emit itemsActivated(selectedItems);
+ } else if (selectedItems.count() == 1) {
+ emit itemActivated(selectedItems.toList().first());
+ } else {
+ emit itemActivated(index);
+ }
break;
+ }
case Qt::Key_Space:
if (controlPressed) {
m_selectionManager->endAnchoredSelection();
m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
m_selectionManager->beginAnchoredSelection(index);
- break;
+ } else {
+ const int current = m_selectionManager->currentItem();
+ m_selectionManager->setSelected(current);
}
+ break;
+
+ case Qt::Key_Menu: {
+ // Emit the signal itemContextMenuRequested() in case if at least one
+ // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
+ const QSet<int> selectedItems = m_selectionManager->selectedItems();
+ int index = -1;
+ if (selectedItems.count() >= 2) {
+ const int currentItemIndex = m_selectionManager->currentItem();
+ index = selectedItems.contains(currentItemIndex)
+ ? currentItemIndex : selectedItems.toList().first();
+ } else if (selectedItems.count() == 1) {
+ index = selectedItems.toList().first();
+ }
+
+ if (index >= 0) {
+ const QRectF contextRect = m_view->itemContextRect(index);
+ const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
+ emit itemContextMenuRequested(index, pos);
+ } else {
+ emit viewContextMenuRequested(QCursor::pos());
+ }
+ break;
+ }
default:
m_keyboardManager->addKeys(event->text());
if (!shiftPressed) {
m_selectionManager->beginAnchoredSelection(index);
}
+
+ m_view->scrollToItem(index);
}
return true;
}
void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
{
- if (!m_model) {
+ if (!m_model || m_model->count() == 0) {
return;
}
const int currentIndex = m_selectionManager->currentItem();
int index;
if (searchFromNextItem) {
index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
- }
- else {
+ } else {
index = m_model->indexForKeyboardSearch(text, currentIndex);
}
if (index >= 0) {
m_selectionManager->clearSelection();
m_selectionManager->setSelected(index, 1);
m_selectionManager->beginAnchoredSelection(index);
+ m_view->scrollToItem(index);
+ }
+}
+
+void KItemListController::slotAutoActivationTimeout()
+{
+ if (!m_model || !m_view) {
+ return;
+ }
+
+ const int index = m_autoActivationTimer->property("index").toInt();
+ if (index < 0 || index >= m_model->count()) {
+ return;
+ }
+
+ if (m_model->supportsDropping(index)) {
+ if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
+ const bool expanded = m_model->isExpanded(index);
+ m_model->setExpanded(index, !expanded);
+ } else {
+ emit itemActivated(index);
+ }
}
}
return false;
}
- m_dragging = false;
m_pressedMousePos = transform.map(event->pos());
m_pressedIndex = m_view->itemAt(m_pressedMousePos);
+ if (m_pressedIndex >= 0) {
+ emit itemPressed(m_pressedIndex, event->button());
+ }
if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) {
m_selectionManager->setCurrentItem(m_pressedIndex);
return true;
}
+ m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
+ if (m_selectionTogglePressed) {
+ m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
+ // The previous anchored selection has been finished already in
+ // KItemListSelectionManager::setSelected(). We can safely change
+ // the current item and start a new anchored selection now.
+ m_selectionManager->setCurrentItem(m_pressedIndex);
+ m_selectionManager->beginAnchoredSelection(m_pressedIndex);
+ return true;
+ }
+
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
const bool controlPressed = event->modifiers() & Qt::ControlModifier;
- if (m_selectionBehavior == SingleSelection) {
+ // The previous selection is cleared if either
+ // 1. The selection mode is SingleSelection, or
+ // 2. the selection mode is MultiSelection, and *none* of the following conditions are met:
+ // a) Shift or Control are pressed.
+ // b) The clicked item is selected already. In that case, the user might want to:
+ // - start dragging multiple items, or
+ // - open the context menu and perform an action for all selected items.
+ const bool shiftOrControlPressed = shiftPressed || controlPressed;
+ const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex);
+ const bool clearSelection = m_selectionBehavior == SingleSelection ||
+ (!shiftOrControlPressed && !pressedItemAlreadySelected);
+ if (clearSelection) {
m_selectionManager->clearSelection();
}
break;
}
+ if (event->buttons() & Qt::RightButton) {
+ emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
+ }
+
return true;
- } else {
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ }
+
+ if (event->buttons() & Qt::RightButton) {
+ const QRectF headerBounds = m_view->headerBoundaries();
+ if (headerBounds.contains(event->pos())) {
+ emit headerContextMenuRequested(event->screenPos());
+ } else {
+ emit viewContextMenuRequested(event->screenPos());
+ }
+ return true;
+ }
+
+ if (m_selectionBehavior == MultiSelection) {
QPointF startPos = m_pressedMousePos;
if (m_view->scrollOrientation() == Qt::Vertical) {
- startPos.ry() += m_view->offset();
+ startPos.ry() += m_view->scrollOffset();
if (m_view->itemSize().width() < 0) {
// Use a special rubberband for views that have only one column and
// expand the rubberband to use the whole width of the view.
startPos.setX(0);
}
} else {
- startPos.rx() += m_view->offset();
+ startPos.rx() += m_view->scrollOffset();
}
m_oldSelection = m_selectionManager->selectedItems();
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
rubberBand->setStartPosition(startPos);
rubberBand->setEndPosition(startPos);
rubberBand->setActive(true);
if (m_pressedIndex >= 0) {
// Check whether a dragging should be started
- if (!m_dragging && (event->buttons() & Qt::LeftButton)) {
+ if (event->buttons() & Qt::LeftButton) {
const QPointF pos = transform.map(event->pos());
- const qreal minDragDiff = 4;
- const bool hasMinDragDiff = qAbs(pos.x() - m_pressedMousePos.x()) >= minDragDiff ||
- qAbs(pos.y() - m_pressedMousePos.y()) >= minDragDiff;
- if (hasMinDragDiff && startDragging()) {
- m_dragging = true;
+ if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+ if (!m_selectionManager->isSelected(m_pressedIndex)) {
+ // Always assure that the dragged item gets selected. Usually this is already
+ // done on the mouse-press event, but when using the selection-toggle on a
+ // selected item the dragged item is not selected yet.
+ m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
+ }
+ startDragging();
}
}
} else {
KItemListRubberBand* rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
QPointF endPos = transform.map(event->pos());
+
+ // Update the current item.
+ const int newCurrent = m_view->itemAt(endPos);
+ if (newCurrent >= 0) {
+ // It's expected that the new current index is also the new anchor (bug 163451).
+ m_selectionManager->endAnchoredSelection();
+ m_selectionManager->setCurrentItem(newCurrent);
+ m_selectionManager->beginAnchoredSelection(newCurrent);
+ }
+
if (m_view->scrollOrientation() == Qt::Vertical) {
- endPos.ry() += m_view->offset();
+ endPos.ry() += m_view->scrollOffset();
if (m_view->itemSize().width() < 0) {
// Use a special rubberband for views that have only one column and
// expand the rubberband to use the whole width of the view.
endPos.setX(m_view->size().width());
}
} else {
- endPos.rx() += m_view->offset();
+ endPos.rx() += m_view->scrollOffset();
}
rubberBand->setEndPosition(endPos);
}
return false;
}
+ if (m_pressedIndex >= 0) {
+ emit itemReleased(m_pressedIndex, event->button());
+ }
+
+ const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
+ if (isAboveSelectionToggle) {
+ m_selectionTogglePressed = false;
+ return true;
+ }
+
+ if (!isAboveSelectionToggle && m_selectionTogglePressed) {
+ m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
+ m_selectionTogglePressed = false;
+ return true;
+ }
+
const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
event->modifiers() & Qt::ControlModifier;
- bool clearSelection = !shiftOrControlPressed && !m_dragging && !(event->button() == Qt::RightButton);
-
KItemListRubberBand* rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
disconnect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
rubberBand->setActive(false);
m_oldSelection.clear();
m_view->setAutoScroll(false);
-
- if (rubberBand->startPosition() != rubberBand->endPosition()) {
- clearSelection = false;
- }
}
const QPointF pos = transform.map(event->pos());
if (index >= 0 && index == m_pressedIndex) {
// The release event is done above the same item as the press event
- if (clearSelection) {
- // Clear the previous selection but reselect the current index
- m_selectionManager->setSelectedItems(QSet<int>() << index);
- }
-
if (event->button() & Qt::LeftButton) {
bool emitItemActivated = true;
if (m_view->isAboveExpansionToggle(index, pos)) {
+ const bool expanded = m_model->isExpanded(index);
+ m_model->setExpanded(index, !expanded);
+
emit itemExpansionToggleClicked(index);
emitItemActivated = false;
} else if (shiftOrControlPressed) {
}
} else if (event->button() & Qt::MidButton) {
emit itemMiddleClicked(index);
- } else if (event->button() & Qt::RightButton) {
- emit contextMenuRequested(index, QPointF(event->pos()));
}
- } else if (clearSelection) {
- m_selectionManager->clearSelection();
}
- m_dragging = false;
m_pressedMousePos = QPointF();
m_pressedIndex = -1;
return false;
const int index = m_view->itemAt(pos);
bool emitItemActivated = !KGlobalSettings::singleClick() &&
- (event->button() & Qt::LeftButton) &&
- index >= 0 && index < m_model->count();
+ (event->button() & Qt::LeftButton) &&
+ index >= 0 && index < m_model->count();
if (emitItemActivated) {
emit itemActivated(index);
}
bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
Q_UNUSED(transform);
+ if (!m_model || !m_view) {
+ return false;
+ }
+
+ KItemListWidget* oldHoveredWidget = hoveredWidget();
+
+ const QPointF pos = transform.map(event->pos());
+ KItemListWidget* newHoveredWidget = widgetForPos(pos);
+
+ if (oldHoveredWidget != newHoveredWidget) {
+ m_autoActivationTimer->stop();
+
+ if (oldHoveredWidget) {
+ oldHoveredWidget->setHovered(false);
+ emit itemUnhovered(oldHoveredWidget->index());
+ }
+
+ if (newHoveredWidget) {
+ const int index = newHoveredWidget->index();
+ if (m_model->supportsDropping(index)) {
+ newHoveredWidget->setHovered(true);
+ }
+ emit itemHovered(index);
+
+ if (m_autoActivationTimer->interval() >= 0) {
+ m_autoActivationTimer->setProperty("index", index);
+ m_autoActivationTimer->start();
+ }
+ }
+ }
+
return false;
}
bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
- Q_UNUSED(transform);
+ Q_UNUSED(transform)
+ if (!m_view) {
+ return false;
+ }
- m_dragging = false;
- return false;
+ m_autoActivationTimer->stop();
+
+ const QPointF pos = transform.map(event->pos());
+ const int index = m_view->itemAt(pos);
+ emit itemDropEvent(index, event);
+
+ return true;
}
bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
- // The implementation assumes that only one item can get hovered no matter
- // whether they overlap or not.
-
Q_UNUSED(transform);
if (!m_model || !m_view) {
return false;
}
- // Search the previously hovered item that might get unhovered
- KItemListWidget* unhoveredWidget = 0;
- foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
- if (widget->isHovered()) {
- unhoveredWidget = widget;
- break;
- }
- }
-
- // Search the currently hovered item
- KItemListWidget* hoveredWidget = 0;
- foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
- const QPointF mappedPos = widget->mapFromItem(m_view, event->pos());
-
- const bool hovered = widget->contains(mappedPos) &&
- !widget->expansionToggleRect().contains(mappedPos) &&
- !widget->selectionToggleRect().contains(mappedPos);
- if (hovered) {
- hoveredWidget = widget;
- break;
- }
- }
+ KItemListWidget* oldHoveredWidget = hoveredWidget();
+ const QPointF pos = transform.map(event->pos());
+ KItemListWidget* newHoveredWidget = widgetForPos(pos);
- if (unhoveredWidget != hoveredWidget) {
- if (unhoveredWidget) {
- unhoveredWidget->setHovered(false);
- emit itemUnhovered(unhoveredWidget->index());
+ if (oldHoveredWidget != newHoveredWidget) {
+ if (oldHoveredWidget) {
+ oldHoveredWidget->setHovered(false);
+ emit itemUnhovered(oldHoveredWidget->index());
}
- if (hoveredWidget) {
- hoveredWidget->setHovered(true);
- emit itemHovered(hoveredWidget->index());
+ if (newHoveredWidget) {
+ newHoveredWidget->setHovered(true);
+ emit itemHovered(newHoveredWidget->index());
}
}
return false;
}
-void KItemListController::slotViewOffsetChanged(qreal current, qreal previous)
+void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
{
if (!m_view) {
return;
const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
if (scrollVertical) {
- rubberBandRect.translate(0, -m_view->offset());
+ rubberBandRect.translate(0, -m_view->scrollOffset());
} else {
- rubberBandRect.translate(-m_view->offset(), 0);
+ rubberBandRect.translate(-m_view->scrollOffset(), 0);
}
if (!m_oldSelection.isEmpty()) {
foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
const int index = widget->index();
- const QRectF widgetRect = m_view->itemBoundingRect(index);
+ const QRectF widgetRect = m_view->itemRect(index);
if (widgetRect.intersects(rubberBandRect)) {
- const QRectF iconRect = widget->iconBoundingRect().translated(widgetRect.topLeft());
- const QRectF textRect = widget->textBoundingRect().translated(widgetRect.topLeft());
+ 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);
}
int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
bool selectionFinished = false;
do {
- const QRectF widgetRect = m_view->itemBoundingRect(index);
+ const QRectF widgetRect = m_view->itemRect(index);
if (widgetRect.intersects(rubberBandRect)) {
selectedItems.insert(index);
}
}
}
-bool KItemListController::startDragging()
+void KItemListController::startDragging()
{
if (!m_view || !m_model) {
- return false;
+ return;
}
const QSet<int> selectedItems = m_selectionManager->selectedItems();
+ if (selectedItems.isEmpty()) {
+ return;
+ }
+
QMimeData* data = m_model->createMimeData(selectedItems);
if (!data) {
- return false;
+ return;
}
// The created drag object will be owned and deleted
const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
drag->setPixmap(pixmap);
- drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::IgnoreAction);
- return true;
+ drag->exec(Qt::CopyAction);
+}
+
+KItemListWidget* KItemListController::hoveredWidget() const
+{
+ Q_ASSERT(m_view);
+
+ foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ if (widget->isHovered()) {
+ return widget;
+ }
+ }
+
+ return 0;
+}
+
+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 0;
+}
+
+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() const
+{
+ const int currentIndex = m_selectionManager->currentItem();
+ if (m_keyboardAnchorIndex < 0) {
+ return currentIndex;
+ }
+
+ const int maxIndex = m_model->count() - 1;
+ if (currentIndex == maxIndex) {
+ return currentIndex;
+ }
+
+ // Calculate the index of the last column inside the row of the current index
+ int lastColumnIndex = currentIndex;
+ while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
+ ++lastColumnIndex;
+ if (lastColumnIndex >= maxIndex) {
+ return currentIndex;
+ }
+ }
+
+ // 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() const
+{
+ const int currentIndex = m_selectionManager->currentItem();
+ if (m_keyboardAnchorIndex < 0 || currentIndex == 0) {
+ return currentIndex;
+ }
+
+ // Calculate the index of the first column inside the row of the current index
+ int firstColumnIndex = currentIndex;
+ while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
+ --firstColumnIndex;
+ if (firstColumnIndex <= 0) {
+ return currentIndex;
+ }
+ }
+
+ // 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;
}
#include "kitemlistcontroller.moc"