File indexing completed on 2024-04-28 05:45:07

0001 /*
0002  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
0003  *
0004  * Based on the Itemviews NG project from Trolltech Labs
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KITEMLISTCONTROLLER_H
0010 #define KITEMLISTCONTROLLER_H
0011 
0012 #include <optional>
0013 
0014 #include "dolphin_export.h"
0015 #include "kitemset.h"
0016 
0017 #include <QObject>
0018 #include <QPointF>
0019 #include <QScroller>
0020 
0021 class QTimer;
0022 class KItemModelBase;
0023 class KItemListKeyboardSearchManager;
0024 class KItemListSelectionManager;
0025 class KItemListView;
0026 class KItemListWidget;
0027 class QContextMenuEvent;
0028 class QGestureEvent;
0029 class QGraphicsSceneHoverEvent;
0030 class QGraphicsSceneDragDropEvent;
0031 class QGraphicsSceneMouseEvent;
0032 class QGraphicsSceneResizeEvent;
0033 class QGraphicsSceneWheelEvent;
0034 class QInputMethodEvent;
0035 class QKeyEvent;
0036 class QTapGesture;
0037 class QTransform;
0038 class QTouchEvent;
0039 
0040 /**
0041  * @brief Controls the view, model and selection of an item-list.
0042  *
0043  * For a working item-list it is mandatory to set a compatible view and model
0044  * with KItemListController::setView() and KItemListController::setModel().
0045  *
0046  * @see KItemListView
0047  * @see KItemModelBase
0048  * @see KItemListSelectionManager
0049  */
0050 class DOLPHIN_EXPORT KItemListController : public QObject
0051 {
0052     Q_OBJECT
0053     Q_PROPERTY(KItemModelBase *model READ model WRITE setModel NOTIFY modelChanged)
0054     Q_PROPERTY(KItemListView *view READ view WRITE setView NOTIFY viewChanged)
0055 
0056 public:
0057     enum SelectionBehavior { NoSelection, SingleSelection, MultiSelection };
0058     Q_ENUM(SelectionBehavior)
0059 
0060     enum AutoActivationBehavior { ActivationAndExpansion, ExpansionOnly };
0061 
0062     enum MouseDoubleClickAction { ActivateAndExpandItem, ActivateItemOnly };
0063 
0064     /**
0065      * @param model  Model of the controller. The ownership is passed to the controller.
0066      * @param view   View of the controller. The ownership is passed to the controller.
0067      * @param parent Optional parent object.
0068      */
0069     KItemListController(KItemModelBase *model, KItemListView *view, QObject *parent = nullptr);
0070     ~KItemListController() override;
0071 
0072     void setModel(KItemModelBase *model);
0073     KItemModelBase *model() const;
0074 
0075     void setView(KItemListView *view);
0076     KItemListView *view() const;
0077 
0078     KItemListSelectionManager *selectionManager() const;
0079 
0080     void setSelectionBehavior(SelectionBehavior behavior);
0081     SelectionBehavior selectionBehavior() const;
0082 
0083     void setAutoActivationBehavior(AutoActivationBehavior behavior);
0084     AutoActivationBehavior autoActivationBehavior() const;
0085 
0086     void setMouseDoubleClickAction(MouseDoubleClickAction action);
0087     MouseDoubleClickAction mouseDoubleClickAction() const;
0088 
0089     int indexCloseToMousePressedPosition() const;
0090 
0091     /**
0092      * Sets the delay in milliseconds when dragging an object above an item
0093      * until the item gets activated automatically. A value of -1 indicates
0094      * that no automatic activation will be done at all (= default value).
0095      *
0096      * The hovered item must support dropping (see KItemModelBase::supportsDropping()),
0097      * otherwise the automatic activation is not available.
0098      *
0099      * After activating the item the signal itemActivated() will be
0100      * emitted. If the view supports the expanding of items
0101      * (KItemListView::supportsItemExpanding() returns true) and the item
0102      * itself is expandable (see KItemModelBase::isExpandable()) then instead
0103      * of activating the item it gets expanded instead (see
0104      * KItemModelBase::setExpanded()).
0105      */
0106     void setAutoActivationDelay(int delay);
0107     int autoActivationDelay() const;
0108 
0109     /**
0110      * If set to true, the signals itemActivated() and itemsActivated() are emitted
0111      * after a single-click of the left mouse button. If set to false (the default),
0112      * the setting from style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) is used.
0113      */
0114     void setSingleClickActivationEnforced(bool singleClick);
0115     bool singleClickActivationEnforced() const;
0116 
0117     /**
0118      * Setting the selection mode to enabled will make selecting and deselecting easier by acting
0119      * kind of similar to when the Control Key is held down.
0120      */
0121     void setSelectionModeEnabled(bool enabled);
0122     bool selectionMode() const;
0123 
0124     /**
0125      * @return \c true if search as you type is active, or \c false otherwise.
0126      */
0127     bool isSearchAsYouTypeActive() const;
0128 
0129     bool processEvent(QEvent *event, const QTransform &transform);
0130 
0131 Q_SIGNALS:
0132     /**
0133      * Is emitted if exactly one item has been activated by e.g. a mouse-click
0134      * or by pressing Return/Enter.
0135      */
0136     void itemActivated(int index);
0137 
0138     /**
0139      * Is emitted if more than one item has been activated by pressing Return/Enter
0140      * when having a selection.
0141      */
0142     void itemsActivated(const KItemSet &indexes);
0143 
0144     void itemMiddleClicked(int index);
0145 
0146     /**
0147      * Emitted if a context-menu is requested for the item with
0148      * the index \a index. It is assured that the index is valid.
0149      */
0150     void itemContextMenuRequested(int index, const QPointF &pos);
0151 
0152     /**
0153      * Emitted if a context-menu is requested for the KItemListView.
0154      */
0155     void viewContextMenuRequested(const QPointF &pos);
0156 
0157     /**
0158      * Emitted if a context-menu is requested for the header of the KItemListView.
0159      */
0160     void headerContextMenuRequested(const QPointF &pos);
0161 
0162     /**
0163      * Is emitted if the item with the index \p index gets hovered.
0164      */
0165     void itemHovered(int index);
0166 
0167     /**
0168      * Is emitted if the item with the index \p index gets unhovered.
0169      * It is assured that the signal itemHovered() for this index
0170      * has been emitted before.
0171      */
0172     void itemUnhovered(int index);
0173 
0174     /**
0175      * Is emitted if a mouse-button has been pressed above an item.
0176      * If the index is smaller than 0, the mouse-button has been pressed
0177      * above the viewport.
0178      */
0179     void mouseButtonPressed(int itemIndex, Qt::MouseButtons buttons);
0180 
0181     /**
0182      * Is emitted if a mouse-button has been released above an item.
0183      * It is assured that the signal mouseButtonPressed() has been emitted before.
0184      * If the index is smaller than 0, the mouse-button has been pressed
0185      * above the viewport.
0186      */
0187     void mouseButtonReleased(int itemIndex, Qt::MouseButtons buttons);
0188 
0189     void itemExpansionToggleClicked(int index);
0190 
0191     /**
0192      * Is emitted if a drop event is done above the item with the index
0193      * \a index. If \a index is < 0 the drop event is done above an
0194      * empty area of the view.
0195      * TODO: Introduce a new signal viewDropEvent(QGraphicsSceneDragDropEvent),
0196      *       which is emitted if the drop event occurs on an empty area in
0197      *       the view, and make sure that index is always >= 0 in itemDropEvent().
0198      */
0199     void itemDropEvent(int index, QGraphicsSceneDragDropEvent *event);
0200 
0201     /**
0202      * Is emitted if a drop event is done between the item with the index
0203      * \a index and the previous item.
0204      */
0205     void aboveItemDropEvent(int index, QGraphicsSceneDragDropEvent *event);
0206 
0207     /**
0208      * Is emitted if the Escape key is pressed.
0209      */
0210     void escapePressed();
0211 
0212     /**
0213      * Used to request either entering or leaving of selection mode
0214      * Leaving is requested by pressing Escape when no item is selected.
0215      *
0216      * Entering is requested if left click is pressed down for a long time without moving the cursor too much.
0217      * Moving the cursor would either trigger an item drag if the click was initiated on top of an item
0218      * or a selection rectangle if the click was not initiated on top of an item.
0219      * So long press is only emitted if there wasn't a lot of cursor movement.
0220      */
0221     void selectionModeChangeRequested(bool enabled);
0222 
0223     void modelChanged(KItemModelBase *current, KItemModelBase *previous);
0224     void viewChanged(KItemListView *current, KItemListView *previous);
0225 
0226     void selectedItemTextPressed(int index);
0227 
0228     void scrollerStop();
0229     void increaseZoom();
0230     void decreaseZoom();
0231     void swipeUp();
0232 
0233 public Q_SLOTS:
0234     void slotStateChanged(QScroller::State newState);
0235 
0236 private Q_SLOTS:
0237     void slotViewScrollOffsetChanged(qreal current, qreal previous);
0238 
0239     /**
0240      * Is invoked when the rubberband boundaries have been changed and will select
0241      * all items that are touched by the rubberband.
0242      */
0243     void slotRubberBandChanged();
0244 
0245     void slotChangeCurrentItem(const QString &text, bool searchFromNextItem);
0246 
0247     void slotAutoActivationTimeout();
0248 
0249 private:
0250     /**
0251      * Creates a QDrag object and initiates a drag-operation.
0252      */
0253     void startDragging();
0254 
0255     /**
0256      * @return Widget that is currently in the hovered state. 0 is returned
0257      *         if no widget is marked as hovered.
0258      */
0259     KItemListWidget *hoveredWidget() const;
0260 
0261     /**
0262      * @return Widget that is below the position \a pos. 0 is returned
0263      *         if no widget is below the position.
0264      */
0265     KItemListWidget *widgetForPos(const QPointF &pos) const;
0266 
0267     /**
0268      * @return Widget that should receive a drop event if an item is dropped at \a pos. 0 is returned
0269      *         if no widget should receive a drop event at the position.
0270      *
0271      * While widgetForPos() returns a widget if \a pos is anywhere inside the hover highlight area of the widget,
0272      * widgetForDropPos() only returns a widget if \a pos is directly above the widget (widget->contains(pos) == true).
0273      */
0274     KItemListWidget *widgetForDropPos(const QPointF &pos) const;
0275 
0276     /**
0277      * Updates m_keyboardAnchorIndex and m_keyboardAnchorPos. If no anchor is
0278      * set, it will be adjusted to the current item. If it is set it will be
0279      * checked whether it is still valid, otherwise it will be reset to the
0280      * current item.
0281      */
0282     void updateKeyboardAnchor();
0283 
0284     /**
0285      * @return Index for the next row based on \a index.
0286      *         If there is no next row \a index will be returned.
0287      */
0288     int nextRowIndex(int index) const;
0289 
0290     /**
0291      * @return Index for the previous row based on  \a index.
0292      *         If there is no previous row \a index will be returned.
0293      */
0294     int previousRowIndex(int index) const;
0295 
0296     /**
0297      * Helper method for updateKeyboardAnchor(), previousRowIndex() and nextRowIndex().
0298      * @return The position of the keyboard anchor for the item with the index \a index.
0299      *         If a horizontal scrolling is used the y-position of the item will be returned,
0300      *         for the vertical scrolling the x-position will be returned.
0301      */
0302     qreal keyboardAnchorPos(int index) const;
0303 
0304     /**
0305      * Dependent on the selection-behavior the extendedSelectionRegion-property
0306      * of the KItemListStyleOption from the view should be adjusted: If no
0307      * rubberband selection is used the property should be enabled.
0308      */
0309     void updateExtendedSelectionRegion();
0310 
0311     bool keyPressEvent(QKeyEvent *event);
0312     bool inputMethodEvent(QInputMethodEvent *event);
0313     bool mousePressEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
0314     bool mouseMoveEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
0315     bool mouseReleaseEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
0316     bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
0317     bool contextMenuEvent(QContextMenuEvent *event);
0318     bool dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
0319     bool dragLeaveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
0320     bool dragMoveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
0321     bool dropEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
0322     bool hoverEnterEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
0323     bool hoverMoveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
0324     bool hoverLeaveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
0325     bool wheelEvent(QGraphicsSceneWheelEvent *event, const QTransform &transform);
0326     bool resizeEvent(QGraphicsSceneResizeEvent *event, const QTransform &transform);
0327     bool gestureEvent(QGestureEvent *event, const QTransform &transform);
0328     bool touchBeginEvent(QTouchEvent *event, const QTransform &transform);
0329     void tapTriggered(QTapGesture *tap, const QTransform &transform);
0330     void tapAndHoldTriggered(QGestureEvent *event, const QTransform &transform);
0331     void pinchTriggered(QGestureEvent *event, const QTransform &transform);
0332     void swipeTriggered(QGestureEvent *event, const QTransform &transform);
0333     void twoFingerTapTriggered(QGestureEvent *event, const QTransform &transform);
0334     bool onPress(const QPoint &screenPos, const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons);
0335     bool onRelease(const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons, bool touch);
0336     void startRubberBand();
0337 
0338 private:
0339     bool m_singleClickActivationEnforced;
0340     bool m_selectionMode;
0341     bool m_selectionTogglePressed;
0342     bool m_clearSelectionIfItemsAreNotDragged;
0343     bool m_isSwipeGesture;
0344     bool m_dragActionOrRightClick;
0345     bool m_scrollerIsScrolling;
0346     bool m_pinchGestureInProgress;
0347     bool m_mousePress;
0348     bool m_isTouchEvent;
0349     SelectionBehavior m_selectionBehavior;
0350     AutoActivationBehavior m_autoActivationBehavior;
0351     MouseDoubleClickAction m_mouseDoubleClickAction;
0352     KItemModelBase *m_model;
0353     KItemListView *m_view;
0354     KItemListSelectionManager *m_selectionManager;
0355     KItemListKeyboardSearchManager *m_keyboardManager;
0356     std::optional<int> m_pressedIndex;
0357     QPointF m_pressedMouseGlobalPos;
0358 
0359     QTimer *m_autoActivationTimer;
0360 
0361     Qt::GestureType m_swipeGesture;
0362     Qt::GestureType m_twoFingerTapGesture;
0363 
0364     /**
0365      * When starting a rubberband selection during a Shift- or Control-key has been
0366      * pressed the current selection should never be deleted. To be able to restore
0367      * the current selection it is remembered in m_oldSelection before the
0368      * rubberband gets activated.
0369      */
0370     KItemSet m_oldSelection;
0371 
0372     /**
0373      * Assuming a view is given with a vertical scroll-orientation, grouped items and
0374      * a maximum of 4 columns:
0375      *
0376      *  1  2  3  4
0377      *  5  6  7
0378      *  8  9 10 11
0379      * 12 13 14
0380      *
0381      * If the current index is on 4 and key-down is pressed, then item 7 gets the current
0382      * item. Now when pressing key-down again item 11 should get the current item and not
0383      * item 10. This makes it necessary to keep track of the requested column to have a
0384      * similar behavior like in a text editor:
0385      */
0386     int m_keyboardAnchorIndex;
0387     qreal m_keyboardAnchorPos;
0388 };
0389 
0390 #endif