1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QABSTRACTITEMVIEW_P_H
41 #define QABSTRACTITEMVIEW_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists purely as an
48 // implementation detail.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtWidgets/private/qtwidgetsglobal_p.h>
55 #include "private/qabstractscrollarea_p.h"
56 #include "private/qabstractitemmodel_p.h"
57 #include "QtWidgets/qapplication.h"
58 #include "QtGui/qevent.h"
59 #include "QtCore/qmimedata.h"
60 #include "QtGui/qpainter.h"
61 #include "QtCore/qpair.h"
62 #include "QtGui/qregion.h"
63 #include "QtCore/qdebug.h"
64 #include "QtCore/qbasictimer.h"
65 #include "QtCore/qelapsedtimer.h"
66 
67 QT_REQUIRE_CONFIG(itemviews);
68 
69 QT_BEGIN_NAMESPACE
70 
71 struct QEditorInfo {
QEditorInfoQEditorInfo72     QEditorInfo(QWidget *e, bool s): widget(QPointer<QWidget>(e)), isStatic(s) {}
QEditorInfoQEditorInfo73     QEditorInfo(): isStatic(false) {}
74 
75     QPointer<QWidget> widget;
76     bool isStatic;
77 };
78 
79 //  Fast associativity between Persistent editors and indices.
80 typedef QHash<QWidget *, QPersistentModelIndex> QEditorIndexHash;
81 typedef QHash<QPersistentModelIndex, QEditorInfo> QIndexEditorHash;
82 
83 struct QItemViewPaintPair {
84     QRect rect;
85     QModelIndex index;
86 };
87 template <>
88 class QTypeInfo<QItemViewPaintPair> : public QTypeInfoMerger<QItemViewPaintPair, QRect, QModelIndex> {};
89 
90 typedef QVector<QItemViewPaintPair> QItemViewPaintPairs;
91 
92 class Q_AUTOTEST_EXPORT QAbstractItemViewPrivate : public QAbstractScrollAreaPrivate
93 {
94     Q_DECLARE_PUBLIC(QAbstractItemView)
95 
96 public:
97     QAbstractItemViewPrivate();
98     virtual ~QAbstractItemViewPrivate();
99 
100     void init();
101 
102     virtual void _q_rowsRemoved(const QModelIndex &parent, int start, int end);
103     virtual void _q_rowsInserted(const QModelIndex &parent, int start, int end);
104     virtual void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
105     virtual void _q_columnsRemoved(const QModelIndex &parent, int start, int end);
106     virtual void _q_columnsInserted(const QModelIndex &parent, int start, int end);
107     virtual void _q_modelDestroyed();
108     virtual void _q_layoutChanged();
109     virtual void _q_rowsMoved(const QModelIndex &source, int sourceStart, int sourceEnd, const QModelIndex &destination, int destinationStart);
110     virtual void _q_columnsMoved(const QModelIndex &source, int sourceStart, int sourceEnd, const QModelIndex &destination, int destinationStart);
111 
_q_headerDataChanged()112     void _q_headerDataChanged() { doDelayedItemsLayout(); }
113     void _q_scrollerStateChanged();
114 
115     void fetchMore();
116 
117     bool shouldEdit(QAbstractItemView::EditTrigger trigger, const QModelIndex &index) const;
118     bool shouldForwardEvent(QAbstractItemView::EditTrigger trigger, const QEvent *event) const;
119     bool shouldAutoScroll(const QPoint &pos) const;
120     void doDelayedItemsLayout(int delay = 0);
121     void interruptDelayedItemsLayout() const;
122 
123     void updateGeometry();
124 
startAutoScroll()125     void startAutoScroll()
126     {   // ### it would be nice to make this into a style hint one day
127         int scrollInterval = (verticalScrollMode == QAbstractItemView::ScrollPerItem) ? 150 : 50;
128         autoScrollTimer.start(scrollInterval, q_func());
129         autoScrollCount = 0;
130     }
stopAutoScroll()131     void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;}
132 
133 #if QT_CONFIG(draganddrop)
134     virtual bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index);
135 #endif
136     bool droppingOnItself(QDropEvent *event, const QModelIndex &index);
137 
138     QWidget *editor(const QModelIndex &index, const QStyleOptionViewItem &options);
139     bool sendDelegateEvent(const QModelIndex &index, QEvent *event) const;
140     bool openEditor(const QModelIndex &index, QEvent *event);
141     void updateEditorData(const QModelIndex &topLeft, const QModelIndex &bottomRight);
142 
143     QItemSelectionModel::SelectionFlags multiSelectionCommand(const QModelIndex &index,
144                                                               const QEvent *event) const;
145     QItemSelectionModel::SelectionFlags extendedSelectionCommand(const QModelIndex &index,
146                                                                  const QEvent *event) const;
147     QItemSelectionModel::SelectionFlags contiguousSelectionCommand(const QModelIndex &index,
148                                                                    const QEvent *event) const;
149     virtual void selectAll(QItemSelectionModel::SelectionFlags command);
150 
151     void setHoverIndex(const QPersistentModelIndex &index);
152 
153     void checkMouseMove(const QPersistentModelIndex &index);
checkMouseMove(const QPoint & pos)154     inline void checkMouseMove(const QPoint &pos) { checkMouseMove(q_func()->indexAt(pos)); }
155 
selectionBehaviorFlags()156     inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const
157     {
158         switch (selectionBehavior) {
159         case QAbstractItemView::SelectRows: return QItemSelectionModel::Rows;
160         case QAbstractItemView::SelectColumns: return QItemSelectionModel::Columns;
161         case QAbstractItemView::SelectItems: default: return QItemSelectionModel::NoUpdate;
162         }
163     }
164 
165 #if QT_CONFIG(draganddrop)
166     virtual QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const;
167 
canDrop(QDropEvent * event)168     inline bool canDrop(QDropEvent *event) {
169         const QMimeData *mime = event->mimeData();
170 
171         // Drag enter event shall always be accepted, if mime type and action match.
172         // Whether the data can actually be dropped will be checked in drag move.
173         if (event->type() == QEvent::DragEnter && (event->dropAction() & model->supportedDropActions())) {
174             const QStringList modelTypes = model->mimeTypes();
175             for (const auto &modelType : modelTypes) {
176                 if (mime->hasFormat(modelType))
177                     return true;
178             }
179         }
180 
181         QModelIndex index;
182         int col = -1;
183         int row = -1;
184         if (dropOn(event, &row, &col, &index)) {
185             return model->canDropMimeData(mime,
186                                           dragDropMode == QAbstractItemView::InternalMove ? Qt::MoveAction : event->dropAction(),
187                                           row, col, index);
188         }
189         return false;
190     }
191 
paintDropIndicator(QPainter * painter)192     inline void paintDropIndicator(QPainter *painter)
193     {
194         if (showDropIndicator && state == QAbstractItemView::DraggingState
195 #ifndef QT_NO_CURSOR
196             && viewport->cursor().shape() != Qt::ForbiddenCursor
197 #endif
198             ) {
199             QStyleOption opt;
200             opt.init(q_func());
201             opt.rect = dropIndicatorRect;
202             q_func()->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, painter, q_func());
203         }
204     }
205 
206 #endif
207     virtual QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const;
208     // reimplemented in subclasses
adjustViewOptionsForIndex(QStyleOptionViewItem *,const QModelIndex &)209     virtual void adjustViewOptionsForIndex(QStyleOptionViewItem*, const QModelIndex&) const {}
210 
211     inline void releaseEditor(QWidget *editor, const QModelIndex &index = QModelIndex()) const {
212         if (editor) {
213             QObject::disconnect(editor, SIGNAL(destroyed(QObject*)),
214                                 q_func(), SLOT(editorDestroyed(QObject*)));
215             editor->removeEventFilter(itemDelegate);
216             editor->hide();
217             QAbstractItemDelegate *delegate = delegateForIndex(index);
218 
219             if (delegate)
220                 delegate->destroyEditor(editor, index);
221             else
222                 editor->deleteLater();
223         }
224     }
225 
executePostedLayout()226     inline void executePostedLayout() const {
227         if (delayedPendingLayout && state != QAbstractItemView::CollapsingState) {
228             interruptDelayedItemsLayout();
229             const_cast<QAbstractItemView*>(q_func())->doItemsLayout();
230         }
231     }
232 
setDirtyRegion(const QRegion & visualRegion)233     inline void setDirtyRegion(const QRegion &visualRegion) {
234         updateRegion += visualRegion;
235         if (!updateTimer.isActive())
236             updateTimer.start(0, q_func());
237     }
238 
scrollDirtyRegion(int dx,int dy)239     inline void scrollDirtyRegion(int dx, int dy) {
240         scrollDelayOffset = QPoint(-dx, -dy);
241         updateDirtyRegion();
242         scrollDelayOffset = QPoint(0, 0);
243     }
244 
scrollContentsBy(int dx,int dy)245     inline void scrollContentsBy(int dx, int dy) {
246         scrollDirtyRegion(dx, dy);
247         viewport->scroll(dx, dy);
248     }
249 
updateDirtyRegion()250     void updateDirtyRegion() {
251         updateTimer.stop();
252         viewport->update(updateRegion);
253         updateRegion = QRegion();
254     }
255 
256     void clearOrRemove();
257     void checkPersistentEditorFocus();
258 
259     QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const;
260 
offset()261     inline QPoint offset() const {
262         const Q_Q(QAbstractItemView);
263         return QPoint(q->isRightToLeft() ? -q->horizontalOffset()
264                       : q->horizontalOffset(), q->verticalOffset());
265     }
266 
267     const QEditorInfo &editorForIndex(const QModelIndex &index) const;
268     bool hasEditor(const QModelIndex &index) const;
269 
270     QModelIndex indexForEditor(QWidget *editor) const;
271     void addEditor(const QModelIndex &index, QWidget *editor, bool isStatic);
272     void removeEditor(QWidget *editor);
273 
isAnimating()274     inline bool isAnimating() const {
275         return state == QAbstractItemView::AnimatingState;
276     }
277 
delegateForIndex(const QModelIndex & index)278     inline QAbstractItemDelegate *delegateForIndex(const QModelIndex &index) const {
279         QMap<int, QPointer<QAbstractItemDelegate> >::ConstIterator it;
280 
281         it = rowDelegates.find(index.row());
282         if (it != rowDelegates.end())
283             return it.value();
284 
285         it = columnDelegates.find(index.column());
286         if (it != columnDelegates.end())
287             return it.value();
288 
289         return itemDelegate;
290     }
291 
isIndexValid(const QModelIndex & index)292     inline bool isIndexValid(const QModelIndex &index) const {
293          return (index.row() >= 0) && (index.column() >= 0) && (index.model() == model);
294     }
isIndexSelectable(const QModelIndex & index)295     inline bool isIndexSelectable(const QModelIndex &index) const {
296         return (model->flags(index) & Qt::ItemIsSelectable);
297     }
isIndexEnabled(const QModelIndex & index)298     inline bool isIndexEnabled(const QModelIndex &index) const {
299         return (model->flags(index) & Qt::ItemIsEnabled);
300     }
isIndexDropEnabled(const QModelIndex & index)301     inline bool isIndexDropEnabled(const QModelIndex &index) const {
302         return (model->flags(index) & Qt::ItemIsDropEnabled);
303     }
isIndexDragEnabled(const QModelIndex & index)304     inline bool isIndexDragEnabled(const QModelIndex &index) const {
305         return (model->flags(index) & Qt::ItemIsDragEnabled);
306     }
307 
selectionAllowed(const QModelIndex & index)308     virtual bool selectionAllowed(const QModelIndex &index) const {
309         // in some views we want to go ahead with selections, even if the index is invalid
310         return isIndexValid(index) && isIndexSelectable(index);
311     }
312 
313     // reimplemented from QAbstractScrollAreaPrivate
contentsOffset()314     QPoint contentsOffset() const override {
315         Q_Q(const QAbstractItemView);
316         return QPoint(q->horizontalOffset(), q->verticalOffset());
317     }
318 
319     /**
320      * For now, assume that we have few editors, if we need a more efficient implementation
321      * we should add a QMap<QAbstractItemDelegate*, int> member.
322      */
delegateRefCount(const QAbstractItemDelegate * delegate)323     int delegateRefCount(const QAbstractItemDelegate *delegate) const
324     {
325         int ref = 0;
326         if (itemDelegate == delegate)
327             ++ref;
328 
329         for (int maps = 0; maps < 2; ++maps) {
330             const QMap<int, QPointer<QAbstractItemDelegate> > *delegates = maps ? &columnDelegates : &rowDelegates;
331             for (QMap<int, QPointer<QAbstractItemDelegate> >::const_iterator it = delegates->begin();
332                 it != delegates->end(); ++it) {
333                     if (it.value() == delegate) {
334                         ++ref;
335                         // optimization, we are only interested in the ref count values 0, 1 or >=2
336                         if (ref >= 2) {
337                             return ref;
338                         }
339                     }
340             }
341         }
342         return ref;
343     }
344 
345     /**
346      * return true if the index is registered as a QPersistentModelIndex
347      */
isPersistent(const QModelIndex & index)348     inline bool isPersistent(const QModelIndex &index) const
349     {
350         return static_cast<QAbstractItemModelPrivate *>(model->d_ptr.data())->persistent.indexes.contains(index);
351     }
352 
353     QModelIndexList selectedDraggableIndexes() const;
354 
355     QStyleOptionViewItem viewOptionsV1() const;
356 
doDelayedReset()357     void doDelayedReset()
358     {
359         //we delay the reset of the timer because some views (QTableView)
360         //with headers can't handle the fact that the model has been destroyed
361         //all _q_modelDestroyed slots must have been called
362         if (!delayedReset.isActive())
363             delayedReset.start(0, q_func());
364     }
365 
366     QAbstractItemModel *model;
367     QPointer<QAbstractItemDelegate> itemDelegate;
368     QMap<int, QPointer<QAbstractItemDelegate> > rowDelegates;
369     QMap<int, QPointer<QAbstractItemDelegate> > columnDelegates;
370     QPointer<QItemSelectionModel> selectionModel;
371     QItemSelectionModel::SelectionFlag ctrlDragSelectionFlag;
372     bool noSelectionOnMousePress;
373 
374     QAbstractItemView::SelectionMode selectionMode;
375     QAbstractItemView::SelectionBehavior selectionBehavior;
376 
377     QEditorIndexHash editorIndexHash;
378     QIndexEditorHash indexEditorHash;
379     QSet<QWidget*> persistent;
380     QWidget *currentlyCommittingEditor;
381 
382     QPersistentModelIndex enteredIndex;
383     QPersistentModelIndex pressedIndex;
384     QPersistentModelIndex currentSelectionStartIndex;
385     Qt::KeyboardModifiers pressedModifiers;
386     QPoint pressedPosition;
387     bool pressedAlreadySelected;
388 
389     //forces the next mouseMoveEvent to send the viewportEntered signal
390     //if the mouse is over the viewport and not over an item
391     bool viewportEnteredNeeded;
392 
393     QAbstractItemView::State state;
394     QAbstractItemView::State stateBeforeAnimation;
395     QAbstractItemView::EditTriggers editTriggers;
396     QAbstractItemView::EditTrigger lastTrigger;
397 
398     QPersistentModelIndex root;
399     QPersistentModelIndex hover;
400 
401     bool tabKeyNavigation;
402 
403 #if QT_CONFIG(draganddrop)
404     bool showDropIndicator;
405     QRect dropIndicatorRect;
406     bool dragEnabled;
407     QAbstractItemView::DragDropMode dragDropMode;
408     bool overwrite;
409     bool dropEventMoved;
410     QAbstractItemView::DropIndicatorPosition dropIndicatorPosition;
411     Qt::DropAction defaultDropAction;
412 #endif
413 
414     QString keyboardInput;
415     QElapsedTimer keyboardInputTime;
416 
417     bool autoScroll;
418     QBasicTimer autoScrollTimer;
419     int autoScrollMargin;
420     int autoScrollCount;
421     bool shouldScrollToCurrentOnShow; //used to know if we should scroll to current on show event
422     bool shouldClearStatusTip; //if there is a statustip currently shown that need to be cleared when leaving.
423 
424     bool alternatingColors;
425 
426     QSize iconSize;
427     Qt::TextElideMode textElideMode;
428 
429     QRegion updateRegion; // used for the internal update system
430     QPoint scrollDelayOffset;
431 
432     QBasicTimer updateTimer;
433     QBasicTimer delayedEditing;
434     QBasicTimer delayedAutoScroll; //used when an item is clicked
435     QBasicTimer delayedReset;
436 
437     QAbstractItemView::ScrollMode verticalScrollMode;
438     QAbstractItemView::ScrollMode horizontalScrollMode;
439 
440 #ifndef QT_NO_GESTURES
441     // the selection before the last mouse down. In case we have to restore it for scrolling
442     QItemSelection oldSelection;
443     QModelIndex oldCurrent;
444 #endif
445 
446     bool currentIndexSet;
447 
448     bool wrapItemText;
449     mutable bool delayedPendingLayout;
450     bool moveCursorUpdatedView;
451 
452     // Whether scroll mode has been explicitly set or its value come from SH_ItemView_ScrollMode
453     bool verticalScrollModeSet;
454     bool horizontalScrollModeSet;
455 
456 private:
457     mutable QBasicTimer delayedLayout;
458     mutable QBasicTimer fetchMoreTimer;
459 };
460 
461 QT_BEGIN_INCLUDE_NAMESPACE
462 #include <qvector.h>
463 QT_END_INCLUDE_NAMESPACE
464 
465 template <typename T>
qBinarySearch(const QVector<T> & vec,const T & item,int start,int end)466 inline int qBinarySearch(const QVector<T> &vec, const T &item, int start, int end)
467 {
468     int i = (start + end + 1) >> 1;
469     while (end - start > 0) {
470         if (vec.at(i) > item)
471             end = i - 1;
472         else
473             start = i;
474         i = (start + end + 1) >> 1;
475     }
476     return i;
477 }
478 
479 QT_END_NAMESPACE
480 
481 #endif // QABSTRACTITEMVIEW_P_H
482