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