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