]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistcontroller.cpp
Merge branch 'release/20.04'
[dolphin.git] / src / kitemviews / kitemlistcontroller.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2012 by Frank Reininghaus <frank78ac@googlemail.com> *
4 * *
5 * Based on the Itemviews NG project from Trolltech Labs *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
21 ***************************************************************************/
22
23 #include "kitemlistcontroller.h"
24
25 #include "kitemlistselectionmanager.h"
26 #include "kitemlistview.h"
27 #include "private/kitemlistkeyboardsearchmanager.h"
28 #include "private/kitemlistrubberband.h"
29 #include "views/draganddrophelper.h"
30
31 #include <QAccessible>
32 #include <QApplication>
33 #include <QDrag>
34 #include <QGraphicsScene>
35 #include <QGraphicsSceneEvent>
36 #include <QGraphicsView>
37 #include <QMimeData>
38 #include <QTimer>
39
40 KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) :
41 QObject(parent),
42 m_singleClickActivationEnforced(false),
43 m_selectionTogglePressed(false),
44 m_clearSelectionIfItemsAreNotDragged(false),
45 m_selectionBehavior(NoSelection),
46 m_autoActivationBehavior(ActivationAndExpansion),
47 m_mouseDoubleClickAction(ActivateItemOnly),
48 m_model(nullptr),
49 m_view(nullptr),
50 m_selectionManager(new KItemListSelectionManager(this)),
51 m_keyboardManager(new KItemListKeyboardSearchManager(this)),
52 m_pressedIndex(-1),
53 m_pressedMousePos(),
54 m_autoActivationTimer(nullptr),
55 m_oldSelection(),
56 m_keyboardAnchorIndex(-1),
57 m_keyboardAnchorPos(0)
58 {
59 connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem,
60 this, &KItemListController::slotChangeCurrentItem);
61 connect(m_selectionManager, &KItemListSelectionManager::currentChanged,
62 m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged);
63 connect(m_selectionManager, &KItemListSelectionManager::selectionChanged,
64 m_keyboardManager, &KItemListKeyboardSearchManager::slotSelectionChanged);
65
66 m_autoActivationTimer = new QTimer(this);
67 m_autoActivationTimer->setSingleShot(true);
68 m_autoActivationTimer->setInterval(-1);
69 connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout);
70
71 setModel(model);
72 setView(view);
73 }
74
75 KItemListController::~KItemListController()
76 {
77 setView(nullptr);
78 Q_ASSERT(!m_view);
79
80 setModel(nullptr);
81 Q_ASSERT(!m_model);
82 }
83
84 void KItemListController::setModel(KItemModelBase* model)
85 {
86 if (m_model == model) {
87 return;
88 }
89
90 KItemModelBase* oldModel = m_model;
91 if (oldModel) {
92 oldModel->deleteLater();
93 }
94
95 m_model = model;
96 if (m_model) {
97 m_model->setParent(this);
98 }
99
100 if (m_view) {
101 m_view->setModel(m_model);
102 }
103
104 m_selectionManager->setModel(m_model);
105
106 emit modelChanged(m_model, oldModel);
107 }
108
109 KItemModelBase* KItemListController::model() const
110 {
111 return m_model;
112 }
113
114 KItemListSelectionManager* KItemListController::selectionManager() const
115 {
116 return m_selectionManager;
117 }
118
119 void KItemListController::setView(KItemListView* view)
120 {
121 if (m_view == view) {
122 return;
123 }
124
125 KItemListView* oldView = m_view;
126 if (oldView) {
127 disconnect(oldView, &KItemListView::scrollOffsetChanged, this, &KItemListController::slotViewScrollOffsetChanged);
128 oldView->deleteLater();
129 }
130
131 m_view = view;
132
133 if (m_view) {
134 m_view->setParent(this);
135 m_view->setController(this);
136 m_view->setModel(m_model);
137 connect(m_view, &KItemListView::scrollOffsetChanged, this, &KItemListController::slotViewScrollOffsetChanged);
138 updateExtendedSelectionRegion();
139 }
140
141 emit viewChanged(m_view, oldView);
142 }
143
144 KItemListView* KItemListController::view() const
145 {
146 return m_view;
147 }
148
149 void KItemListController::setSelectionBehavior(SelectionBehavior behavior)
150 {
151 m_selectionBehavior = behavior;
152 updateExtendedSelectionRegion();
153 }
154
155 KItemListController::SelectionBehavior KItemListController::selectionBehavior() const
156 {
157 return m_selectionBehavior;
158 }
159
160 void KItemListController::setAutoActivationBehavior(AutoActivationBehavior behavior)
161 {
162 m_autoActivationBehavior = behavior;
163 }
164
165 KItemListController::AutoActivationBehavior KItemListController::autoActivationBehavior() const
166 {
167 return m_autoActivationBehavior;
168 }
169
170 void KItemListController::setMouseDoubleClickAction(MouseDoubleClickAction action)
171 {
172 m_mouseDoubleClickAction = action;
173 }
174
175 KItemListController::MouseDoubleClickAction KItemListController::mouseDoubleClickAction() const
176 {
177 return m_mouseDoubleClickAction;
178 }
179
180 int KItemListController::indexCloseToMousePressedPosition() const
181 {
182 QHashIterator<KItemListWidget*, KItemListGroupHeader*> it(m_view->m_visibleGroups);
183 while (it.hasNext()) {
184 it.next();
185 KItemListGroupHeader *groupHeader = it.value();
186 const QPointF mappedToGroup = groupHeader->mapFromItem(nullptr, m_pressedMousePos);
187 if (groupHeader->contains(mappedToGroup)) {
188 return it.key()->index();
189 }
190 }
191 return -1;
192 }
193
194 void KItemListController::setAutoActivationDelay(int delay)
195 {
196 m_autoActivationTimer->setInterval(delay);
197 }
198
199 int KItemListController::autoActivationDelay() const
200 {
201 return m_autoActivationTimer->interval();
202 }
203
204 void KItemListController::setSingleClickActivationEnforced(bool singleClick)
205 {
206 m_singleClickActivationEnforced = singleClick;
207 }
208
209 bool KItemListController::singleClickActivationEnforced() const
210 {
211 return m_singleClickActivationEnforced;
212 }
213
214 bool KItemListController::keyPressEvent(QKeyEvent* event)
215 {
216 int index = m_selectionManager->currentItem();
217 int key = event->key();
218
219 // Handle the expanding/collapsing of items
220 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
221 if (key == Qt::Key_Right) {
222 if (m_model->setExpanded(index, true)) {
223 return true;
224 }
225 } else if (key == Qt::Key_Left) {
226 if (m_model->setExpanded(index, false)) {
227 return true;
228 }
229 }
230 }
231
232 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
233 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
234 const bool shiftOrControlPressed = shiftPressed || controlPressed;
235 const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End ||
236 key == Qt::Key_PageUp || key == Qt::Key_PageDown ||
237 key == Qt::Key_Up || key == Qt::Key_Down ||
238 key == Qt::Key_Left || key == Qt::Key_Right;
239
240 const int itemCount = m_model->count();
241
242 // For horizontal scroll orientation, transform
243 // the arrow keys to simplify the event handling.
244 if (m_view->scrollOrientation() == Qt::Horizontal) {
245 switch (key) {
246 case Qt::Key_Up: key = Qt::Key_Left; break;
247 case Qt::Key_Down: key = Qt::Key_Right; break;
248 case Qt::Key_Left: key = Qt::Key_Up; break;
249 case Qt::Key_Right: key = Qt::Key_Down; break;
250 default: break;
251 }
252 }
253
254 const bool selectSingleItem = m_selectionBehavior != NoSelection && itemCount == 1 && navigationPressed;
255
256 if (selectSingleItem) {
257 const int current = m_selectionManager->currentItem();
258 m_selectionManager->setSelected(current);
259 return true;
260 }
261
262 switch (key) {
263 case Qt::Key_Home:
264 index = 0;
265 m_keyboardAnchorIndex = index;
266 m_keyboardAnchorPos = keyboardAnchorPos(index);
267 break;
268
269 case Qt::Key_End:
270 index = itemCount - 1;
271 m_keyboardAnchorIndex = index;
272 m_keyboardAnchorPos = keyboardAnchorPos(index);
273 break;
274
275 case Qt::Key_Left:
276 if (index > 0) {
277 const int expandedParentsCount = m_model->expandedParentsCount(index);
278 if (expandedParentsCount == 0) {
279 --index;
280 } else {
281 // Go to the parent of the current item.
282 do {
283 --index;
284 } while (index > 0 && m_model->expandedParentsCount(index) == expandedParentsCount);
285 }
286 m_keyboardAnchorIndex = index;
287 m_keyboardAnchorPos = keyboardAnchorPos(index);
288 }
289 break;
290
291 case Qt::Key_Right:
292 if (index < itemCount - 1) {
293 ++index;
294 m_keyboardAnchorIndex = index;
295 m_keyboardAnchorPos = keyboardAnchorPos(index);
296 }
297 break;
298
299 case Qt::Key_Up:
300 updateKeyboardAnchor();
301 index = previousRowIndex(index);
302 break;
303
304 case Qt::Key_Down:
305 updateKeyboardAnchor();
306 index = nextRowIndex(index);
307 break;
308
309 case Qt::Key_PageUp:
310 if (m_view->scrollOrientation() == Qt::Horizontal) {
311 // The new current index should correspond to the first item in the current column.
312 int newIndex = qMax(index - 1, 0);
313 while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) {
314 index = newIndex;
315 newIndex = qMax(index - 1, 0);
316 }
317 m_keyboardAnchorIndex = index;
318 m_keyboardAnchorPos = keyboardAnchorPos(index);
319 } else {
320 const qreal currentItemBottom = m_view->itemRect(index).bottomLeft().y();
321 const qreal height = m_view->geometry().height();
322
323 // The new current item should be the first item in the current
324 // column whose itemRect's top coordinate is larger than targetY.
325 const qreal targetY = currentItemBottom - height;
326
327 updateKeyboardAnchor();
328 int newIndex = previousRowIndex(index);
329 do {
330 index = newIndex;
331 updateKeyboardAnchor();
332 newIndex = previousRowIndex(index);
333 } while (m_view->itemRect(newIndex).topLeft().y() > targetY && newIndex != index);
334 }
335 break;
336
337 case Qt::Key_PageDown:
338 if (m_view->scrollOrientation() == Qt::Horizontal) {
339 // The new current index should correspond to the last item in the current column.
340 int newIndex = qMin(index + 1, m_model->count() - 1);
341 while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) {
342 index = newIndex;
343 newIndex = qMin(index + 1, m_model->count() - 1);
344 }
345 m_keyboardAnchorIndex = index;
346 m_keyboardAnchorPos = keyboardAnchorPos(index);
347 } else {
348 const qreal currentItemTop = m_view->itemRect(index).topLeft().y();
349 const qreal height = m_view->geometry().height();
350
351 // The new current item should be the last item in the current
352 // column whose itemRect's bottom coordinate is smaller than targetY.
353 const qreal targetY = currentItemTop + height;
354
355 updateKeyboardAnchor();
356 int newIndex = nextRowIndex(index);
357 do {
358 index = newIndex;
359 updateKeyboardAnchor();
360 newIndex = nextRowIndex(index);
361 } while (m_view->itemRect(newIndex).bottomLeft().y() < targetY && newIndex != index);
362 }
363 break;
364
365 case Qt::Key_Enter:
366 case Qt::Key_Return: {
367 const KItemSet selectedItems = m_selectionManager->selectedItems();
368 if (selectedItems.count() >= 2) {
369 emit itemsActivated(selectedItems);
370 } else if (selectedItems.count() == 1) {
371 emit itemActivated(selectedItems.first());
372 } else {
373 emit itemActivated(index);
374 }
375 break;
376 }
377
378 case Qt::Key_Menu: {
379 // Emit the signal itemContextMenuRequested() in case if at least one
380 // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
381 const KItemSet selectedItems = m_selectionManager->selectedItems();
382 int index = -1;
383 if (selectedItems.count() >= 2) {
384 const int currentItemIndex = m_selectionManager->currentItem();
385 index = selectedItems.contains(currentItemIndex)
386 ? currentItemIndex : selectedItems.first();
387 } else if (selectedItems.count() == 1) {
388 index = selectedItems.first();
389 }
390
391 if (index >= 0) {
392 const QRectF contextRect = m_view->itemContextRect(index);
393 const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
394 emit itemContextMenuRequested(index, pos);
395 } else {
396 emit viewContextMenuRequested(QCursor::pos());
397 }
398 break;
399 }
400
401 case Qt::Key_Escape:
402 if (m_selectionBehavior != SingleSelection) {
403 m_selectionManager->clearSelection();
404 }
405 m_keyboardManager->cancelSearch();
406 emit escapePressed();
407 break;
408
409 case Qt::Key_Space:
410 if (m_selectionBehavior == MultiSelection) {
411 if (controlPressed) {
412 // Toggle the selection state of the current item.
413 m_selectionManager->endAnchoredSelection();
414 m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
415 m_selectionManager->beginAnchoredSelection(index);
416 break;
417 } else {
418 // Select the current item if it is not selected yet.
419 const int current = m_selectionManager->currentItem();
420 if (!m_selectionManager->isSelected(current)) {
421 m_selectionManager->setSelected(current);
422 break;
423 }
424 }
425 }
426 Q_FALLTHROUGH(); // fall through to the default case and add the Space to the current search string.
427 default:
428 m_keyboardManager->addKeys(event->text());
429 // Make sure unconsumed events get propagated up the chain. #302329
430 event->ignore();
431 return false;
432 }
433
434 if (m_selectionManager->currentItem() != index) {
435 switch (m_selectionBehavior) {
436 case NoSelection:
437 m_selectionManager->setCurrentItem(index);
438 break;
439
440 case SingleSelection:
441 m_selectionManager->setCurrentItem(index);
442 m_selectionManager->clearSelection();
443 m_selectionManager->setSelected(index, 1);
444 break;
445
446 case MultiSelection:
447 if (controlPressed) {
448 m_selectionManager->endAnchoredSelection();
449 }
450
451 m_selectionManager->setCurrentItem(index);
452
453 if (!shiftOrControlPressed) {
454 m_selectionManager->clearSelection();
455 m_selectionManager->setSelected(index, 1);
456 }
457
458 if (!shiftPressed) {
459 m_selectionManager->beginAnchoredSelection(index);
460 }
461 break;
462 }
463 }
464
465 if (navigationPressed) {
466 m_view->scrollToItem(index);
467 }
468 return true;
469 }
470
471 void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
472 {
473 if (!m_model || m_model->count() == 0) {
474 return;
475 }
476 int index;
477 if (searchFromNextItem) {
478 const int currentIndex = m_selectionManager->currentItem();
479 index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
480 } else {
481 index = m_model->indexForKeyboardSearch(text, 0);
482 }
483 if (index >= 0) {
484 m_selectionManager->setCurrentItem(index);
485
486 if (m_selectionBehavior != NoSelection) {
487 m_selectionManager->replaceSelection(index);
488 m_selectionManager->beginAnchoredSelection(index);
489 }
490
491 m_view->scrollToItem(index);
492 }
493 }
494
495 void KItemListController::slotAutoActivationTimeout()
496 {
497 if (!m_model || !m_view) {
498 return;
499 }
500
501 const int index = m_autoActivationTimer->property("index").toInt();
502 if (index < 0 || index >= m_model->count()) {
503 return;
504 }
505
506 /* m_view->isUnderMouse() fixes a bug in the Folder-View-Panel and in the
507 * Places-Panel.
508 *
509 * Bug: When you drag a file onto a Folder-View-Item or a Places-Item and
510 * then move away before the auto-activation timeout triggers, than the
511 * item still becomes activated/expanded.
512 *
513 * See Bug 293200 and 305783
514 */
515 if (m_model->supportsDropping(index) && m_view->isUnderMouse()) {
516 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
517 const bool expanded = m_model->isExpanded(index);
518 m_model->setExpanded(index, !expanded);
519 } else if (m_autoActivationBehavior != ExpansionOnly) {
520 emit itemActivated(index);
521 }
522 }
523 }
524
525 bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
526 {
527 Q_UNUSED(event)
528 return false;
529 }
530
531 bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
532 {
533 if (!m_view) {
534 return false;
535 }
536
537 m_pressedMousePos = transform.map(event->pos());
538 m_pressedIndex = m_view->itemAt(m_pressedMousePos);
539 emit mouseButtonPressed(m_pressedIndex, event->buttons());
540
541 if (event->buttons() & (Qt::BackButton | Qt::ForwardButton)) {
542 // Do not select items when clicking the back/forward buttons, see
543 // https://bugs.kde.org/show_bug.cgi?id=327412.
544 return true;
545 }
546
547 if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) {
548 m_selectionManager->endAnchoredSelection();
549 m_selectionManager->setCurrentItem(m_pressedIndex);
550 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
551 return true;
552 }
553
554 m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
555 if (m_selectionTogglePressed) {
556 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
557 // The previous anchored selection has been finished already in
558 // KItemListSelectionManager::setSelected(). We can safely change
559 // the current item and start a new anchored selection now.
560 m_selectionManager->setCurrentItem(m_pressedIndex);
561 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
562 return true;
563 }
564
565 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
566 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
567
568 // The previous selection is cleared if either
569 // 1. The selection mode is SingleSelection, or
570 // 2. the selection mode is MultiSelection, and *none* of the following conditions are met:
571 // a) Shift or Control are pressed.
572 // b) The clicked item is selected already. In that case, the user might want to:
573 // - start dragging multiple items, or
574 // - open the context menu and perform an action for all selected items.
575 const bool shiftOrControlPressed = shiftPressed || controlPressed;
576 const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex);
577 const bool clearSelection = m_selectionBehavior == SingleSelection ||
578 (!shiftOrControlPressed && !pressedItemAlreadySelected);
579 if (clearSelection) {
580 m_selectionManager->clearSelection();
581 } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (event->buttons() & Qt::LeftButton)) {
582 // The user might want to start dragging multiple items, but if he clicks the item
583 // in order to trigger it instead, the other selected items must be deselected.
584 // However, we do not know yet what the user is going to do.
585 // -> remember that the user pressed an item which had been selected already and
586 // clear the selection in mouseReleaseEvent(), unless the items are dragged.
587 m_clearSelectionIfItemsAreNotDragged = true;
588
589 if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex, m_pressedMousePos)) {
590 emit selectedItemTextPressed(m_pressedIndex);
591 }
592 }
593
594 if (!shiftPressed) {
595 // Finish the anchored selection before the current index is changed
596 m_selectionManager->endAnchoredSelection();
597 }
598
599 if (event->buttons() & Qt::RightButton) {
600 // Stop rubber band from persisting after right-clicks
601 KItemListRubberBand* rubberBand = m_view->rubberBand();
602 if (rubberBand->isActive()) {
603 disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
604 rubberBand->setActive(false);
605 m_view->setAutoScroll(false);
606 }
607 }
608
609 if (m_pressedIndex >= 0) {
610 m_selectionManager->setCurrentItem(m_pressedIndex);
611
612 switch (m_selectionBehavior) {
613 case NoSelection:
614 break;
615
616 case SingleSelection:
617 m_selectionManager->setSelected(m_pressedIndex);
618 break;
619
620 case MultiSelection:
621 if (controlPressed && !shiftPressed) {
622 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
623 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
624 } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
625 // Select the pressed item and start a new anchored selection
626 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
627 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
628 }
629 break;
630
631 default:
632 Q_ASSERT(false);
633 break;
634 }
635
636 if (event->buttons() & Qt::RightButton) {
637 emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
638 }
639
640 return true;
641 }
642
643 if (event->buttons() & Qt::RightButton) {
644 const QRectF headerBounds = m_view->headerBoundaries();
645 if (headerBounds.contains(event->pos())) {
646 emit headerContextMenuRequested(event->screenPos());
647 } else {
648 emit viewContextMenuRequested(event->screenPos());
649 }
650 return true;
651 }
652
653 if (m_selectionBehavior == MultiSelection) {
654 QPointF startPos = m_pressedMousePos;
655 if (m_view->scrollOrientation() == Qt::Vertical) {
656 startPos.ry() += m_view->scrollOffset();
657 if (m_view->itemSize().width() < 0) {
658 // Use a special rubberband for views that have only one column and
659 // expand the rubberband to use the whole width of the view.
660 startPos.setX(0);
661 }
662 } else {
663 startPos.rx() += m_view->scrollOffset();
664 }
665
666 m_oldSelection = m_selectionManager->selectedItems();
667 KItemListRubberBand* rubberBand = m_view->rubberBand();
668 rubberBand->setStartPosition(startPos);
669 rubberBand->setEndPosition(startPos);
670 rubberBand->setActive(true);
671 connect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
672 m_view->setAutoScroll(true);
673 }
674
675 return false;
676 }
677
678 bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
679 {
680 if (!m_view) {
681 return false;
682 }
683
684 if (m_pressedIndex >= 0) {
685 // Check whether a dragging should be started
686 if (event->buttons() & Qt::LeftButton) {
687 const QPointF pos = transform.map(event->pos());
688 if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
689 if (!m_selectionManager->isSelected(m_pressedIndex)) {
690 // Always assure that the dragged item gets selected. Usually this is already
691 // done on the mouse-press event, but when using the selection-toggle on a
692 // selected item the dragged item is not selected yet.
693 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
694 } else {
695 // A selected item has been clicked to drag all selected items
696 // -> the selection should not be cleared when the mouse button is released.
697 m_clearSelectionIfItemsAreNotDragged = false;
698 }
699
700 startDragging();
701 }
702 }
703 } else {
704 KItemListRubberBand* rubberBand = m_view->rubberBand();
705 if (rubberBand->isActive()) {
706 QPointF endPos = transform.map(event->pos());
707
708 // Update the current item.
709 const int newCurrent = m_view->itemAt(endPos);
710 if (newCurrent >= 0) {
711 // It's expected that the new current index is also the new anchor (bug 163451).
712 m_selectionManager->endAnchoredSelection();
713 m_selectionManager->setCurrentItem(newCurrent);
714 m_selectionManager->beginAnchoredSelection(newCurrent);
715 }
716
717 if (m_view->scrollOrientation() == Qt::Vertical) {
718 endPos.ry() += m_view->scrollOffset();
719 if (m_view->itemSize().width() < 0) {
720 // Use a special rubberband for views that have only one column and
721 // expand the rubberband to use the whole width of the view.
722 endPos.setX(m_view->size().width());
723 }
724 } else {
725 endPos.rx() += m_view->scrollOffset();
726 }
727 rubberBand->setEndPosition(endPos);
728 }
729 }
730
731 return false;
732 }
733
734 bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
735 {
736 if (!m_view) {
737 return false;
738 }
739
740 emit mouseButtonReleased(m_pressedIndex, event->buttons());
741
742 const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
743 if (isAboveSelectionToggle) {
744 m_selectionTogglePressed = false;
745 return true;
746 }
747
748 if (!isAboveSelectionToggle && m_selectionTogglePressed) {
749 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
750 m_selectionTogglePressed = false;
751 return true;
752 }
753
754 const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
755 event->modifiers() & Qt::ControlModifier;
756
757 KItemListRubberBand* rubberBand = m_view->rubberBand();
758 if (rubberBand->isActive()) {
759 disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
760 rubberBand->setActive(false);
761 m_oldSelection.clear();
762 m_view->setAutoScroll(false);
763 }
764
765 const QPointF pos = transform.map(event->pos());
766 const int index = m_view->itemAt(pos);
767
768 if (index >= 0 && index == m_pressedIndex) {
769 // The release event is done above the same item as the press event
770
771 if (m_clearSelectionIfItemsAreNotDragged) {
772 // A selected item has been clicked, but no drag operation has been started
773 // -> clear the rest of the selection.
774 m_selectionManager->clearSelection();
775 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
776 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
777 }
778
779 if (event->button() & Qt::LeftButton) {
780 bool emitItemActivated = true;
781 if (m_view->isAboveExpansionToggle(index, pos)) {
782 const bool expanded = m_model->isExpanded(index);
783 m_model->setExpanded(index, !expanded);
784
785 emit itemExpansionToggleClicked(index);
786 emitItemActivated = false;
787 } else if (shiftOrControlPressed) {
788 // The mouse click should only update the selection, not trigger the item
789 emitItemActivated = false;
790 } else if (!(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)) {
791 emitItemActivated = false;
792 }
793 if (emitItemActivated) {
794 emit itemActivated(index);
795 }
796 } else if (event->button() & Qt::MidButton) {
797 emit itemMiddleClicked(index);
798 }
799 }
800
801 m_pressedMousePos = QPointF();
802 m_pressedIndex = -1;
803 m_clearSelectionIfItemsAreNotDragged = false;
804 return false;
805 }
806
807 bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
808 {
809 const QPointF pos = transform.map(event->pos());
810 const int index = m_view->itemAt(pos);
811
812 // Expand item if desired - See Bug 295573
813 if (m_mouseDoubleClickAction != ActivateItemOnly) {
814 if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
815 const bool expanded = m_model->isExpanded(index);
816 m_model->setExpanded(index, !expanded);
817 }
818 }
819
820 if (event->button() & Qt::RightButton) {
821 m_selectionManager->clearSelection();
822 if (index >= 0) {
823 m_selectionManager->setSelected(index);
824 emit itemContextMenuRequested(index, event->screenPos());
825 } else {
826 const QRectF headerBounds = m_view->headerBoundaries();
827 if (headerBounds.contains(event->pos())) {
828 emit headerContextMenuRequested(event->screenPos());
829 } else {
830 emit viewContextMenuRequested(event->screenPos());
831 }
832 }
833 return true;
834 }
835
836 bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) &&
837 (event->button() & Qt::LeftButton) &&
838 index >= 0 && index < m_model->count();
839 if (emitItemActivated) {
840 emit itemActivated(index);
841 }
842 return false;
843 }
844
845 bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
846 {
847 Q_UNUSED(event)
848 Q_UNUSED(transform)
849
850 DragAndDropHelper::clearUrlListMatchesUrlCache();
851
852 return false;
853 }
854
855 bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
856 {
857 Q_UNUSED(event)
858 Q_UNUSED(transform)
859
860 m_autoActivationTimer->stop();
861 m_view->setAutoScroll(false);
862 m_view->hideDropIndicator();
863
864 KItemListWidget* widget = hoveredWidget();
865 if (widget) {
866 widget->setHovered(false);
867 emit itemUnhovered(widget->index());
868 }
869 return false;
870 }
871
872 bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
873 {
874 if (!m_model || !m_view) {
875 return false;
876 }
877
878
879 QUrl hoveredDir = m_model->directory();
880 KItemListWidget* oldHoveredWidget = hoveredWidget();
881
882 const QPointF pos = transform.map(event->pos());
883 KItemListWidget* newHoveredWidget = widgetForPos(pos);
884
885 if (oldHoveredWidget != newHoveredWidget) {
886 m_autoActivationTimer->stop();
887
888 if (oldHoveredWidget) {
889 oldHoveredWidget->setHovered(false);
890 emit itemUnhovered(oldHoveredWidget->index());
891 }
892 }
893
894 if (newHoveredWidget) {
895 bool droppingBetweenItems = false;
896 if (m_model->sortRole().isEmpty()) {
897 // The model supports inserting items between other items.
898 droppingBetweenItems = (m_view->showDropIndicator(pos) >= 0);
899 }
900
901 const int index = newHoveredWidget->index();
902
903 if (m_model->isDir(index)) {
904 hoveredDir = m_model->url(index);
905 }
906
907 if (!droppingBetweenItems) {
908 if (m_model->supportsDropping(index)) {
909 // Something has been dragged on an item.
910 m_view->hideDropIndicator();
911 if (!newHoveredWidget->isHovered()) {
912 newHoveredWidget->setHovered(true);
913 emit itemHovered(index);
914 }
915
916 if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) {
917 m_autoActivationTimer->setProperty("index", index);
918 m_autoActivationTimer->start();
919 }
920 }
921 } else {
922 m_autoActivationTimer->stop();
923 if (newHoveredWidget && newHoveredWidget->isHovered()) {
924 newHoveredWidget->setHovered(false);
925 emit itemUnhovered(index);
926 }
927 }
928 } else {
929 m_view->hideDropIndicator();
930 }
931
932 if (DragAndDropHelper::urlListMatchesUrl(event->mimeData()->urls(), hoveredDir)) {
933 event->setDropAction(Qt::IgnoreAction);
934 event->ignore();
935 } else {
936 event->setDropAction(event->proposedAction());
937 event->accept();
938 }
939 return false;
940 }
941
942 bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
943 {
944 if (!m_view) {
945 return false;
946 }
947
948 m_autoActivationTimer->stop();
949 m_view->setAutoScroll(false);
950
951 const QPointF pos = transform.map(event->pos());
952
953 int dropAboveIndex = -1;
954 if (m_model->sortRole().isEmpty()) {
955 // The model supports inserting of items between other items.
956 dropAboveIndex = m_view->showDropIndicator(pos);
957 }
958
959 if (dropAboveIndex >= 0) {
960 // Something has been dropped between two items.
961 m_view->hideDropIndicator();
962 emit aboveItemDropEvent(dropAboveIndex, event);
963 } else if (!event->mimeData()->hasFormat(m_model->blacklistItemDropEventMimeType())) {
964 // Something has been dropped on an item or on an empty part of the view.
965 emit itemDropEvent(m_view->itemAt(pos), event);
966 }
967
968 QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropEnd);
969 QAccessible::updateAccessibility(&accessibilityEvent);
970
971 return true;
972 }
973
974 bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
975 {
976 Q_UNUSED(event)
977 Q_UNUSED(transform)
978 return false;
979 }
980
981 bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
982 {
983 Q_UNUSED(transform)
984 if (!m_model || !m_view) {
985 return false;
986 }
987
988 KItemListWidget* oldHoveredWidget = hoveredWidget();
989 const QPointF pos = transform.map(event->pos());
990 KItemListWidget* newHoveredWidget = widgetForPos(pos);
991
992 if (oldHoveredWidget != newHoveredWidget) {
993 if (oldHoveredWidget) {
994 oldHoveredWidget->setHovered(false);
995 emit itemUnhovered(oldHoveredWidget->index());
996 }
997
998 if (newHoveredWidget) {
999 newHoveredWidget->setHovered(true);
1000 const QPointF mappedPos = newHoveredWidget->mapFromItem(m_view, pos);
1001 newHoveredWidget->setHoverPosition(mappedPos);
1002 emit itemHovered(newHoveredWidget->index());
1003 }
1004 } else if (oldHoveredWidget) {
1005 const QPointF mappedPos = oldHoveredWidget->mapFromItem(m_view, pos);
1006 oldHoveredWidget->setHoverPosition(mappedPos);
1007 }
1008
1009 return false;
1010 }
1011
1012 bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
1013 {
1014 Q_UNUSED(event)
1015 Q_UNUSED(transform)
1016
1017 if (!m_model || !m_view) {
1018 return false;
1019 }
1020
1021 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1022 if (widget->isHovered()) {
1023 widget->setHovered(false);
1024 emit itemUnhovered(widget->index());
1025 }
1026 }
1027 return false;
1028 }
1029
1030 bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
1031 {
1032 Q_UNUSED(event)
1033 Q_UNUSED(transform)
1034 return false;
1035 }
1036
1037 bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
1038 {
1039 Q_UNUSED(event)
1040 Q_UNUSED(transform)
1041 return false;
1042 }
1043
1044 bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
1045 {
1046 if (!event) {
1047 return false;
1048 }
1049
1050 switch (event->type()) {
1051 case QEvent::KeyPress:
1052 return keyPressEvent(static_cast<QKeyEvent*>(event));
1053 case QEvent::InputMethod:
1054 return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
1055 case QEvent::GraphicsSceneMousePress:
1056 return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1057 case QEvent::GraphicsSceneMouseMove:
1058 return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1059 case QEvent::GraphicsSceneMouseRelease:
1060 return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1061 case QEvent::GraphicsSceneMouseDoubleClick:
1062 return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1063 case QEvent::GraphicsSceneWheel:
1064 return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
1065 case QEvent::GraphicsSceneDragEnter:
1066 return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1067 case QEvent::GraphicsSceneDragLeave:
1068 return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1069 case QEvent::GraphicsSceneDragMove:
1070 return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1071 case QEvent::GraphicsSceneDrop:
1072 return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1073 case QEvent::GraphicsSceneHoverEnter:
1074 return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1075 case QEvent::GraphicsSceneHoverMove:
1076 return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1077 case QEvent::GraphicsSceneHoverLeave:
1078 return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1079 case QEvent::GraphicsSceneResize:
1080 return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
1081 default:
1082 break;
1083 }
1084
1085 return false;
1086 }
1087
1088 void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
1089 {
1090 if (!m_view) {
1091 return;
1092 }
1093
1094 KItemListRubberBand* rubberBand = m_view->rubberBand();
1095 if (rubberBand->isActive()) {
1096 const qreal diff = current - previous;
1097 // TODO: Ideally just QCursor::pos() should be used as
1098 // new end-position but it seems there is no easy way
1099 // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
1100 // (... or I just missed an easy way to do the mapping)
1101 QPointF endPos = rubberBand->endPosition();
1102 if (m_view->scrollOrientation() == Qt::Vertical) {
1103 endPos.ry() += diff;
1104 } else {
1105 endPos.rx() += diff;
1106 }
1107
1108 rubberBand->setEndPosition(endPos);
1109 }
1110 }
1111
1112 void KItemListController::slotRubberBandChanged()
1113 {
1114 if (!m_view || !m_model || m_model->count() <= 0) {
1115 return;
1116 }
1117
1118 const KItemListRubberBand* rubberBand = m_view->rubberBand();
1119 const QPointF startPos = rubberBand->startPosition();
1120 const QPointF endPos = rubberBand->endPosition();
1121 QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
1122
1123 const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
1124 if (scrollVertical) {
1125 rubberBandRect.translate(0, -m_view->scrollOffset());
1126 } else {
1127 rubberBandRect.translate(-m_view->scrollOffset(), 0);
1128 }
1129
1130 if (!m_oldSelection.isEmpty()) {
1131 // Clear the old selection that was available before the rubberband has
1132 // been activated in case if no Shift- or Control-key are pressed
1133 const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
1134 QApplication::keyboardModifiers() & Qt::ControlModifier;
1135 if (!shiftOrControlPressed) {
1136 m_oldSelection.clear();
1137 }
1138 }
1139
1140 KItemSet selectedItems;
1141
1142 // Select all visible items that intersect with the rubberband
1143 foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1144 const int index = widget->index();
1145
1146 const QRectF widgetRect = m_view->itemRect(index);
1147 if (widgetRect.intersects(rubberBandRect)) {
1148 const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
1149 const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
1150 if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
1151 selectedItems.insert(index);
1152 }
1153 }
1154 }
1155
1156 // Select all invisible items that intersect with the rubberband. Instead of
1157 // iterating all items only the area which might be touched by the rubberband
1158 // will be checked.
1159 const bool increaseIndex = scrollVertical ?
1160 startPos.y() > endPos.y(): startPos.x() > endPos.x();
1161
1162 int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
1163 bool selectionFinished = false;
1164 do {
1165 const QRectF widgetRect = m_view->itemRect(index);
1166 if (widgetRect.intersects(rubberBandRect)) {
1167 selectedItems.insert(index);
1168 }
1169
1170 if (increaseIndex) {
1171 ++index;
1172 selectionFinished = (index >= m_model->count()) ||
1173 ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
1174 (!scrollVertical && widgetRect.left() > rubberBandRect.right());
1175 } else {
1176 --index;
1177 selectionFinished = (index < 0) ||
1178 ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
1179 (!scrollVertical && widgetRect.right() < rubberBandRect.left());
1180 }
1181 } while (!selectionFinished);
1182
1183 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
1184 // If Control is pressed, the selection state of all items in the rubberband is toggled.
1185 // Therefore, the new selection contains:
1186 // 1. All previously selected items which are not inside the rubberband, and
1187 // 2. all items inside the rubberband which have not been selected previously.
1188 m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems);
1189 }
1190 else {
1191 m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
1192 }
1193 }
1194
1195 void KItemListController::startDragging()
1196 {
1197 if (!m_view || !m_model) {
1198 return;
1199 }
1200
1201 const KItemSet selectedItems = m_selectionManager->selectedItems();
1202 if (selectedItems.isEmpty()) {
1203 return;
1204 }
1205
1206 QMimeData* data = m_model->createMimeData(selectedItems);
1207 if (!data) {
1208 return;
1209 }
1210
1211 // The created drag object will be owned and deleted
1212 // by QApplication::activeWindow().
1213 QDrag* drag = new QDrag(QApplication::activeWindow());
1214 drag->setMimeData(data);
1215
1216 const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
1217 drag->setPixmap(pixmap);
1218
1219 const QPoint hotSpot((pixmap.width() / pixmap.devicePixelRatio()) / 2, 0);
1220 drag->setHotSpot(hotSpot);
1221
1222 drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::MoveAction);
1223
1224 QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropStart);
1225 QAccessible::updateAccessibility(&accessibilityEvent);
1226 }
1227
1228 KItemListWidget* KItemListController::hoveredWidget() const
1229 {
1230 Q_ASSERT(m_view);
1231
1232 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1233 if (widget->isHovered()) {
1234 return widget;
1235 }
1236 }
1237
1238 return nullptr;
1239 }
1240
1241 KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
1242 {
1243 Q_ASSERT(m_view);
1244
1245 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1246 const QPointF mappedPos = widget->mapFromItem(m_view, pos);
1247
1248 const bool hovered = widget->contains(mappedPos) &&
1249 !widget->expansionToggleRect().contains(mappedPos);
1250 if (hovered) {
1251 return widget;
1252 }
1253 }
1254
1255 return nullptr;
1256 }
1257
1258 void KItemListController::updateKeyboardAnchor()
1259 {
1260 const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
1261 m_keyboardAnchorIndex < m_model->count() &&
1262 keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
1263 if (!validAnchor) {
1264 const int index = m_selectionManager->currentItem();
1265 m_keyboardAnchorIndex = index;
1266 m_keyboardAnchorPos = keyboardAnchorPos(index);
1267 }
1268 }
1269
1270 int KItemListController::nextRowIndex(int index) const
1271 {
1272 if (m_keyboardAnchorIndex < 0) {
1273 return index;
1274 }
1275
1276 const int maxIndex = m_model->count() - 1;
1277 if (index == maxIndex) {
1278 return index;
1279 }
1280
1281 // Calculate the index of the last column inside the row of the current index
1282 int lastColumnIndex = index;
1283 while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
1284 ++lastColumnIndex;
1285 if (lastColumnIndex >= maxIndex) {
1286 return index;
1287 }
1288 }
1289
1290 // Based on the last column index go to the next row and calculate the nearest index
1291 // that is below the current index
1292 int nextRowIndex = lastColumnIndex + 1;
1293 int searchIndex = nextRowIndex;
1294 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
1295 while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
1296 ++searchIndex;
1297 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1298 if (searchDiff < minDiff) {
1299 minDiff = searchDiff;
1300 nextRowIndex = searchIndex;
1301 }
1302 }
1303
1304 return nextRowIndex;
1305 }
1306
1307 int KItemListController::previousRowIndex(int index) const
1308 {
1309 if (m_keyboardAnchorIndex < 0 || index == 0) {
1310 return index;
1311 }
1312
1313 // Calculate the index of the first column inside the row of the current index
1314 int firstColumnIndex = index;
1315 while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
1316 --firstColumnIndex;
1317 if (firstColumnIndex <= 0) {
1318 return index;
1319 }
1320 }
1321
1322 // Based on the first column index go to the previous row and calculate the nearest index
1323 // that is above the current index
1324 int previousRowIndex = firstColumnIndex - 1;
1325 int searchIndex = previousRowIndex;
1326 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
1327 while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
1328 --searchIndex;
1329 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1330 if (searchDiff < minDiff) {
1331 minDiff = searchDiff;
1332 previousRowIndex = searchIndex;
1333 }
1334 }
1335
1336 return previousRowIndex;
1337 }
1338
1339 qreal KItemListController::keyboardAnchorPos(int index) const
1340 {
1341 const QRectF itemRect = m_view->itemRect(index);
1342 if (!itemRect.isEmpty()) {
1343 return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y();
1344 }
1345
1346 return 0;
1347 }
1348
1349 void KItemListController::updateExtendedSelectionRegion()
1350 {
1351 if (m_view) {
1352 const bool extend = (m_selectionBehavior != MultiSelection);
1353 KItemListStyleOption option = m_view->styleOption();
1354 if (option.extendedSelectionRegion != extend) {
1355 option.extendedSelectionRegion = extend;
1356 m_view->setStyleOption(option);
1357 }
1358 }
1359 }
1360