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
67QT_REQUIRE_CONFIG(itemviews);
68
69QT_BEGIN_NAMESPACE
70
71struct QEditorInfo {
72 QEditorInfo(QWidget *e, bool s): widget(QPointer<QWidget>(e)), isStatic(s) {}
73 QEditorInfo(): isStatic(false) {}
74
75 QPointer<QWidget> widget;
76 bool isStatic;
77};
78
79// Fast associativity between Persistent editors and indices.
80typedef QHash<QWidget *, QPersistentModelIndex> QEditorIndexHash;
81typedef QHash<QPersistentModelIndex, QEditorInfo> QIndexEditorHash;
82
83struct QItemViewPaintPair {
84 QRect rect;
85 QModelIndex index;
86};
87template <>
88class QTypeInfo<QItemViewPaintPair> : public QTypeInfoMerger<QItemViewPaintPair, QRect, QModelIndex> {};
89
90typedef QList<QItemViewPaintPair> QItemViewPaintPairs;
91
92class Q_AUTOTEST_EXPORT QAbstractItemViewPrivate : public QAbstractScrollAreaPrivate
93{
94 Q_DECLARE_PUBLIC(QAbstractItemView)
95
96public:
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 virtual QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const;
112
113 void _q_headerDataChanged() { doDelayedItemsLayout(); }
114 void _q_scrollerStateChanged();
115
116 void fetchMore();
117
118 bool shouldEdit(QAbstractItemView::EditTrigger trigger, const QModelIndex &index) const;
119 bool shouldForwardEvent(QAbstractItemView::EditTrigger trigger, const QEvent *event) const;
120 bool shouldAutoScroll(const QPoint &pos) const;
121 void doDelayedItemsLayout(int delay = 0);
122 void interruptDelayedItemsLayout() const;
123
124 void updateGeometry();
125
126 void startAutoScroll()
127 { // ### it would be nice to make this into a style hint one day
128 int scrollInterval = (verticalScrollMode == QAbstractItemView::ScrollPerItem) ? 150 : 50;
129 autoScrollTimer.start(scrollInterval, q_func());
130 autoScrollCount = 0;
131 }
132 void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;}
133
134#if QT_CONFIG(draganddrop)
135 virtual bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index);
136#endif
137 bool droppingOnItself(QDropEvent *event, const QModelIndex &index);
138
139 QWidget *editor(const QModelIndex &index, const QStyleOptionViewItem &options);
140 bool sendDelegateEvent(const QModelIndex &index, QEvent *event) const;
141 bool openEditor(const QModelIndex &index, QEvent *event);
142 void updateEditorData(const QModelIndex &topLeft, const QModelIndex &bottomRight);
143
144 QItemSelectionModel::SelectionFlags multiSelectionCommand(const QModelIndex &index,
145 const QEvent *event) const;
146 QItemSelectionModel::SelectionFlags extendedSelectionCommand(const QModelIndex &index,
147 const QEvent *event) const;
148 QItemSelectionModel::SelectionFlags contiguousSelectionCommand(const QModelIndex &index,
149 const QEvent *event) const;
150 virtual void selectAll(QItemSelectionModel::SelectionFlags command);
151
152 void setHoverIndex(const QPersistentModelIndex &index);
153
154 void checkMouseMove(const QPersistentModelIndex &index);
155 inline void checkMouseMove(const QPoint &pos) { checkMouseMove(q_func()->indexAt(pos)); }
156
157 inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const
158 {
159 switch (selectionBehavior) {
160 case QAbstractItemView::SelectRows: return QItemSelectionModel::Rows;
161 case QAbstractItemView::SelectColumns: return QItemSelectionModel::Columns;
162 case QAbstractItemView::SelectItems: default: return QItemSelectionModel::NoUpdate;
163 }
164 }
165
166#if QT_CONFIG(draganddrop)
167 virtual QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const;
168
169 inline bool canDrop(QDropEvent *event) {
170 const QMimeData *mime = event->mimeData();
171
172 // Drag enter event shall always be accepted, if mime type and action match.
173 // Whether the data can actually be dropped will be checked in drag move.
174 if (event->type() == QEvent::DragEnter && (event->dropAction() & model->supportedDropActions())) {
175 const QStringList modelTypes = model->mimeTypes();
176 for (const auto &modelType : modelTypes) {
177 if (mime->hasFormat(modelType))
178 return true;
179 }
180 }
181
182 QModelIndex index;
183 int col = -1;
184 int row = -1;
185 if (dropOn(event, &row, &col, &index)) {
186 return model->canDropMimeData(mime,
187 dragDropMode == QAbstractItemView::InternalMove ? Qt::MoveAction : event->dropAction(),
188 row, col, index);
189 }
190 return false;
191 }
192
193 inline void paintDropIndicator(QPainter *painter)
194 {
195 if (showDropIndicator && state == QAbstractItemView::DraggingState
196#ifndef QT_NO_CURSOR
197 && viewport->cursor().shape() != Qt::ForbiddenCursor
198#endif
199 ) {
200 QStyleOption opt;
201 opt.initFrom(q_func());
202 opt.rect = dropIndicatorRect;
203 q_func()->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, painter, q_func());
204 }
205 }
206
207#endif
208 virtual QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const;
209 // reimplemented in subclasses
210 virtual void adjustViewOptionsForIndex(QStyleOptionViewItem*, const QModelIndex&) const {}
211
212 inline void releaseEditor(QWidget *editor, const QModelIndex &index = QModelIndex()) const {
213 if (editor) {
214 Q_Q(const QAbstractItemView);
215 QObject::disconnect(editor, SIGNAL(destroyed(QObject*)),
216 q_func(), SLOT(editorDestroyed(QObject*)));
217 editor->removeEventFilter(itemDelegate);
218 editor->hide();
219 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
220
221 if (delegate)
222 delegate->destroyEditor(editor, index);
223 else
224 editor->deleteLater();
225 }
226 }
227
228 inline void executePostedLayout() const {
229 if (delayedPendingLayout && state != QAbstractItemView::CollapsingState) {
230 interruptDelayedItemsLayout();
231 const_cast<QAbstractItemView*>(q_func())->doItemsLayout();
232 }
233 }
234
235 inline void setDirtyRegion(const QRegion &visualRegion) {
236 updateRegion += visualRegion;
237 if (!updateTimer.isActive())
238 updateTimer.start(0, q_func());
239 }
240
241 inline void scrollDirtyRegion(int dx, int dy) {
242 scrollDelayOffset = QPoint(-dx, -dy);
243 updateDirtyRegion();
244 scrollDelayOffset = QPoint(0, 0);
245 }
246
247 inline void scrollContentsBy(int dx, int dy) {
248 scrollDirtyRegion(dx, dy);
249 viewport->scroll(dx, dy);
250 }
251
252 void updateDirtyRegion() {
253 updateTimer.stop();
254 viewport->update(updateRegion);
255 updateRegion = QRegion();
256 }
257
258 void clearOrRemove();
259 void checkPersistentEditorFocus();
260
261 QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const;
262
263 inline QPoint offset() const {
264 const Q_Q(QAbstractItemView);
265 return QPoint(q->isRightToLeft() ? -q->horizontalOffset()
266 : q->horizontalOffset(), q->verticalOffset());
267 }
268
269 const QEditorInfo &editorForIndex(const QModelIndex &index) const;
270 bool hasEditor(const QModelIndex &index) const;
271
272 QModelIndex indexForEditor(QWidget *editor) const;
273 void addEditor(const QModelIndex &index, QWidget *editor, bool isStatic);
274 void removeEditor(QWidget *editor);
275
276 inline bool isAnimating() const {
277 return state == QAbstractItemView::AnimatingState;
278 }
279
280 inline bool isIndexValid(const QModelIndex &index) const {
281 return (index.row() >= 0) && (index.column() >= 0) && (index.model() == model);
282 }
283 inline bool isIndexSelectable(const QModelIndex &index) const {
284 return (model->flags(index) & Qt::ItemIsSelectable);
285 }
286 inline bool isIndexEnabled(const QModelIndex &index) const {
287 return (model->flags(index) & Qt::ItemIsEnabled);
288 }
289 inline bool isIndexDropEnabled(const QModelIndex &index) const {
290 return (model->flags(index) & Qt::ItemIsDropEnabled);
291 }
292 inline bool isIndexDragEnabled(const QModelIndex &index) const {
293 return (model->flags(index) & Qt::ItemIsDragEnabled);
294 }
295
296 virtual bool selectionAllowed(const QModelIndex &index) const {
297 // in some views we want to go ahead with selections, even if the index is invalid
298 return isIndexValid(index) && isIndexSelectable(index);
299 }
300
301 // reimplemented from QAbstractScrollAreaPrivate
302 QPoint contentsOffset() const override {
303 Q_Q(const QAbstractItemView);
304 return QPoint(q->horizontalOffset(), q->verticalOffset());
305 }
306
307 /**
308 * For now, assume that we have few editors, if we need a more efficient implementation
309 * we should add a QMap<QAbstractItemDelegate*, int> member.
310 */
311 int delegateRefCount(const QAbstractItemDelegate *delegate) const
312 {
313 int ref = 0;
314 if (itemDelegate == delegate)
315 ++ref;
316
317 for (int maps = 0; maps < 2; ++maps) {
318 const QMap<int, QPointer<QAbstractItemDelegate> > *delegates = maps ? &columnDelegates : &rowDelegates;
319 for (QMap<int, QPointer<QAbstractItemDelegate> >::const_iterator it = delegates->begin();
320 it != delegates->end(); ++it) {
321 if (it.value() == delegate) {
322 ++ref;
323 // optimization, we are only interested in the ref count values 0, 1 or >=2
324 if (ref >= 2) {
325 return ref;
326 }
327 }
328 }
329 }
330 return ref;
331 }
332
333 /**
334 * return true if the index is registered as a QPersistentModelIndex
335 */
336 inline bool isPersistent(const QModelIndex &index) const
337 {
338 return static_cast<QAbstractItemModelPrivate *>(model->d_ptr.data())->persistent.indexes.contains(index);
339 }
340
341 QModelIndexList selectedDraggableIndexes() const;
342
343 void doDelayedReset()
344 {
345 //we delay the reset of the timer because some views (QTableView)
346 //with headers can't handle the fact that the model has been destroyed
347 //all _q_modelDestroyed slots must have been called
348 if (!delayedReset.isActive())
349 delayedReset.start(0, q_func());
350 }
351
352 QAbstractItemModel *model;
353 QPointer<QAbstractItemDelegate> itemDelegate;
354 QMap<int, QPointer<QAbstractItemDelegate> > rowDelegates;
355 QMap<int, QPointer<QAbstractItemDelegate> > columnDelegates;
356 QPointer<QItemSelectionModel> selectionModel;
357 QItemSelectionModel::SelectionFlag ctrlDragSelectionFlag;
358 bool noSelectionOnMousePress;
359
360 QAbstractItemView::SelectionMode selectionMode;
361 QAbstractItemView::SelectionBehavior selectionBehavior;
362
363 QEditorIndexHash editorIndexHash;
364 QIndexEditorHash indexEditorHash;
365 QSet<QWidget*> persistent;
366 QWidget *currentlyCommittingEditor;
367
368 QPersistentModelIndex enteredIndex;
369 QPersistentModelIndex pressedIndex;
370 QPersistentModelIndex currentSelectionStartIndex;
371 Qt::KeyboardModifiers pressedModifiers;
372 QPoint pressedPosition;
373 bool pressedAlreadySelected;
374
375 //forces the next mouseMoveEvent to send the viewportEntered signal
376 //if the mouse is over the viewport and not over an item
377 bool viewportEnteredNeeded;
378
379 QAbstractItemView::State state;
380 QAbstractItemView::State stateBeforeAnimation;
381 QAbstractItemView::EditTriggers editTriggers;
382 QAbstractItemView::EditTrigger lastTrigger;
383
384 QPersistentModelIndex root;
385 QPersistentModelIndex hover;
386
387 bool tabKeyNavigation;
388
389#if QT_CONFIG(draganddrop)
390 bool showDropIndicator;
391 QRect dropIndicatorRect;
392 bool dragEnabled;
393 QAbstractItemView::DragDropMode dragDropMode;
394 bool overwrite;
395 bool dropEventMoved;
396 QAbstractItemView::DropIndicatorPosition dropIndicatorPosition;
397 Qt::DropAction defaultDropAction;
398#endif
399
400 QString keyboardInput;
401 QElapsedTimer keyboardInputTime;
402
403 bool autoScroll;
404 QBasicTimer autoScrollTimer;
405 int autoScrollMargin;
406 int autoScrollCount;
407 bool shouldScrollToCurrentOnShow; //used to know if we should scroll to current on show event
408 bool shouldClearStatusTip; //if there is a statustip currently shown that need to be cleared when leaving.
409
410 bool alternatingColors;
411
412 QSize iconSize;
413 Qt::TextElideMode textElideMode;
414
415 QRegion updateRegion; // used for the internal update system
416 QPoint scrollDelayOffset;
417
418 QBasicTimer updateTimer;
419 QBasicTimer delayedEditing;
420 QBasicTimer delayedAutoScroll; //used when an item is clicked
421 QBasicTimer delayedReset;
422
423 QAbstractItemView::ScrollMode verticalScrollMode;
424 QAbstractItemView::ScrollMode horizontalScrollMode;
425
426#ifndef QT_NO_GESTURES
427 // the selection before the last mouse down. In case we have to restore it for scrolling
428 QItemSelection oldSelection;
429 QModelIndex oldCurrent;
430#endif
431
432 bool currentIndexSet;
433
434 bool wrapItemText;
435 mutable bool delayedPendingLayout;
436 bool moveCursorUpdatedView;
437
438 // Whether scroll mode has been explicitly set or its value come from SH_ItemView_ScrollMode
439 bool verticalScrollModeSet;
440 bool horizontalScrollModeSet;
441
442private:
443 inline QAbstractItemDelegate *delegateForIndex(const QModelIndex &index) const {
444 QMap<int, QPointer<QAbstractItemDelegate> >::ConstIterator it;
445
446 it = rowDelegates.find(index.row());
447 if (it != rowDelegates.end())
448 return it.value();
449
450 it = columnDelegates.find(index.column());
451 if (it != columnDelegates.end())
452 return it.value();
453
454 return itemDelegate;
455 }
456
457 mutable QBasicTimer delayedLayout;
458 mutable QBasicTimer fetchMoreTimer;
459};
460
461QT_BEGIN_INCLUDE_NAMESPACE
462#include <qlist.h>
463QT_END_INCLUDE_NAMESPACE
464
465template<typename T>
466inline int qBinarySearch(const QList<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
479QT_END_NAMESPACE
480
481#endif // QABSTRACTITEMVIEW_P_H
482