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