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