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