Changeset View
Changeset View
Standalone View
Standalone View
src/kitemviews/kitemlistcontroller.cpp
Show All 37 Lines | |||||
38 | #include <QMimeData> | 38 | #include <QMimeData> | ||
39 | #include <QTimer> | 39 | #include <QTimer> | ||
40 | 40 | | |||
41 | KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) : | 41 | KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) : | ||
42 | QObject(parent), | 42 | QObject(parent), | ||
43 | m_singleClickActivationEnforced(false), | 43 | m_singleClickActivationEnforced(false), | ||
44 | m_selectionTogglePressed(false), | 44 | m_selectionTogglePressed(false), | ||
45 | m_clearSelectionIfItemsAreNotDragged(false), | 45 | m_clearSelectionIfItemsAreNotDragged(false), | ||
46 | m_cancelTouchOnRelease(false), | ||||
47 | m_touchDoubleClickInProgress(false), | ||||
46 | m_selectionBehavior(NoSelection), | 48 | m_selectionBehavior(NoSelection), | ||
47 | m_autoActivationBehavior(ActivationAndExpansion), | 49 | m_autoActivationBehavior(ActivationAndExpansion), | ||
48 | m_mouseDoubleClickAction(ActivateItemOnly), | 50 | m_mouseDoubleClickAction(ActivateItemOnly), | ||
49 | m_model(nullptr), | 51 | m_model(nullptr), | ||
50 | m_view(nullptr), | 52 | m_view(nullptr), | ||
51 | m_selectionManager(new KItemListSelectionManager(this)), | 53 | m_selectionManager(new KItemListSelectionManager(this)), | ||
52 | m_keyboardManager(new KItemListKeyboardSearchManager(this)), | 54 | m_keyboardManager(new KItemListKeyboardSearchManager(this)), | ||
53 | m_pressedIndex(-1), | 55 | m_pressedIndex(-1), | ||
54 | m_pressedMousePos(), | 56 | m_pressedMousePos(), | ||
55 | m_autoActivationTimer(nullptr), | 57 | m_autoActivationTimer(nullptr), | ||
56 | m_oldSelection(), | 58 | m_oldSelection(), | ||
57 | m_keyboardAnchorIndex(-1), | 59 | m_keyboardAnchorIndex(-1), | ||
58 | m_keyboardAnchorPos(0) | 60 | m_keyboardAnchorPos(0) | ||
59 | { | 61 | { | ||
60 | connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem, | 62 | connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem, | ||
61 | this, &KItemListController::slotChangeCurrentItem); | 63 | this, &KItemListController::slotChangeCurrentItem); | ||
62 | connect(m_selectionManager, &KItemListSelectionManager::currentChanged, | 64 | connect(m_selectionManager, &KItemListSelectionManager::currentChanged, | ||
63 | m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged); | 65 | m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged); | ||
64 | 66 | | |||
65 | m_autoActivationTimer = new QTimer(this); | 67 | m_autoActivationTimer = new QTimer(this); | ||
66 | m_autoActivationTimer->setSingleShot(true); | 68 | m_autoActivationTimer->setSingleShot(true); | ||
67 | m_autoActivationTimer->setInterval(-1); | 69 | m_autoActivationTimer->setInterval(-1); | ||
68 | connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout); | 70 | connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout); | ||
69 | 71 | | |||
72 | m_touchDoubleClickTimer.setSingleShot(true); | ||||
73 | connect(&m_touchDoubleClickTimer, &QTimer::timeout, [this]() { | ||||
elvisangelaccio: Please add `this` as receiver, otherwise this connection can lead to crashes. | |||||
abalaji: I'm sorry can you elaborate on this a little bit? | |||||
Basically Qt could call the lambda slot after the object has been already destroyed. More details here: https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f elvisangelaccio: Basically Qt could call the lambda slot after the object has been already destroyed.
More… | |||||
Ahh, of course! This makes so much sense. I've experienced weird crashes with connect and lambda's before and never knew this was a thing. Thanks for pointing it out! abalaji: Ahh, of course! This makes so much sense. I've experienced weird crashes with `connect` and… | |||||
74 | m_touchDoubleClickInProgress = false; | ||||
75 | }); | ||||
70 | setModel(model); | 76 | setModel(model); | ||
71 | setView(view); | 77 | setView(view); | ||
72 | } | 78 | } | ||
73 | 79 | | |||
74 | KItemListController::~KItemListController() | 80 | KItemListController::~KItemListController() | ||
75 | { | 81 | { | ||
76 | setView(nullptr); | 82 | setView(nullptr); | ||
77 | Q_ASSERT(!m_view); | 83 | Q_ASSERT(!m_view); | ||
▲ Show 20 Lines • Show All 462 Lines • ▼ Show 20 Line(s) | |||||
540 | bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | 546 | bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | ||
541 | { | 547 | { | ||
542 | if (!m_view) { | 548 | if (!m_view) { | ||
543 | return false; | 549 | return false; | ||
544 | } | 550 | } | ||
545 | 551 | | |||
546 | m_pressedMousePos = transform.map(event->pos()); | 552 | m_pressedMousePos = transform.map(event->pos()); | ||
547 | m_pressedIndex = m_view->itemAt(m_pressedMousePos); | 553 | m_pressedIndex = m_view->itemAt(m_pressedMousePos); | ||
548 | emit mouseButtonPressed(m_pressedIndex, event->buttons()); | 554 | const Qt::MouseButtons buttons = event->buttons(); | ||
555 | emit mouseButtonPressed(m_pressedIndex, buttons); | ||||
549 | 556 | | |||
550 | if (event->buttons() & (Qt::BackButton | Qt::ForwardButton)) { | 557 | if (buttons & (Qt::BackButton | Qt::ForwardButton)) { | ||
551 | // Do not select items when clicking the back/forward buttons, see | 558 | // Do not select items when clicking the back/forward buttons, see | ||
552 | // https://bugs.kde.org/show_bug.cgi?id=327412. | 559 | // https://bugs.kde.org/show_bug.cgi?id=327412. | ||
553 | return true; | 560 | return true; | ||
554 | } | 561 | } | ||
555 | 562 | | |||
556 | if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) { | 563 | if (!onPress(event->screenPos(), event->modifiers(), buttons)) { | ||
557 | m_selectionManager->endAnchoredSelection(); | | |||
558 | m_selectionManager->setCurrentItem(m_pressedIndex); | | |||
559 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | | |||
560 | return true; | | |||
561 | } | | |||
562 | | ||||
563 | m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); | | |||
564 | if (m_selectionTogglePressed) { | | |||
565 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | | |||
566 | // The previous anchored selection has been finished already in | | |||
567 | // KItemListSelectionManager::setSelected(). We can safely change | | |||
568 | // the current item and start a new anchored selection now. | | |||
569 | m_selectionManager->setCurrentItem(m_pressedIndex); | | |||
570 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | | |||
571 | return true; | | |||
572 | } | | |||
573 | | ||||
574 | const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; | | |||
575 | const bool controlPressed = event->modifiers() & Qt::ControlModifier; | | |||
576 | | ||||
577 | // The previous selection is cleared if either | | |||
578 | // 1. The selection mode is SingleSelection, or | | |||
579 | // 2. the selection mode is MultiSelection, and *none* of the following conditions are met: | | |||
580 | // a) Shift or Control are pressed. | | |||
581 | // b) The clicked item is selected already. In that case, the user might want to: | | |||
582 | // - start dragging multiple items, or | | |||
583 | // - open the context menu and perform an action for all selected items. | | |||
584 | const bool shiftOrControlPressed = shiftPressed || controlPressed; | | |||
585 | const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex); | | |||
586 | const bool clearSelection = m_selectionBehavior == SingleSelection || | | |||
587 | (!shiftOrControlPressed && !pressedItemAlreadySelected); | | |||
588 | if (clearSelection) { | | |||
589 | m_selectionManager->clearSelection(); | | |||
590 | } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (event->buttons() & Qt::LeftButton)) { | | |||
591 | // The user might want to start dragging multiple items, but if he clicks the item | | |||
592 | // in order to trigger it instead, the other selected items must be deselected. | | |||
593 | // However, we do not know yet what the user is going to do. | | |||
594 | // -> remember that the user pressed an item which had been selected already and | | |||
595 | // clear the selection in mouseReleaseEvent(), unless the items are dragged. | | |||
596 | m_clearSelectionIfItemsAreNotDragged = true; | | |||
597 | | ||||
598 | if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex, m_pressedMousePos)) { | | |||
599 | emit selectedItemTextPressed(m_pressedIndex); | | |||
600 | } | | |||
601 | } | | |||
602 | | ||||
603 | if (!shiftPressed) { | | |||
604 | // Finish the anchored selection before the current index is changed | | |||
605 | m_selectionManager->endAnchoredSelection(); | | |||
606 | } | | |||
607 | | ||||
608 | if (m_pressedIndex >= 0) { | | |||
609 | m_selectionManager->setCurrentItem(m_pressedIndex); | | |||
610 | | ||||
611 | switch (m_selectionBehavior) { | | |||
612 | case NoSelection: | | |||
613 | break; | | |||
614 | | ||||
615 | case SingleSelection: | | |||
616 | m_selectionManager->setSelected(m_pressedIndex); | | |||
617 | break; | | |||
618 | | ||||
619 | case MultiSelection: | | |||
620 | if (controlPressed && !shiftPressed) { | | |||
621 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | | |||
622 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | | |||
623 | } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) { | | |||
624 | // Select the pressed item and start a new anchored selection | | |||
625 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); | | |||
626 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | | |||
627 | } | | |||
628 | break; | | |||
629 | | ||||
630 | default: | | |||
631 | Q_ASSERT(false); | | |||
632 | break; | | |||
633 | } | | |||
634 | | ||||
635 | if (event->buttons() & Qt::RightButton) { | | |||
636 | emit itemContextMenuRequested(m_pressedIndex, event->screenPos()); | | |||
637 | } | | |||
638 | | ||||
639 | return true; | | |||
640 | } | | |||
641 | | ||||
642 | if (event->buttons() & Qt::RightButton) { | | |||
643 | const QRectF headerBounds = m_view->headerBoundaries(); | | |||
644 | if (headerBounds.contains(event->pos())) { | | |||
645 | emit headerContextMenuRequested(event->screenPos()); | | |||
646 | } else { | | |||
647 | emit viewContextMenuRequested(event->screenPos()); | | |||
648 | } | | |||
649 | return true; | | |||
650 | } | | |||
651 | | ||||
652 | if (m_selectionBehavior == MultiSelection) { | 564 | if (m_selectionBehavior == MultiSelection) { | ||
653 | QPointF startPos = m_pressedMousePos; | 565 | QPointF startPos = m_pressedMousePos; | ||
654 | if (m_view->scrollOrientation() == Qt::Vertical) { | 566 | if (m_view->scrollOrientation() == Qt::Vertical) { | ||
655 | startPos.ry() += m_view->scrollOffset(); | 567 | startPos.ry() += m_view->scrollOffset(); | ||
656 | if (m_view->itemSize().width() < 0) { | 568 | if (m_view->itemSize().width() < 0) { | ||
657 | // Use a special rubberband for views that have only one column and | 569 | // Use a special rubberband for views that have only one column and | ||
658 | // expand the rubberband to use the whole width of the view. | 570 | // expand the rubberband to use the whole width of the view. | ||
659 | startPos.setX(0); | 571 | startPos.setX(0); | ||
660 | } | 572 | } | ||
661 | } else { | 573 | } else { | ||
662 | startPos.rx() += m_view->scrollOffset(); | 574 | startPos.rx() += m_view->scrollOffset(); | ||
663 | } | 575 | } | ||
664 | 576 | | |||
665 | m_oldSelection = m_selectionManager->selectedItems(); | 577 | m_oldSelection = m_selectionManager->selectedItems(); | ||
666 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | 578 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | ||
667 | rubberBand->setStartPosition(startPos); | 579 | rubberBand->setStartPosition(startPos); | ||
668 | rubberBand->setEndPosition(startPos); | 580 | rubberBand->setEndPosition(startPos); | ||
669 | rubberBand->setActive(true); | 581 | rubberBand->setActive(true); | ||
670 | connect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged); | 582 | connect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged); | ||
671 | m_view->setAutoScroll(true); | 583 | m_view->setAutoScroll(true); | ||
672 | } | 584 | } | ||
673 | | ||||
674 | return false; | 585 | return false; | ||
586 | } else { | ||||
587 | return true; | ||||
588 | } | ||||
675 | } | 589 | } | ||
676 | 590 | | |||
677 | bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | 591 | bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | ||
678 | { | 592 | { | ||
679 | if (!m_view) { | 593 | if (!m_view) { | ||
680 | return false; | 594 | return false; | ||
681 | } | 595 | } | ||
682 | 596 | | |||
683 | if (m_pressedIndex >= 0) { | 597 | onMove(transform.map(event->pos()), event->buttons()); | ||
684 | // Check whether a dragging should be started | | |||
685 | if (event->buttons() & Qt::LeftButton) { | | |||
686 | const QPointF pos = transform.map(event->pos()); | | |||
687 | if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) { | | |||
688 | if (!m_selectionManager->isSelected(m_pressedIndex)) { | | |||
689 | // Always assure that the dragged item gets selected. Usually this is already | | |||
690 | // done on the mouse-press event, but when using the selection-toggle on a | | |||
691 | // selected item the dragged item is not selected yet. | | |||
692 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | | |||
693 | } else { | | |||
694 | // A selected item has been clicked to drag all selected items | | |||
695 | // -> the selection should not be cleared when the mouse button is released. | | |||
696 | m_clearSelectionIfItemsAreNotDragged = false; | | |||
697 | } | | |||
698 | | ||||
699 | startDragging(); | | |||
700 | } | | |||
701 | } | | |||
702 | } else { | | |||
703 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | | |||
704 | if (rubberBand->isActive()) { | | |||
705 | QPointF endPos = transform.map(event->pos()); | | |||
706 | | ||||
707 | // Update the current item. | | |||
708 | const int newCurrent = m_view->itemAt(endPos); | | |||
709 | if (newCurrent >= 0) { | | |||
710 | // It's expected that the new current index is also the new anchor (bug 163451). | | |||
711 | m_selectionManager->endAnchoredSelection(); | | |||
712 | m_selectionManager->setCurrentItem(newCurrent); | | |||
713 | m_selectionManager->beginAnchoredSelection(newCurrent); | | |||
714 | } | | |||
715 | | ||||
716 | if (m_view->scrollOrientation() == Qt::Vertical) { | | |||
717 | endPos.ry() += m_view->scrollOffset(); | | |||
718 | if (m_view->itemSize().width() < 0) { | | |||
719 | // Use a special rubberband for views that have only one column and | | |||
720 | // expand the rubberband to use the whole width of the view. | | |||
721 | endPos.setX(m_view->size().width()); | | |||
722 | } | | |||
723 | } else { | | |||
724 | endPos.rx() += m_view->scrollOffset(); | | |||
725 | } | | |||
726 | rubberBand->setEndPosition(endPos); | | |||
727 | } | | |||
728 | } | | |||
729 | | ||||
730 | return false; | 598 | return false; | ||
731 | } | 599 | } | ||
732 | 600 | | |||
733 | bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | 601 | bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | ||
734 | { | 602 | { | ||
735 | if (!m_view) { | 603 | if (!m_view) { | ||
736 | return false; | 604 | return false; | ||
737 | } | 605 | } | ||
738 | 606 | | |||
739 | emit mouseButtonReleased(m_pressedIndex, event->buttons()); | 607 | emit mouseButtonReleased(m_pressedIndex, event->buttons()); | ||
740 | 608 | | |||
741 | const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); | 609 | return onRelease(transform.map(event->pos()), event->modifiers(), event->button()); | ||
742 | if (isAboveSelectionToggle) { | | |||
743 | m_selectionTogglePressed = false; | | |||
744 | return true; | | |||
745 | } | | |||
746 | | ||||
747 | if (!isAboveSelectionToggle && m_selectionTogglePressed) { | | |||
748 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | | |||
749 | m_selectionTogglePressed = false; | | |||
750 | return true; | | |||
751 | } | | |||
752 | | ||||
753 | const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || | | |||
754 | event->modifiers() & Qt::ControlModifier; | | |||
755 | | ||||
756 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | | |||
757 | if (rubberBand->isActive()) { | | |||
758 | disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged); | | |||
759 | rubberBand->setActive(false); | | |||
760 | m_oldSelection.clear(); | | |||
761 | m_view->setAutoScroll(false); | | |||
762 | } | | |||
763 | | ||||
764 | const QPointF pos = transform.map(event->pos()); | | |||
765 | const int index = m_view->itemAt(pos); | | |||
766 | | ||||
767 | if (index >= 0 && index == m_pressedIndex) { | | |||
768 | // The release event is done above the same item as the press event | | |||
769 | | ||||
770 | if (m_clearSelectionIfItemsAreNotDragged) { | | |||
771 | // A selected item has been clicked, but no drag operation has been started | | |||
772 | // -> clear the rest of the selection. | | |||
773 | m_selectionManager->clearSelection(); | | |||
774 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); | | |||
775 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | | |||
776 | } | | |||
777 | | ||||
778 | if (event->button() & Qt::LeftButton) { | | |||
779 | bool emitItemActivated = true; | | |||
780 | if (m_view->isAboveExpansionToggle(index, pos)) { | | |||
781 | const bool expanded = m_model->isExpanded(index); | | |||
782 | m_model->setExpanded(index, !expanded); | | |||
783 | | ||||
784 | emit itemExpansionToggleClicked(index); | | |||
785 | emitItemActivated = false; | | |||
786 | } else if (shiftOrControlPressed) { | | |||
787 | // The mouse click should only update the selection, not trigger the item | | |||
788 | emitItemActivated = false; | | |||
789 | } else if (!(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)) { | | |||
790 | emitItemActivated = false; | | |||
791 | } | | |||
792 | if (emitItemActivated) { | | |||
793 | emit itemActivated(index); | | |||
794 | } | | |||
795 | } else if (event->button() & Qt::MidButton) { | | |||
796 | emit itemMiddleClicked(index); | | |||
797 | } | | |||
798 | } | | |||
799 | | ||||
800 | m_pressedMousePos = QPointF(); | | |||
801 | m_pressedIndex = -1; | | |||
802 | m_clearSelectionIfItemsAreNotDragged = false; | | |||
803 | return false; | | |||
804 | } | 610 | } | ||
805 | 611 | | |||
806 | bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | 612 | bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) | ||
807 | { | 613 | { | ||
808 | const QPointF pos = transform.map(event->pos()); | 614 | const QPointF pos = transform.map(event->pos()); | ||
809 | const int index = m_view->itemAt(pos); | 615 | const int index = m_view->itemAt(pos); | ||
810 | 616 | | |||
811 | // Expand item if desired - See Bug 295573 | 617 | return onDoubleClick(pos, event->screenPos(), index, event->button()); | ||
812 | if (m_mouseDoubleClickAction != ActivateItemOnly) { | | |||
813 | if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index)) { | | |||
814 | const bool expanded = m_model->isExpanded(index); | | |||
815 | m_model->setExpanded(index, !expanded); | | |||
816 | } | | |||
817 | } | | |||
818 | | ||||
819 | if (event->button() & Qt::RightButton) { | | |||
820 | m_selectionManager->clearSelection(); | | |||
821 | if (index >= 0) { | | |||
822 | m_selectionManager->setSelected(index); | | |||
823 | emit itemContextMenuRequested(index, event->screenPos()); | | |||
824 | } else { | | |||
825 | const QRectF headerBounds = m_view->headerBoundaries(); | | |||
826 | if (headerBounds.contains(event->pos())) { | | |||
827 | emit headerContextMenuRequested(event->screenPos()); | | |||
828 | } else { | | |||
829 | emit viewContextMenuRequested(event->screenPos()); | | |||
830 | } | | |||
831 | } | | |||
832 | return true; | | |||
833 | } | | |||
834 | | ||||
835 | bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) && | | |||
836 | (event->button() & Qt::LeftButton) && | | |||
837 | index >= 0 && index < m_model->count(); | | |||
838 | if (emitItemActivated) { | | |||
839 | emit itemActivated(index); | | |||
840 | } | | |||
841 | return false; | | |||
842 | } | 618 | } | ||
843 | 619 | | |||
844 | bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) | 620 | bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) | ||
845 | { | 621 | { | ||
846 | Q_UNUSED(event); | 622 | Q_UNUSED(event); | ||
847 | Q_UNUSED(transform); | 623 | Q_UNUSED(transform); | ||
848 | 624 | | |||
849 | DragAndDropHelper::clearUrlListMatchesUrlCache(); | 625 | DragAndDropHelper::clearUrlListMatchesUrlCache(); | ||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Line(s) | |||||
1030 | 806 | | |||
1031 | bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform) | 807 | bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform) | ||
1032 | { | 808 | { | ||
1033 | Q_UNUSED(event); | 809 | Q_UNUSED(event); | ||
1034 | Q_UNUSED(transform); | 810 | Q_UNUSED(transform); | ||
1035 | return false; | 811 | return false; | ||
1036 | } | 812 | } | ||
1037 | 813 | | |||
814 | bool KItemListController::touchBeginEvent(QTouchEvent* event, const QTransform& transform) | ||||
815 | { | ||||
816 | if (!m_view) { | ||||
817 | return false; | ||||
818 | } | ||||
819 | | ||||
820 | const QTouchEvent::TouchPoint& touchPoint = event->touchPoints()[0]; | ||||
821 | | ||||
822 | m_pressedMousePos = transform.map(touchPoint.pos()); | ||||
823 | m_pressedIndex = m_view->itemAt(m_pressedMousePos); | ||||
824 | onPress(touchPoint.screenPos().toPoint(), event->modifiers()); | ||||
825 | return true; | ||||
826 | } | ||||
827 | | ||||
828 | bool KItemListController::touchUpdateEvent(QTouchEvent* event, const QTransform& transform) | ||||
829 | { | ||||
830 | if (!m_view) { | ||||
831 | return false; | ||||
832 | } | ||||
833 | | ||||
834 | const auto touchState = event->touchPointStates(); | ||||
835 | | ||||
836 | if (event->touchPoints().length() > 1) { | ||||
837 | return true; | ||||
838 | } | ||||
839 | if (touchState & Qt::TouchPointMoved) { | ||||
840 | if (!m_cancelTouchOnRelease) { | ||||
841 | const QPointF& pos = transform.map(event->touchPoints()[0].pos()); | ||||
842 | // allow finger to fumble about a bit, but beyond the threshold suppress on release action | ||||
843 | if ((m_pressedMousePos - pos).manhattanLength() >= QApplication::startDragDistance()) { | ||||
844 | m_cancelTouchOnRelease = true; | ||||
845 | } | ||||
846 | } | ||||
847 | } | ||||
848 | return true; | ||||
849 | } | ||||
850 | | ||||
851 | bool KItemListController::touchEndEvent(QTouchEvent* event, const QTransform& transform) | ||||
852 | { | ||||
853 | if (!m_view) { | ||||
854 | return false; | ||||
855 | } | ||||
856 | | ||||
857 | if (m_cancelTouchOnRelease) { | ||||
858 | m_cancelTouchOnRelease = false; | ||||
859 | return true; | ||||
860 | } | ||||
861 | | ||||
862 | const QTouchEvent::TouchPoint& touchPoint = event->touchPoints()[0]; | ||||
863 | const QPointF pos = transform.map(touchPoint.pos()); | ||||
864 | const int index = m_view->itemAt(pos); | ||||
865 | if (m_touchDoubleClickInProgress) { | ||||
866 | if ( | ||||
867 | (index >= 0 && index == m_touchDoubleClickStartIndex) || | ||||
868 | (pos - m_touchDoubleClickStartPos).manhattanLength() < 20 | ||||
869 | ) { | ||||
870 | onDoubleClick(pos, touchPoint.screenPos().toPoint(), m_pressedIndex, Qt::LeftButton); | ||||
871 | } | ||||
872 | } else { | ||||
873 | m_touchDoubleClickInProgress = true; | ||||
874 | } | ||||
875 | m_touchDoubleClickStartPos = pos; | ||||
876 | m_touchDoubleClickStartIndex = index; | ||||
877 | m_touchDoubleClickTimer.start(QApplication::doubleClickInterval()); | ||||
878 | | ||||
879 | onRelease(pos, event->modifiers(), Qt::LeftButton); | ||||
880 | return true; | ||||
881 | } | ||||
882 | | ||||
1038 | bool KItemListController::processEvent(QEvent* event, const QTransform& transform) | 883 | bool KItemListController::processEvent(QEvent* event, const QTransform& transform) | ||
1039 | { | 884 | { | ||
1040 | if (!event) { | 885 | if (!event) { | ||
1041 | return false; | 886 | return false; | ||
1042 | } | 887 | } | ||
1043 | 888 | | |||
1044 | switch (event->type()) { | 889 | switch (event->type()) { | ||
1045 | case QEvent::KeyPress: | 890 | case QEvent::KeyPress: | ||
Show All 21 Lines | |||||
1067 | case QEvent::GraphicsSceneHoverEnter: | 912 | case QEvent::GraphicsSceneHoverEnter: | ||
1068 | return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | 913 | return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | ||
1069 | case QEvent::GraphicsSceneHoverMove: | 914 | case QEvent::GraphicsSceneHoverMove: | ||
1070 | return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | 915 | return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | ||
1071 | case QEvent::GraphicsSceneHoverLeave: | 916 | case QEvent::GraphicsSceneHoverLeave: | ||
1072 | return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | 917 | return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform()); | ||
1073 | case QEvent::GraphicsSceneResize: | 918 | case QEvent::GraphicsSceneResize: | ||
1074 | return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform); | 919 | return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform); | ||
920 | case QEvent::TouchBegin: | ||||
921 | return touchBeginEvent(static_cast<QTouchEvent*>(event), QTransform()); | ||||
922 | case QEvent::TouchUpdate: | ||||
923 | return touchUpdateEvent(static_cast<QTouchEvent*>(event), QTransform()); | ||||
924 | case QEvent::TouchEnd: | ||||
925 | return touchEndEvent(static_cast<QTouchEvent*>(event), QTransform()); | ||||
1075 | default: | 926 | default: | ||
1076 | break; | 927 | break; | ||
1077 | } | 928 | } | ||
1078 | 929 | | |||
1079 | return false; | 930 | return false; | ||
1080 | } | 931 | } | ||
1081 | 932 | | |||
1082 | void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous) | 933 | void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous) | ||
▲ Show 20 Lines • Show All 264 Lines • ▼ Show 20 Line(s) | 1196 | if (m_view) { | |||
1347 | KItemListStyleOption option = m_view->styleOption(); | 1198 | KItemListStyleOption option = m_view->styleOption(); | ||
1348 | if (option.extendedSelectionRegion != extend) { | 1199 | if (option.extendedSelectionRegion != extend) { | ||
1349 | option.extendedSelectionRegion = extend; | 1200 | option.extendedSelectionRegion = extend; | ||
1350 | m_view->setStyleOption(option); | 1201 | m_view->setStyleOption(option); | ||
1351 | } | 1202 | } | ||
1352 | } | 1203 | } | ||
1353 | } | 1204 | } | ||
1354 | 1205 | | |||
1206 | bool KItemListController::onPress(const QPoint& screenPos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons) | ||||
1207 | { | ||||
1208 | if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) { | ||||
1209 | m_selectionManager->endAnchoredSelection(); | ||||
1210 | m_selectionManager->setCurrentItem(m_pressedIndex); | ||||
1211 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | ||||
1212 | return true; | ||||
1213 | } | ||||
1214 | | ||||
1215 | m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); | ||||
1216 | if (m_selectionTogglePressed) { | ||||
1217 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | ||||
1218 | // The previous anchored selection has been finished already in | ||||
1219 | // KItemListSelectionManager::setSelected(). We can safely change | ||||
1220 | // the current item and start a new anchored selection now. | ||||
1221 | m_selectionManager->setCurrentItem(m_pressedIndex); | ||||
1222 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | ||||
1223 | return true; | ||||
1224 | } | ||||
1225 | | ||||
1226 | const bool shiftPressed = modifiers & Qt::ShiftModifier; | ||||
1227 | const bool controlPressed = modifiers & Qt::ControlModifier; | ||||
1228 | | ||||
1229 | // The previous selection is cleared if either | ||||
1230 | // 1. The selection mode is SingleSelection, or | ||||
1231 | // 2. the selection mode is MultiSelection, and *none* of the following conditions are met: | ||||
1232 | // a) Shift or Control are pressed. | ||||
1233 | // b) The clicked item is selected already. In that case, the user might want to: | ||||
1234 | // - start dragging multiple items, or | ||||
1235 | // - open the context menu and perform an action for all selected items. | ||||
1236 | const bool shiftOrControlPressed = shiftPressed || controlPressed; | ||||
1237 | const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex); | ||||
1238 | const bool clearSelection = m_selectionBehavior == SingleSelection || | ||||
1239 | (!shiftOrControlPressed && !pressedItemAlreadySelected); | ||||
1240 | if (clearSelection) { | ||||
1241 | m_selectionManager->clearSelection(); | ||||
1242 | } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (buttons & Qt::LeftButton)) { | ||||
1243 | // The user might want to start dragging multiple items, but if he clicks the item | ||||
1244 | // in order to trigger it instead, the other selected items must be deselected. | ||||
1245 | // However, we do not know yet what the user is going to do. | ||||
1246 | // -> remember that the user pressed an item which had been selected already and | ||||
1247 | // clear the selection in mouseReleaseEvent(), unless the items are dragged. | ||||
1248 | m_clearSelectionIfItemsAreNotDragged = true; | ||||
1249 | | ||||
1250 | if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex, m_pressedMousePos)) { | ||||
1251 | emit selectedItemTextPressed(m_pressedIndex); | ||||
1252 | } | ||||
1253 | } | ||||
1254 | | ||||
1255 | if (!shiftPressed) { | ||||
1256 | // Finish the anchored selection before the current index is changed | ||||
1257 | m_selectionManager->endAnchoredSelection(); | ||||
1258 | } | ||||
1259 | | ||||
1260 | if (m_pressedIndex >= 0) { | ||||
1261 | m_selectionManager->setCurrentItem(m_pressedIndex); | ||||
1262 | | ||||
1263 | switch (m_selectionBehavior) { | ||||
1264 | case NoSelection: | ||||
1265 | break; | ||||
1266 | | ||||
1267 | case SingleSelection: | ||||
1268 | m_selectionManager->setSelected(m_pressedIndex); | ||||
1269 | break; | ||||
1270 | | ||||
1271 | case MultiSelection: | ||||
1272 | if (controlPressed && !shiftPressed) { | ||||
1273 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | ||||
1274 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | ||||
1275 | } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) { | ||||
1276 | // Select the pressed item and start a new anchored selection | ||||
1277 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); | ||||
1278 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | ||||
1279 | } | ||||
1280 | break; | ||||
1281 | | ||||
1282 | default: | ||||
1283 | Q_ASSERT(false); | ||||
1284 | break; | ||||
1285 | } | ||||
1286 | | ||||
1287 | if (buttons & Qt::RightButton) { | ||||
1288 | emit itemContextMenuRequested(m_pressedIndex, screenPos); | ||||
1289 | } | ||||
1290 | | ||||
1291 | return true; | ||||
1292 | } | ||||
1293 | | ||||
1294 | if (buttons & Qt::RightButton) { | ||||
1295 | const QRectF headerBounds = m_view->headerBoundaries(); | ||||
1296 | if (headerBounds.contains(m_pressedMousePos)) { | ||||
1297 | emit headerContextMenuRequested(screenPos); | ||||
1298 | } else { | ||||
1299 | emit viewContextMenuRequested(screenPos); | ||||
1300 | } | ||||
1301 | return true; | ||||
1302 | } | ||||
1303 | | ||||
1304 | return false; | ||||
1305 | } | ||||
1306 | | ||||
1307 | void KItemListController::onMove(const QPointF& pos, const Qt::MouseButtons buttons) | ||||
1308 | { | ||||
1309 | if (m_pressedIndex >= 0) { | ||||
1310 | // Check whether a dragging should be started | ||||
1311 | if (buttons & Qt::LeftButton) { | ||||
1312 | if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) { | ||||
1313 | if (!m_selectionManager->isSelected(m_pressedIndex)) { | ||||
1314 | // Always assure that the dragged item gets selected. Usually this is already | ||||
1315 | // done on the mouse-press event, but when using the selection-toggle on a | ||||
1316 | // selected item the dragged item is not selected yet. | ||||
1317 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | ||||
1318 | } else { | ||||
1319 | // A selected item has been clicked to drag all selected items | ||||
1320 | // -> the selection should not be cleared when the mouse button is released. | ||||
1321 | m_clearSelectionIfItemsAreNotDragged = false; | ||||
1322 | } | ||||
1323 | | ||||
1324 | startDragging(); | ||||
1325 | } | ||||
1326 | } | ||||
1327 | } else { | ||||
1328 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | ||||
1329 | if (rubberBand->isActive()) { | ||||
1330 | // Update the current item. | ||||
1331 | QPointF endPos(pos); | ||||
1332 | const int newCurrent = m_view->itemAt(endPos); | ||||
1333 | if (newCurrent >= 0) { | ||||
1334 | // It's expected that the new current index is also the new anchor (bug 163451). | ||||
1335 | m_selectionManager->endAnchoredSelection(); | ||||
1336 | m_selectionManager->setCurrentItem(newCurrent); | ||||
1337 | m_selectionManager->beginAnchoredSelection(newCurrent); | ||||
1338 | } | ||||
1339 | | ||||
1340 | if (m_view->scrollOrientation() == Qt::Vertical) { | ||||
1341 | endPos.ry() += m_view->scrollOffset(); | ||||
1342 | if (m_view->itemSize().width() < 0) { | ||||
1343 | // Use a special rubberband for views that have only one column and | ||||
1344 | // expand the rubberband to use the whole width of the view. | ||||
1345 | endPos.setX(m_view->size().width()); | ||||
1346 | } | ||||
1347 | } else { | ||||
1348 | endPos.rx() += m_view->scrollOffset(); | ||||
1349 | } | ||||
1350 | rubberBand->setEndPosition(endPos); | ||||
1351 | } | ||||
1352 | } | ||||
1353 | } | ||||
1354 | | ||||
1355 | bool KItemListController::onRelease(const QPointF& pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons button) | ||||
1356 | { | ||||
1357 | const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); | ||||
1358 | if (isAboveSelectionToggle) { | ||||
1359 | m_selectionTogglePressed = false; | ||||
1360 | return true; | ||||
1361 | } | ||||
1362 | | ||||
1363 | if (!isAboveSelectionToggle && m_selectionTogglePressed) { | ||||
1364 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); | ||||
1365 | m_selectionTogglePressed = false; | ||||
1366 | return true; | ||||
1367 | } | ||||
1368 | | ||||
1369 | const bool shiftOrControlPressed = modifiers & Qt::ShiftModifier || | ||||
1370 | modifiers & Qt::ControlModifier; | ||||
1371 | | ||||
1372 | KItemListRubberBand* rubberBand = m_view->rubberBand(); | ||||
1373 | if (rubberBand->isActive()) { | ||||
1374 | disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged); | ||||
1375 | rubberBand->setActive(false); | ||||
1376 | m_oldSelection.clear(); | ||||
1377 | m_view->setAutoScroll(false); | ||||
1378 | } | ||||
1379 | | ||||
1380 | const int index = m_view->itemAt(pos); | ||||
1381 | | ||||
1382 | if (index >= 0 && index == m_pressedIndex) { | ||||
1383 | // The release event is done above the same item as the press event | ||||
1384 | | ||||
1385 | if (m_clearSelectionIfItemsAreNotDragged) { | ||||
1386 | // A selected item has been clicked, but no drag operation has been started | ||||
1387 | // -> clear the rest of the selection. | ||||
1388 | m_selectionManager->clearSelection(); | ||||
1389 | m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); | ||||
1390 | m_selectionManager->beginAnchoredSelection(m_pressedIndex); | ||||
1391 | } | ||||
1392 | | ||||
1393 | if (button & Qt::LeftButton) { | ||||
1394 | if (m_view->isAboveExpansionToggle(index, pos)) { | ||||
1395 | const bool expanded = m_model->isExpanded(index); | ||||
1396 | m_model->setExpanded(index, !expanded); | ||||
1397 | | ||||
1398 | emit itemExpansionToggleClicked(index); | ||||
1399 | } else if ( | ||||
1400 | !shiftOrControlPressed && ( | ||||
1401 | m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || | ||||
1402 | m_singleClickActivationEnforced | ||||
1403 | ) | ||||
1404 | ) { | ||||
1405 | emit itemActivated(index); | ||||
1406 | } | ||||
1407 | } else if (button & Qt::MidButton) { | ||||
emitItemActivated increases complexity, so just remove it if (m_view->isAboveExpansionToggle(index, pos)) { const bool expanded = m_model->isExpanded(index); m_model->setExpanded(index, !expanded); emit itemExpansionToggleClicked(index); } else if (shiftOrControlPressed || m_singleClickActivationEnforced || !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick))) { // The mouse click should only update the selection, not trigger the item } else { emit itemActivated(index); } anthonyfieroni: emitItemActivated increases complexity, so just remove it
```
if (m_view… | |||||
1408 | emit itemMiddleClicked(index); | ||||
1409 | } | ||||
1410 | } | ||||
1411 | | ||||
1412 | m_pressedMousePos = QPointF(); | ||||
1413 | m_pressedIndex = -1; | ||||
1414 | m_clearSelectionIfItemsAreNotDragged = false; | ||||
1415 | return false; | ||||
1416 | } | ||||
1417 | | ||||
1418 | bool KItemListController::onDoubleClick(const QPointF& pos, const QPoint& screenPos, const int index, const Qt::MouseButton button) | ||||
1419 | { | ||||
1420 | // Expand item if desired - See Bug 295573 | ||||
1421 | if (m_mouseDoubleClickAction != ActivateItemOnly) { | ||||
1422 | if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index)) { | ||||
1423 | const bool expanded = m_model->isExpanded(index); | ||||
1424 | m_model->setExpanded(index, !expanded); | ||||
1425 | } | ||||
1426 | } | ||||
1427 | | ||||
1428 | if (button & Qt::RightButton) { | ||||
1429 | m_selectionManager->clearSelection(); | ||||
1430 | if (index >= 0) { | ||||
1431 | m_selectionManager->setSelected(index); | ||||
1432 | emit itemContextMenuRequested(index, screenPos); | ||||
1433 | } else { | ||||
1434 | const QRectF headerBounds = m_view->headerBoundaries(); | ||||
1435 | if (headerBounds.contains(pos)) { | ||||
1436 | emit headerContextMenuRequested(screenPos); | ||||
1437 | } else { | ||||
1438 | emit viewContextMenuRequested(screenPos); | ||||
1439 | } | ||||
1440 | } | ||||
1441 | return true; | ||||
1442 | } | ||||
1443 | | ||||
1444 | bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) && | ||||
1445 | (button & Qt::LeftButton) && | ||||
1446 | index >= 0 && index < m_model->count(); | ||||
1447 | if (emitItemActivated) { | ||||
1448 | emit itemActivated(index); | ||||
1449 | } | ||||
1450 | return false; | ||||
1451 | } |
Please add this as receiver, otherwise this connection can lead to crashes.