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