]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistcontroller.cpp
Reactivate the "Open folders during drag operations" feature
[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 if (m_view->scrollOrientation() == Qt::Vertical) {
464 endPos.ry() += m_view->scrollOffset();
465 if (m_view->itemSize().width() < 0) {
466 // Use a special rubberband for views that have only one column and
467 // expand the rubberband to use the whole width of the view.
468 endPos.setX(m_view->size().width());
469 }
470 } else {
471 endPos.rx() += m_view->scrollOffset();
472 }
473 rubberBand->setEndPosition(endPos);
474 }
475 }
476
477 return false;
478 }
479
480 bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
481 {
482 if (!m_view) {
483 return false;
484 }
485
486 const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
487 if (isAboveSelectionToggle) {
488 m_selectionTogglePressed = false;
489 return true;
490 }
491
492 if (!isAboveSelectionToggle && m_selectionTogglePressed) {
493 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
494 m_selectionTogglePressed = false;
495 return true;
496 }
497
498 const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
499 event->modifiers() & Qt::ControlModifier;
500
501 bool clearSelection = !shiftOrControlPressed && event->button() != Qt::RightButton;
502
503 KItemListRubberBand* rubberBand = m_view->rubberBand();
504 if (rubberBand->isActive()) {
505 disconnect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
506 rubberBand->setActive(false);
507 m_oldSelection.clear();
508 m_view->setAutoScroll(false);
509
510 if (rubberBand->startPosition() != rubberBand->endPosition()) {
511 clearSelection = false;
512 }
513 }
514
515 const QPointF pos = transform.map(event->pos());
516 const int index = m_view->itemAt(pos);
517
518 if (index >= 0 && index == m_pressedIndex) {
519 // The release event is done above the same item as the press event
520
521 if (clearSelection) {
522 // Clear the previous selection but reselect the current index
523 m_selectionManager->setSelectedItems(QSet<int>() << index);
524 }
525
526 if (event->button() & Qt::LeftButton) {
527 bool emitItemActivated = true;
528 if (m_view->isAboveExpansionToggle(index, pos)) {
529 const bool expanded = m_model->isExpanded(index);
530 m_model->setExpanded(index, !expanded);
531
532 emit itemExpansionToggleClicked(index);
533 emitItemActivated = false;
534 } else if (shiftOrControlPressed) {
535 // The mouse click should only update the selection, not trigger the item
536 emitItemActivated = false;
537 } else if (!KGlobalSettings::singleClick()) {
538 emitItemActivated = false;
539 }
540 if (emitItemActivated) {
541 emit itemActivated(index);
542 }
543 } else if (event->button() & Qt::MidButton) {
544 emit itemMiddleClicked(index);
545 }
546 } else if (clearSelection) {
547 m_selectionManager->clearSelection();
548 }
549
550 m_pressedMousePos = QPointF();
551 m_pressedIndex = -1;
552 return false;
553 }
554
555 bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
556 {
557 const QPointF pos = transform.map(event->pos());
558 const int index = m_view->itemAt(pos);
559
560 bool emitItemActivated = !KGlobalSettings::singleClick() &&
561 (event->button() & Qt::LeftButton) &&
562 index >= 0 && index < m_model->count();
563 if (emitItemActivated) {
564 emit itemActivated(index);
565 }
566 return false;
567 }
568
569 bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
570 {
571 Q_UNUSED(event);
572 Q_UNUSED(transform);
573 return false;
574 }
575
576 bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
577 {
578 Q_UNUSED(event);
579 Q_UNUSED(transform);
580 return false;
581 }
582
583 bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
584 {
585 Q_UNUSED(transform);
586 if (!m_model || !m_view) {
587 return false;
588 }
589
590 KItemListWidget* oldHoveredWidget = hoveredWidget();
591
592 const QPointF pos = transform.map(event->pos());
593 KItemListWidget* newHoveredWidget = widgetForPos(pos);
594
595 if (oldHoveredWidget != newHoveredWidget) {
596 m_autoActivationTimer->stop();
597
598 if (oldHoveredWidget) {
599 oldHoveredWidget->setHovered(false);
600 emit itemUnhovered(oldHoveredWidget->index());
601 }
602
603 if (newHoveredWidget) {
604 const int index = newHoveredWidget->index();
605 if (m_model->supportsDropping(index)) {
606 newHoveredWidget->setHovered(true);
607 }
608 emit itemHovered(index);
609
610 if (m_autoActivationTimer->interval() >= 0) {
611 m_autoActivationTimer->setProperty("index", index);
612 m_autoActivationTimer->start();
613 }
614 }
615 }
616
617 return false;
618 }
619
620 bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
621 {
622 Q_UNUSED(transform)
623 if (!m_view) {
624 return false;
625 }
626
627 m_autoActivationTimer->stop();
628
629 const QPointF pos = transform.map(event->pos());
630 const int index = m_view->itemAt(pos);
631 emit itemDropEvent(index, event);
632
633 return true;
634 }
635
636 bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
637 {
638 Q_UNUSED(event);
639 Q_UNUSED(transform);
640 return false;
641 }
642
643 bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
644 {
645 Q_UNUSED(transform);
646 if (!m_model || !m_view) {
647 return false;
648 }
649
650 KItemListWidget* oldHoveredWidget = hoveredWidget();
651 const QPointF pos = transform.map(event->pos());
652 KItemListWidget* newHoveredWidget = widgetForPos(pos);
653
654 if (oldHoveredWidget != newHoveredWidget) {
655 if (oldHoveredWidget) {
656 oldHoveredWidget->setHovered(false);
657 emit itemUnhovered(oldHoveredWidget->index());
658 }
659
660 if (newHoveredWidget) {
661 newHoveredWidget->setHovered(true);
662 emit itemHovered(newHoveredWidget->index());
663 }
664 }
665
666 return false;
667 }
668
669 bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
670 {
671 Q_UNUSED(event);
672 Q_UNUSED(transform);
673
674 if (!m_model || !m_view) {
675 return false;
676 }
677
678 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
679 if (widget->isHovered()) {
680 widget->setHovered(false);
681 emit itemUnhovered(widget->index());
682 }
683 }
684 return false;
685 }
686
687 bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
688 {
689 Q_UNUSED(event);
690 Q_UNUSED(transform);
691 return false;
692 }
693
694 bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
695 {
696 Q_UNUSED(event);
697 Q_UNUSED(transform);
698 return false;
699 }
700
701 bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
702 {
703 if (!event) {
704 return false;
705 }
706
707 switch (event->type()) {
708 case QEvent::KeyPress:
709 return keyPressEvent(static_cast<QKeyEvent*>(event));
710 case QEvent::InputMethod:
711 return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
712 case QEvent::GraphicsSceneMousePress:
713 return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
714 case QEvent::GraphicsSceneMouseMove:
715 return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
716 case QEvent::GraphicsSceneMouseRelease:
717 return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
718 case QEvent::GraphicsSceneMouseDoubleClick:
719 return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
720 case QEvent::GraphicsSceneWheel:
721 return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
722 case QEvent::GraphicsSceneDragEnter:
723 return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
724 case QEvent::GraphicsSceneDragLeave:
725 return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
726 case QEvent::GraphicsSceneDragMove:
727 return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
728 case QEvent::GraphicsSceneDrop:
729 return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
730 case QEvent::GraphicsSceneHoverEnter:
731 return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
732 case QEvent::GraphicsSceneHoverMove:
733 return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
734 case QEvent::GraphicsSceneHoverLeave:
735 return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
736 case QEvent::GraphicsSceneResize:
737 return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
738 default:
739 break;
740 }
741
742 return false;
743 }
744
745 void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
746 {
747 if (!m_view) {
748 return;
749 }
750
751 KItemListRubberBand* rubberBand = m_view->rubberBand();
752 if (rubberBand->isActive()) {
753 const qreal diff = current - previous;
754 // TODO: Ideally just QCursor::pos() should be used as
755 // new end-position but it seems there is no easy way
756 // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
757 // (... or I just missed an easy way to do the mapping)
758 QPointF endPos = rubberBand->endPosition();
759 if (m_view->scrollOrientation() == Qt::Vertical) {
760 endPos.ry() += diff;
761 } else {
762 endPos.rx() += diff;
763 }
764
765 rubberBand->setEndPosition(endPos);
766 }
767 }
768
769 void KItemListController::slotRubberBandChanged()
770 {
771 if (!m_view || !m_model || m_model->count() <= 0) {
772 return;
773 }
774
775 const KItemListRubberBand* rubberBand = m_view->rubberBand();
776 const QPointF startPos = rubberBand->startPosition();
777 const QPointF endPos = rubberBand->endPosition();
778 QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
779
780 const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
781 if (scrollVertical) {
782 rubberBandRect.translate(0, -m_view->scrollOffset());
783 } else {
784 rubberBandRect.translate(-m_view->scrollOffset(), 0);
785 }
786
787 if (!m_oldSelection.isEmpty()) {
788 // Clear the old selection that was available before the rubberband has
789 // been activated in case if no Shift- or Control-key are pressed
790 const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
791 QApplication::keyboardModifiers() & Qt::ControlModifier;
792 if (!shiftOrControlPressed) {
793 m_oldSelection.clear();
794 }
795 }
796
797 QSet<int> selectedItems;
798
799 // Select all visible items that intersect with the rubberband
800 foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
801 const int index = widget->index();
802
803 const QRectF widgetRect = m_view->itemRect(index);
804 if (widgetRect.intersects(rubberBandRect)) {
805 const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
806 const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
807 if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
808 selectedItems.insert(index);
809 }
810 }
811 }
812
813 // Select all invisible items that intersect with the rubberband. Instead of
814 // iterating all items only the area which might be touched by the rubberband
815 // will be checked.
816 const bool increaseIndex = scrollVertical ?
817 startPos.y() > endPos.y(): startPos.x() > endPos.x();
818
819 int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
820 bool selectionFinished = false;
821 do {
822 const QRectF widgetRect = m_view->itemRect(index);
823 if (widgetRect.intersects(rubberBandRect)) {
824 selectedItems.insert(index);
825 }
826
827 if (increaseIndex) {
828 ++index;
829 selectionFinished = (index >= m_model->count()) ||
830 ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
831 (!scrollVertical && widgetRect.left() > rubberBandRect.right());
832 } else {
833 --index;
834 selectionFinished = (index < 0) ||
835 ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
836 (!scrollVertical && widgetRect.right() < rubberBandRect.left());
837 }
838 } while (!selectionFinished);
839
840 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
841 // If Control is pressed, the selection state of all items in the rubberband is toggled.
842 // Therefore, the new selection contains:
843 // 1. All previously selected items which are not inside the rubberband, and
844 // 2. all items inside the rubberband which have not been selected previously.
845 m_selectionManager->setSelectedItems((m_oldSelection - selectedItems) + (selectedItems - m_oldSelection));
846 }
847 else {
848 m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
849 }
850 }
851
852 void KItemListController::startDragging()
853 {
854 if (!m_view || !m_model) {
855 return;
856 }
857
858 const QSet<int> selectedItems = m_selectionManager->selectedItems();
859 if (selectedItems.isEmpty()) {
860 return;
861 }
862
863 QMimeData* data = m_model->createMimeData(selectedItems);
864 if (!data) {
865 return;
866 }
867
868 // The created drag object will be owned and deleted
869 // by QApplication::activeWindow().
870 QDrag* drag = new QDrag(QApplication::activeWindow());
871 drag->setMimeData(data);
872
873 const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
874 drag->setPixmap(pixmap);
875
876 drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::IgnoreAction);
877 }
878
879 KItemListWidget* KItemListController::hoveredWidget() const
880 {
881 Q_ASSERT(m_view);
882
883 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
884 if (widget->isHovered()) {
885 return widget;
886 }
887 }
888
889 return 0;
890 }
891
892 KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
893 {
894 Q_ASSERT(m_view);
895
896 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
897 const QPointF mappedPos = widget->mapFromItem(m_view, pos);
898
899 const bool hovered = widget->contains(mappedPos) &&
900 !widget->expansionToggleRect().contains(mappedPos);
901 if (hovered) {
902 return widget;
903 }
904 }
905
906 return 0;
907 }
908
909 #include "kitemlistcontroller.moc"