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