]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistcontroller.cpp
If an item is clicked to trigger it, clear the rest of the selection
[dolphin.git] / src / kitemviews / kitemlistcontroller.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * Based on the Itemviews NG project from Trolltech Labs: *
5 * http://qt.gitorious.org/qt-labs/itemviews-ng *
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 "kitemlistview.h"
26 #include "kitemlistrubberband_p.h"
27 #include "kitemlistselectionmanager.h"
28 #include "kitemlistkeyboardsearchmanager_p.h"
29
30 #include <QApplication>
31 #include <QDrag>
32 #include <QEvent>
33 #include <QGraphicsScene>
34 #include <QGraphicsSceneEvent>
35 #include <QGraphicsView>
36 #include <QMimeData>
37 #include <QTimer>
38
39 #include <KGlobalSettings>
40 #include <KDebug>
41
42 KItemListController::KItemListController(QObject* parent) :
43 QObject(parent),
44 m_singleClickActivation(KGlobalSettings::singleClick()),
45 m_selectionTogglePressed(false),
46 m_clearSelectionIfItemsAreNotDragged(false),
47 m_selectionBehavior(NoSelection),
48 m_model(0),
49 m_view(0),
50 m_selectionManager(new KItemListSelectionManager(this)),
51 m_keyboardManager(new KItemListKeyboardSearchManager(this)),
52 m_pressedIndex(-1),
53 m_pressedMousePos(),
54 m_autoActivationTimer(0),
55 m_oldSelection(),
56 m_keyboardAnchorIndex(-1),
57 m_keyboardAnchorPos(0)
58 {
59 connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)),
60 this, SLOT(slotChangeCurrentItem(QString,bool)));
61
62 m_autoActivationTimer = new QTimer(this);
63 m_autoActivationTimer->setSingleShot(true);
64 m_autoActivationTimer->setInterval(-1);
65 connect(m_autoActivationTimer, SIGNAL(timeout()), this, SLOT(slotAutoActivationTimeout()));
66 }
67
68 KItemListController::~KItemListController()
69 {
70 }
71
72 void KItemListController::setModel(KItemModelBase* model)
73 {
74 if (m_model == model) {
75 return;
76 }
77
78 KItemModelBase* oldModel = m_model;
79 m_model = model;
80
81 if (m_view) {
82 m_view->setModel(m_model);
83 }
84
85 m_selectionManager->setModel(m_model);
86
87 emit modelChanged(m_model, oldModel);
88 }
89
90 KItemModelBase* KItemListController::model() const
91 {
92 return m_model;
93 }
94
95 KItemListSelectionManager* KItemListController::selectionManager() const
96 {
97 return m_selectionManager;
98 }
99
100 void KItemListController::setView(KItemListView* view)
101 {
102 if (m_view == view) {
103 return;
104 }
105
106 KItemListView* oldView = m_view;
107 if (oldView) {
108 disconnect(oldView, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
109 }
110
111 m_view = view;
112
113 if (m_view) {
114 m_view->setController(this);
115 m_view->setModel(m_model);
116 connect(m_view, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
117 }
118
119 emit viewChanged(m_view, oldView);
120 }
121
122 KItemListView* KItemListController::view() const
123 {
124 return m_view;
125 }
126
127 void KItemListController::setSelectionBehavior(SelectionBehavior behavior)
128 {
129 m_selectionBehavior = behavior;
130 }
131
132 KItemListController::SelectionBehavior KItemListController::selectionBehavior() const
133 {
134 return m_selectionBehavior;
135 }
136
137 void KItemListController::setAutoActivationDelay(int delay)
138 {
139 m_autoActivationTimer->setInterval(delay);
140 }
141
142 int KItemListController::autoActivationDelay() const
143 {
144 return m_autoActivationTimer->interval();
145 }
146
147 void KItemListController::setSingleClickActivation(bool singleClick)
148 {
149 m_singleClickActivation = singleClick;
150 }
151
152 bool KItemListController::singleClickActivation() const
153 {
154 return m_singleClickActivation;
155 }
156
157 bool KItemListController::showEvent(QShowEvent* event)
158 {
159 Q_UNUSED(event);
160 return false;
161 }
162
163 bool KItemListController::hideEvent(QHideEvent* event)
164 {
165 Q_UNUSED(event);
166 return false;
167 }
168
169 bool KItemListController::keyPressEvent(QKeyEvent* event)
170 {
171 int index = m_selectionManager->currentItem();
172 int key = event->key();
173
174 // Handle the expanding/collapsing of items
175 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
176 if (key == Qt::Key_Right) {
177 if (m_model->setExpanded(index, true)) {
178 return true;
179 }
180 } else if (key == Qt::Key_Left) {
181 if (m_model->setExpanded(index, false)) {
182 return true;
183 }
184 }
185 }
186
187 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
188 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
189 const bool shiftOrControlPressed = shiftPressed || controlPressed;
190
191 const int itemCount = m_model->count();
192
193 // For horizontal scroll orientation, transform
194 // the arrow keys to simplify the event handling.
195 if (m_view->scrollOrientation() == Qt::Horizontal) {
196 switch (key) {
197 case Qt::Key_Up: key = Qt::Key_Left; break;
198 case Qt::Key_Down: key = Qt::Key_Right; break;
199 case Qt::Key_Left: key = Qt::Key_Up; break;
200 case Qt::Key_Right: key = Qt::Key_Down; break;
201 default: break;
202 }
203 }
204
205 const bool selectSingleItem = itemCount == 1 &&
206 (key == Qt::Key_Home || key == Qt::Key_End ||
207 key == Qt::Key_Up || key == Qt::Key_Down ||
208 key == Qt::Key_Left || key == Qt::Key_Right);
209 if (selectSingleItem) {
210 const int current = m_selectionManager->currentItem();
211 m_selectionManager->setSelected(current);
212 return true;
213 }
214
215 switch (key) {
216 case Qt::Key_Home:
217 index = 0;
218 break;
219
220 case Qt::Key_End:
221 index = itemCount - 1;
222 break;
223
224 case Qt::Key_Left:
225 if (index > 0) {
226 --index;
227 m_keyboardAnchorIndex = index;
228 m_keyboardAnchorPos = keyboardAnchorPos(index);
229 }
230 break;
231
232 case Qt::Key_Right:
233 if (index < itemCount - 1) {
234 ++index;
235 m_keyboardAnchorIndex = index;
236 m_keyboardAnchorPos = keyboardAnchorPos(index);
237 }
238 break;
239
240 case Qt::Key_Up:
241 updateKeyboardAnchor();
242 index = previousRowIndex();
243 break;
244
245 case Qt::Key_Down:
246 updateKeyboardAnchor();
247 index = nextRowIndex();
248 break;
249
250 case Qt::Key_Enter:
251 case Qt::Key_Return: {
252 const QSet<int> selectedItems = m_selectionManager->selectedItems();
253 if (selectedItems.count() >= 2) {
254 emit itemsActivated(selectedItems);
255 } else if (selectedItems.count() == 1) {
256 emit itemActivated(selectedItems.toList().first());
257 } else {
258 emit itemActivated(index);
259 }
260 break;
261 }
262
263 case Qt::Key_Space:
264 if (controlPressed) {
265 m_selectionManager->endAnchoredSelection();
266 m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
267 m_selectionManager->beginAnchoredSelection(index);
268 } else {
269 const int current = m_selectionManager->currentItem();
270 m_selectionManager->setSelected(current);
271 }
272 break;
273
274 case Qt::Key_Menu: {
275 // Emit the signal itemContextMenuRequested() in case if at least one
276 // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
277 const QSet<int> selectedItems = m_selectionManager->selectedItems();
278 int index = -1;
279 if (selectedItems.count() >= 2) {
280 const int currentItemIndex = m_selectionManager->currentItem();
281 index = selectedItems.contains(currentItemIndex)
282 ? currentItemIndex : selectedItems.toList().first();
283 } else if (selectedItems.count() == 1) {
284 index = selectedItems.toList().first();
285 }
286
287 if (index >= 0) {
288 const QRectF contextRect = m_view->itemContextRect(index);
289 const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
290 emit itemContextMenuRequested(index, pos);
291 } else {
292 emit viewContextMenuRequested(QCursor::pos());
293 }
294 break;
295 }
296
297 default:
298 m_keyboardManager->addKeys(event->text());
299 return false;
300 }
301
302 if (m_selectionManager->currentItem() != index) {
303 if (controlPressed) {
304 m_selectionManager->endAnchoredSelection();
305 }
306
307 m_selectionManager->setCurrentItem(index);
308
309 if (!shiftOrControlPressed || m_selectionBehavior == SingleSelection) {
310 m_selectionManager->clearSelection();
311 m_selectionManager->setSelected(index, 1);
312 }
313
314 if (!shiftPressed) {
315 m_selectionManager->beginAnchoredSelection(index);
316 }
317
318 m_view->scrollToItem(index);
319 }
320 return true;
321 }
322
323 void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
324 {
325 if (!m_model || m_model->count() == 0) {
326 return;
327 }
328 const int currentIndex = m_selectionManager->currentItem();
329 int index;
330 if (searchFromNextItem) {
331 index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
332 } else {
333 index = m_model->indexForKeyboardSearch(text, currentIndex);
334 }
335 if (index >= 0) {
336 m_selectionManager->setCurrentItem(index);
337 m_selectionManager->clearSelection();
338 m_selectionManager->setSelected(index, 1);
339 m_selectionManager->beginAnchoredSelection(index);
340 m_view->scrollToItem(index);
341 }
342 }
343
344 void KItemListController::slotAutoActivationTimeout()
345 {
346 if (!m_model || !m_view) {
347 return;
348 }
349
350 const int index = m_autoActivationTimer->property("index").toInt();
351 if (index < 0 || index >= m_model->count()) {
352 return;
353 }
354
355 if (m_model->supportsDropping(index)) {
356 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
357 const bool expanded = m_model->isExpanded(index);
358 m_model->setExpanded(index, !expanded);
359 } else {
360 emit itemActivated(index);
361 }
362 }
363 }
364
365 bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
366 {
367 Q_UNUSED(event);
368 return false;
369 }
370
371 bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
372 {
373 if (!m_view) {
374 return false;
375 }
376
377 m_pressedMousePos = transform.map(event->pos());
378 m_pressedIndex = m_view->itemAt(m_pressedMousePos);
379 if (m_pressedIndex >= 0) {
380 emit itemPressed(m_pressedIndex, event->button());
381 }
382
383 if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) {
384 m_selectionManager->endAnchoredSelection();
385 m_selectionManager->setCurrentItem(m_pressedIndex);
386 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
387 return true;
388 }
389
390 m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
391 if (m_selectionTogglePressed) {
392 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
393 // The previous anchored selection has been finished already in
394 // KItemListSelectionManager::setSelected(). We can safely change
395 // the current item and start a new anchored selection now.
396 m_selectionManager->setCurrentItem(m_pressedIndex);
397 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
398 return true;
399 }
400
401 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
402 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
403
404 // The previous selection is cleared if either
405 // 1. The selection mode is SingleSelection, or
406 // 2. the selection mode is MultiSelection, and *none* of the following conditions are met:
407 // a) Shift or Control are pressed.
408 // b) The clicked item is selected already. In that case, the user might want to:
409 // - start dragging multiple items, or
410 // - open the context menu and perform an action for all selected items.
411 const bool shiftOrControlPressed = shiftPressed || controlPressed;
412 const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex);
413 const bool clearSelection = m_selectionBehavior == SingleSelection ||
414 (!shiftOrControlPressed && !pressedItemAlreadySelected);
415 if (clearSelection) {
416 m_selectionManager->clearSelection();
417 } else if (pressedItemAlreadySelected && (event->buttons() & Qt::LeftButton)) {
418 // The user might want to start dragging multiple items, but if he clicks the item
419 // in order to trigger it instead, the other selected items must be deselected.
420 // However, we do not know yet what the user is going to do.
421 // -> remember that the user pressed an item which had been selected already and
422 // clear the selection in mouseReleaseEvent(), unless the items are dragged.
423 m_clearSelectionIfItemsAreNotDragged = true;
424 }
425
426 if (!shiftPressed) {
427 // Finish the anchored selection before the current index is changed
428 m_selectionManager->endAnchoredSelection();
429 }
430
431 if (m_pressedIndex >= 0) {
432 m_selectionManager->setCurrentItem(m_pressedIndex);
433
434 switch (m_selectionBehavior) {
435 case NoSelection:
436 break;
437
438 case SingleSelection:
439 m_selectionManager->setSelected(m_pressedIndex);
440 break;
441
442 case MultiSelection:
443 if (controlPressed) {
444 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
445 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
446 } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
447 // Select the pressed item and start a new anchored selection
448 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
449 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
450 }
451 break;
452
453 default:
454 Q_ASSERT(false);
455 break;
456 }
457
458 if (event->buttons() & Qt::RightButton) {
459 emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
460 }
461
462 return true;
463 }
464
465 if (event->buttons() & Qt::RightButton) {
466 const QRectF headerBounds = m_view->headerBoundaries();
467 if (headerBounds.contains(event->pos())) {
468 emit headerContextMenuRequested(event->screenPos());
469 } else {
470 emit viewContextMenuRequested(event->screenPos());
471 }
472 return true;
473 }
474
475 if (m_selectionBehavior == MultiSelection) {
476 QPointF startPos = m_pressedMousePos;
477 if (m_view->scrollOrientation() == Qt::Vertical) {
478 startPos.ry() += m_view->scrollOffset();
479 if (m_view->itemSize().width() < 0) {
480 // Use a special rubberband for views that have only one column and
481 // expand the rubberband to use the whole width of the view.
482 startPos.setX(0);
483 }
484 } else {
485 startPos.rx() += m_view->scrollOffset();
486 }
487
488 m_oldSelection = m_selectionManager->selectedItems();
489 KItemListRubberBand* rubberBand = m_view->rubberBand();
490 rubberBand->setStartPosition(startPos);
491 rubberBand->setEndPosition(startPos);
492 rubberBand->setActive(true);
493 connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
494 m_view->setAutoScroll(true);
495 }
496
497 return false;
498 }
499
500 bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
501 {
502 if (!m_view) {
503 return false;
504 }
505
506 if (m_pressedIndex >= 0) {
507 // Check whether a dragging should be started
508 if (event->buttons() & Qt::LeftButton) {
509 const QPointF pos = transform.map(event->pos());
510 if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
511 if (!m_selectionManager->isSelected(m_pressedIndex)) {
512 // Always assure that the dragged item gets selected. Usually this is already
513 // done on the mouse-press event, but when using the selection-toggle on a
514 // selected item the dragged item is not selected yet.
515 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
516 } else {
517 // A selected item has been clicked to drag all selected items
518 // -> the selection should not be cleared when the mouse button is released.
519 m_clearSelectionIfItemsAreNotDragged = false;
520 }
521
522 startDragging();
523 }
524 }
525 } else {
526 KItemListRubberBand* rubberBand = m_view->rubberBand();
527 if (rubberBand->isActive()) {
528 QPointF endPos = transform.map(event->pos());
529
530 // Update the current item.
531 const int newCurrent = m_view->itemAt(endPos);
532 if (newCurrent >= 0) {
533 // It's expected that the new current index is also the new anchor (bug 163451).
534 m_selectionManager->endAnchoredSelection();
535 m_selectionManager->setCurrentItem(newCurrent);
536 m_selectionManager->beginAnchoredSelection(newCurrent);
537 }
538
539 if (m_view->scrollOrientation() == Qt::Vertical) {
540 endPos.ry() += m_view->scrollOffset();
541 if (m_view->itemSize().width() < 0) {
542 // Use a special rubberband for views that have only one column and
543 // expand the rubberband to use the whole width of the view.
544 endPos.setX(m_view->size().width());
545 }
546 } else {
547 endPos.rx() += m_view->scrollOffset();
548 }
549 rubberBand->setEndPosition(endPos);
550 }
551 }
552
553 return false;
554 }
555
556 bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
557 {
558 if (!m_view) {
559 return false;
560 }
561
562 if (m_pressedIndex >= 0) {
563 emit itemReleased(m_pressedIndex, event->button());
564 }
565
566 const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
567 if (isAboveSelectionToggle) {
568 m_selectionTogglePressed = false;
569 return true;
570 }
571
572 if (!isAboveSelectionToggle && m_selectionTogglePressed) {
573 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
574 m_selectionTogglePressed = false;
575 return true;
576 }
577
578 const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
579 event->modifiers() & Qt::ControlModifier;
580
581 KItemListRubberBand* rubberBand = m_view->rubberBand();
582 if (rubberBand->isActive()) {
583 disconnect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
584 rubberBand->setActive(false);
585 m_oldSelection.clear();
586 m_view->setAutoScroll(false);
587 }
588
589 const QPointF pos = transform.map(event->pos());
590 const int index = m_view->itemAt(pos);
591
592 if (index >= 0 && index == m_pressedIndex) {
593 // The release event is done above the same item as the press event
594
595 if (m_clearSelectionIfItemsAreNotDragged) {
596 // A selected item has been clicked, but no drag operation has been started
597 // -> clear the rest of the selection.
598 m_selectionManager->clearSelection();
599 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
600 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
601 }
602
603 if (event->button() & Qt::LeftButton) {
604 bool emitItemActivated = true;
605 if (m_view->isAboveExpansionToggle(index, pos)) {
606 const bool expanded = m_model->isExpanded(index);
607 m_model->setExpanded(index, !expanded);
608
609 emit itemExpansionToggleClicked(index);
610 emitItemActivated = false;
611 } else if (shiftOrControlPressed) {
612 // The mouse click should only update the selection, not trigger the item
613 emitItemActivated = false;
614 } else if (!m_singleClickActivation) {
615 emitItemActivated = false;
616 }
617 if (emitItemActivated) {
618 emit itemActivated(index);
619 }
620 } else if (event->button() & Qt::MidButton) {
621 emit itemMiddleClicked(index);
622 }
623 }
624
625 m_pressedMousePos = QPointF();
626 m_pressedIndex = -1;
627 m_clearSelectionIfItemsAreNotDragged = false;
628 return false;
629 }
630
631 bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
632 {
633 const QPointF pos = transform.map(event->pos());
634 const int index = m_view->itemAt(pos);
635
636 bool emitItemActivated = !m_singleClickActivation &&
637 (event->button() & Qt::LeftButton) &&
638 index >= 0 && index < m_model->count();
639 if (emitItemActivated) {
640 emit itemActivated(index);
641 }
642 return false;
643 }
644
645 bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
646 {
647 Q_UNUSED(event);
648 Q_UNUSED(transform);
649 return false;
650 }
651
652 bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
653 {
654 Q_UNUSED(event);
655 Q_UNUSED(transform);
656 return false;
657 }
658
659 bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
660 {
661 Q_UNUSED(transform);
662 if (!m_model || !m_view) {
663 return false;
664 }
665
666 KItemListWidget* oldHoveredWidget = hoveredWidget();
667
668 const QPointF pos = transform.map(event->pos());
669 KItemListWidget* newHoveredWidget = widgetForPos(pos);
670
671 if (oldHoveredWidget != newHoveredWidget) {
672 m_autoActivationTimer->stop();
673
674 if (oldHoveredWidget) {
675 oldHoveredWidget->setHovered(false);
676 emit itemUnhovered(oldHoveredWidget->index());
677 }
678
679 if (newHoveredWidget) {
680 const int index = newHoveredWidget->index();
681 if (m_model->supportsDropping(index)) {
682 newHoveredWidget->setHovered(true);
683 }
684 emit itemHovered(index);
685
686 if (m_autoActivationTimer->interval() >= 0) {
687 m_autoActivationTimer->setProperty("index", index);
688 m_autoActivationTimer->start();
689 }
690 }
691 }
692
693 return false;
694 }
695
696 bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
697 {
698 Q_UNUSED(transform)
699 if (!m_view) {
700 return false;
701 }
702
703 m_autoActivationTimer->stop();
704
705 const QPointF pos = transform.map(event->pos());
706 const int index = m_view->itemAt(pos);
707 emit itemDropEvent(index, event);
708
709 return true;
710 }
711
712 bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
713 {
714 Q_UNUSED(event);
715 Q_UNUSED(transform);
716 return false;
717 }
718
719 bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
720 {
721 Q_UNUSED(transform);
722 if (!m_model || !m_view) {
723 return false;
724 }
725
726 KItemListWidget* oldHoveredWidget = hoveredWidget();
727 const QPointF pos = transform.map(event->pos());
728 KItemListWidget* newHoveredWidget = widgetForPos(pos);
729
730 if (oldHoveredWidget != newHoveredWidget) {
731 if (oldHoveredWidget) {
732 oldHoveredWidget->setHovered(false);
733 emit itemUnhovered(oldHoveredWidget->index());
734 }
735
736 if (newHoveredWidget) {
737 newHoveredWidget->setHovered(true);
738 emit itemHovered(newHoveredWidget->index());
739 }
740 }
741
742 return false;
743 }
744
745 bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
746 {
747 Q_UNUSED(event);
748 Q_UNUSED(transform);
749
750 if (!m_model || !m_view) {
751 return false;
752 }
753
754 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
755 if (widget->isHovered()) {
756 widget->setHovered(false);
757 emit itemUnhovered(widget->index());
758 }
759 }
760 return false;
761 }
762
763 bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
764 {
765 Q_UNUSED(event);
766 Q_UNUSED(transform);
767 return false;
768 }
769
770 bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
771 {
772 Q_UNUSED(event);
773 Q_UNUSED(transform);
774 return false;
775 }
776
777 bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
778 {
779 if (!event) {
780 return false;
781 }
782
783 switch (event->type()) {
784 case QEvent::KeyPress:
785 return keyPressEvent(static_cast<QKeyEvent*>(event));
786 case QEvent::InputMethod:
787 return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
788 case QEvent::GraphicsSceneMousePress:
789 return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
790 case QEvent::GraphicsSceneMouseMove:
791 return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
792 case QEvent::GraphicsSceneMouseRelease:
793 return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
794 case QEvent::GraphicsSceneMouseDoubleClick:
795 return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
796 case QEvent::GraphicsSceneWheel:
797 return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
798 case QEvent::GraphicsSceneDragEnter:
799 return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
800 case QEvent::GraphicsSceneDragLeave:
801 return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
802 case QEvent::GraphicsSceneDragMove:
803 return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
804 case QEvent::GraphicsSceneDrop:
805 return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
806 case QEvent::GraphicsSceneHoverEnter:
807 return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
808 case QEvent::GraphicsSceneHoverMove:
809 return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
810 case QEvent::GraphicsSceneHoverLeave:
811 return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
812 case QEvent::GraphicsSceneResize:
813 return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
814 default:
815 break;
816 }
817
818 return false;
819 }
820
821 void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
822 {
823 if (!m_view) {
824 return;
825 }
826
827 KItemListRubberBand* rubberBand = m_view->rubberBand();
828 if (rubberBand->isActive()) {
829 const qreal diff = current - previous;
830 // TODO: Ideally just QCursor::pos() should be used as
831 // new end-position but it seems there is no easy way
832 // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
833 // (... or I just missed an easy way to do the mapping)
834 QPointF endPos = rubberBand->endPosition();
835 if (m_view->scrollOrientation() == Qt::Vertical) {
836 endPos.ry() += diff;
837 } else {
838 endPos.rx() += diff;
839 }
840
841 rubberBand->setEndPosition(endPos);
842 }
843 }
844
845 void KItemListController::slotRubberBandChanged()
846 {
847 if (!m_view || !m_model || m_model->count() <= 0) {
848 return;
849 }
850
851 const KItemListRubberBand* rubberBand = m_view->rubberBand();
852 const QPointF startPos = rubberBand->startPosition();
853 const QPointF endPos = rubberBand->endPosition();
854 QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
855
856 const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
857 if (scrollVertical) {
858 rubberBandRect.translate(0, -m_view->scrollOffset());
859 } else {
860 rubberBandRect.translate(-m_view->scrollOffset(), 0);
861 }
862
863 if (!m_oldSelection.isEmpty()) {
864 // Clear the old selection that was available before the rubberband has
865 // been activated in case if no Shift- or Control-key are pressed
866 const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
867 QApplication::keyboardModifiers() & Qt::ControlModifier;
868 if (!shiftOrControlPressed) {
869 m_oldSelection.clear();
870 }
871 }
872
873 QSet<int> selectedItems;
874
875 // Select all visible items that intersect with the rubberband
876 foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
877 const int index = widget->index();
878
879 const QRectF widgetRect = m_view->itemRect(index);
880 if (widgetRect.intersects(rubberBandRect)) {
881 const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
882 const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
883 if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
884 selectedItems.insert(index);
885 }
886 }
887 }
888
889 // Select all invisible items that intersect with the rubberband. Instead of
890 // iterating all items only the area which might be touched by the rubberband
891 // will be checked.
892 const bool increaseIndex = scrollVertical ?
893 startPos.y() > endPos.y(): startPos.x() > endPos.x();
894
895 int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
896 bool selectionFinished = false;
897 do {
898 const QRectF widgetRect = m_view->itemRect(index);
899 if (widgetRect.intersects(rubberBandRect)) {
900 selectedItems.insert(index);
901 }
902
903 if (increaseIndex) {
904 ++index;
905 selectionFinished = (index >= m_model->count()) ||
906 ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
907 (!scrollVertical && widgetRect.left() > rubberBandRect.right());
908 } else {
909 --index;
910 selectionFinished = (index < 0) ||
911 ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
912 (!scrollVertical && widgetRect.right() < rubberBandRect.left());
913 }
914 } while (!selectionFinished);
915
916 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
917 // If Control is pressed, the selection state of all items in the rubberband is toggled.
918 // Therefore, the new selection contains:
919 // 1. All previously selected items which are not inside the rubberband, and
920 // 2. all items inside the rubberband which have not been selected previously.
921 m_selectionManager->setSelectedItems((m_oldSelection - selectedItems) + (selectedItems - m_oldSelection));
922 }
923 else {
924 m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
925 }
926 }
927
928 void KItemListController::startDragging()
929 {
930 if (!m_view || !m_model) {
931 return;
932 }
933
934 const QSet<int> selectedItems = m_selectionManager->selectedItems();
935 if (selectedItems.isEmpty()) {
936 return;
937 }
938
939 QMimeData* data = m_model->createMimeData(selectedItems);
940 if (!data) {
941 return;
942 }
943
944 // The created drag object will be owned and deleted
945 // by QApplication::activeWindow().
946 QDrag* drag = new QDrag(QApplication::activeWindow());
947 drag->setMimeData(data);
948
949 const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
950 drag->setPixmap(pixmap);
951
952 drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction);
953 }
954
955 KItemListWidget* KItemListController::hoveredWidget() const
956 {
957 Q_ASSERT(m_view);
958
959 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
960 if (widget->isHovered()) {
961 return widget;
962 }
963 }
964
965 return 0;
966 }
967
968 KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
969 {
970 Q_ASSERT(m_view);
971
972 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
973 const QPointF mappedPos = widget->mapFromItem(m_view, pos);
974
975 const bool hovered = widget->contains(mappedPos) &&
976 !widget->expansionToggleRect().contains(mappedPos);
977 if (hovered) {
978 return widget;
979 }
980 }
981
982 return 0;
983 }
984
985 void KItemListController::updateKeyboardAnchor()
986 {
987 const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
988 m_keyboardAnchorIndex < m_model->count() &&
989 keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
990 if (!validAnchor) {
991 const int index = m_selectionManager->currentItem();
992 m_keyboardAnchorIndex = index;
993 m_keyboardAnchorPos = keyboardAnchorPos(index);
994 }
995 }
996
997 int KItemListController::nextRowIndex() const
998 {
999 const int currentIndex = m_selectionManager->currentItem();
1000 if (m_keyboardAnchorIndex < 0) {
1001 return currentIndex;
1002 }
1003
1004 const int maxIndex = m_model->count() - 1;
1005 if (currentIndex == maxIndex) {
1006 return currentIndex;
1007 }
1008
1009 // Calculate the index of the last column inside the row of the current index
1010 int lastColumnIndex = currentIndex;
1011 while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
1012 ++lastColumnIndex;
1013 if (lastColumnIndex >= maxIndex) {
1014 return currentIndex;
1015 }
1016 }
1017
1018 // Based on the last column index go to the next row and calculate the nearest index
1019 // that is below the current index
1020 int nextRowIndex = lastColumnIndex + 1;
1021 int searchIndex = nextRowIndex;
1022 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
1023 while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
1024 ++searchIndex;
1025 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1026 if (searchDiff < minDiff) {
1027 minDiff = searchDiff;
1028 nextRowIndex = searchIndex;
1029 }
1030 }
1031
1032 return nextRowIndex;
1033 }
1034
1035 int KItemListController::previousRowIndex() const
1036 {
1037 const int currentIndex = m_selectionManager->currentItem();
1038 if (m_keyboardAnchorIndex < 0 || currentIndex == 0) {
1039 return currentIndex;
1040 }
1041
1042 // Calculate the index of the first column inside the row of the current index
1043 int firstColumnIndex = currentIndex;
1044 while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
1045 --firstColumnIndex;
1046 if (firstColumnIndex <= 0) {
1047 return currentIndex;
1048 }
1049 }
1050
1051 // Based on the first column index go to the previous row and calculate the nearest index
1052 // that is above the current index
1053 int previousRowIndex = firstColumnIndex - 1;
1054 int searchIndex = previousRowIndex;
1055 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
1056 while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
1057 --searchIndex;
1058 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1059 if (searchDiff < minDiff) {
1060 minDiff = searchDiff;
1061 previousRowIndex = searchIndex;
1062 }
1063 }
1064
1065 return previousRowIndex;
1066 }
1067
1068 qreal KItemListController::keyboardAnchorPos(int index) const
1069 {
1070 const QRectF itemRect = m_view->itemRect(index);
1071 if (!itemRect.isEmpty()) {
1072 return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y();
1073 }
1074
1075 return 0;
1076 }
1077
1078 #include "kitemlistcontroller.moc"