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