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