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