Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qabstractitemview.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
6
7#include <qpointer.h>
8#include <qapplication.h>
9#include <qclipboard.h>
10#include <qpainter.h>
11#include <qstyle.h>
12#if QT_CONFIG(draganddrop)
13#include <qdrag.h>
14#endif
15#include <qevent.h>
16#include <qscrollbar.h>
17#if QT_CONFIG(tooltip)
18#include <qtooltip.h>
19#endif
20#include <qdatetime.h>
21#if QT_CONFIG(lineedit)
22#include <qlineedit.h>
23#endif
24#if QT_CONFIG(spinbox)
25#include <qspinbox.h>
26#endif
27#include <qheaderview.h>
28#include <qstyleditemdelegate.h>
29#include <private/qabstractitemview_p.h>
30#include <private/qabstractitemmodel_p.h>
31#include <private/qapplication_p.h>
32#include <private/qguiapplication_p.h>
33#include <private/qscrollbar_p.h>
34#if QT_CONFIG(accessibility)
35#include <qaccessible.h>
36#endif
37#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
38# include <qscroller.h>
39#endif
40
41#include <algorithm>
42
43QT_BEGIN_NAMESPACE
44
45QAbstractItemViewPrivate::QAbstractItemViewPrivate()
46 : model(QAbstractItemModelPrivate::staticEmptyModel()),
47 itemDelegate(nullptr),
48 selectionModel(nullptr),
49 ctrlDragSelectionFlag(QItemSelectionModel::NoUpdate),
50 noSelectionOnMousePress(false),
51 selectionMode(QAbstractItemView::ExtendedSelection),
52 selectionBehavior(QAbstractItemView::SelectItems),
53 currentlyCommittingEditor(nullptr),
54 pressClosedEditor(false),
55 waitForIMCommit(false),
56 pressedModifiers(Qt::NoModifier),
57 pressedPosition(QPoint(-1, -1)),
58 pressedAlreadySelected(false),
59 releaseFromDoubleClick(false),
60 viewportEnteredNeeded(false),
61 state(QAbstractItemView::NoState),
62 stateBeforeAnimation(QAbstractItemView::NoState),
63 editTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed),
64 lastTrigger(QAbstractItemView::NoEditTriggers),
65 tabKeyNavigation(false),
66#if QT_CONFIG(draganddrop)
67 showDropIndicator(true),
68 dragEnabled(false),
69 dragDropMode(QAbstractItemView::NoDragDrop),
70 overwrite(false),
71 dropEventMoved(false),
72 dropIndicatorPosition(QAbstractItemView::OnItem),
73 defaultDropAction(Qt::IgnoreAction),
74#endif
75 autoScroll(true),
76 autoScrollMargin(16),
77 autoScrollCount(0),
78 shouldScrollToCurrentOnShow(false),
79 shouldClearStatusTip(false),
80 alternatingColors(false),
81 textElideMode(Qt::ElideRight),
82 verticalScrollMode(QAbstractItemView::ScrollPerItem),
83 horizontalScrollMode(QAbstractItemView::ScrollPerItem),
84 currentIndexSet(false),
85 wrapItemText(false),
86 delayedPendingLayout(true),
87 moveCursorUpdatedView(false),
88 verticalScrollModeSet(false),
89 horizontalScrollModeSet(false),
90 updateThreshold(200)
91{
92 keyboardInputTime.invalidate();
93}
94
95QAbstractItemViewPrivate::~QAbstractItemViewPrivate()
96{
97}
98
99void QAbstractItemViewPrivate::init()
100{
101 Q_Q(QAbstractItemView);
102 q->setItemDelegate(new QStyledItemDelegate(q));
103
104 vbar->setRange(0, 0);
105 hbar->setRange(0, 0);
106
107 scrollbarConnections = {
108 QObject::connect(vbar, &QScrollBar::actionTriggered,
109 q, &QAbstractItemView::verticalScrollbarAction),
110 QObject::connect(hbar, &QScrollBar::actionTriggered,
111 q, &QAbstractItemView::horizontalScrollbarAction),
112 QObject::connect(vbar, &QScrollBar::valueChanged,
113 q, &QAbstractItemView::verticalScrollbarValueChanged),
114 QObject::connect(hbar, &QScrollBar::valueChanged,
115 q, &QAbstractItemView::horizontalScrollbarValueChanged)
116 };
117 viewport->setBackgroundRole(QPalette::Base);
118
119 q->setAttribute(Qt::WA_InputMethodEnabled);
120
121 verticalScrollMode = static_cast<QAbstractItemView::ScrollMode>(q->style()->styleHint(QStyle::SH_ItemView_ScrollMode, nullptr, q, nullptr));
122 horizontalScrollMode = static_cast<QAbstractItemView::ScrollMode>(q->style()->styleHint(QStyle::SH_ItemView_ScrollMode, nullptr, q, nullptr));
123}
124
125void QAbstractItemViewPrivate::setHoverIndex(const QPersistentModelIndex &index)
126{
127 Q_Q(QAbstractItemView);
128 if (hover == index)
129 return;
130
131 if (selectionBehavior != QAbstractItemView::SelectRows) {
132 q->update(hover); //update the old one
133 q->update(index); //update the new one
134 } else {
135 const QRect oldHoverRect = visualRect(hover);
136 const QRect newHoverRect = visualRect(index);
137 viewport->update(QRect(0, newHoverRect.y(), viewport->width(), newHoverRect.height()));
138 viewport->update(QRect(0, oldHoverRect.y(), viewport->width(), oldHoverRect.height()));
139 }
140 hover = index;
141}
142
143void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index)
144{
145 //we take a persistent model index because the model might change by emitting signals
146 Q_Q(QAbstractItemView);
147 setHoverIndex(index);
148 if (viewportEnteredNeeded || enteredIndex != index) {
149 viewportEnteredNeeded = false;
150
151 if (index.isValid()) {
152 emit q->entered(index);
153#if QT_CONFIG(statustip)
154 QString statustip = model->data(index, Qt::StatusTipRole).toString();
155 if (parent && (shouldClearStatusTip || !statustip.isEmpty())) {
156 QStatusTipEvent tip(statustip);
157 QCoreApplication::sendEvent(parent, &tip);
158 shouldClearStatusTip = !statustip.isEmpty();
159 }
160#endif
161 } else {
162#if QT_CONFIG(statustip)
163 if (parent && shouldClearStatusTip) {
164 QString emptyString;
165 QStatusTipEvent tip( emptyString );
166 QCoreApplication::sendEvent(parent, &tip);
167 }
168#endif
169 emit q->viewportEntered();
170 }
171 enteredIndex = index;
172 }
173}
174
175#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
176
177// stores and restores the selection and current item when flicking
178void QAbstractItemViewPrivate::scrollerStateChanged()
179{
180 Q_Q(QAbstractItemView);
181
182 if (QScroller *scroller = QScroller::scroller(viewport)) {
183 switch (scroller->state()) {
184 case QScroller::Pressed:
185 // store the current selection in case we start scrolling
186 if (q->selectionModel()) {
187 oldSelection = q->selectionModel()->selection();
188 oldCurrent = q->selectionModel()->currentIndex();
189 }
190 break;
191
192 case QScroller::Dragging:
193 // restore the old selection if we really start scrolling
194 if (q->selectionModel()) {
195 q->selectionModel()->select(oldSelection, QItemSelectionModel::ClearAndSelect);
196 // block autoScroll logic while we are already handling scrolling
197 const bool wasAutoScroll = autoScroll;
198 autoScroll = false;
199 q->selectionModel()->setCurrentIndex(oldCurrent, QItemSelectionModel::NoUpdate);
200 autoScroll = wasAutoScroll;
201 }
202 Q_FALLTHROUGH();
203
204 default:
205 oldSelection = QItemSelection();
206 oldCurrent = QModelIndex();
207 break;
208 }
209 }
210}
211
212#endif // QT_NO_GESTURES
213
214void QAbstractItemViewPrivate::delegateSizeHintChanged(const QModelIndex &index)
215{
216 Q_Q(QAbstractItemView);
217 if (model) {
218 if (!model->checkIndex(index))
219 qWarning("Delegate size hint changed for a model index that does not belong to this view");
220 }
221 QMetaObject::invokeMethod(q, &QAbstractItemView::doItemsLayout, Qt::QueuedConnection);
222}
223
224void QAbstractItemViewPrivate::connectDelegate(QAbstractItemDelegate *delegate)
225{
226 if (!delegate)
227 return;
228 Q_Q(QAbstractItemView);
229 QObject::connect(delegate, &QAbstractItemDelegate::closeEditor,
230 q, &QAbstractItemView::closeEditor);
231 QObject::connect(delegate, &QAbstractItemDelegate::commitData,
232 q, &QAbstractItemView::commitData);
233 QObjectPrivate::connect(delegate, &QAbstractItemDelegate::sizeHintChanged,
234 this, &QAbstractItemViewPrivate::delegateSizeHintChanged);
235}
236
237void QAbstractItemViewPrivate::disconnectDelegate(QAbstractItemDelegate *delegate)
238{
239 if (!delegate)
240 return;
241 Q_Q(QAbstractItemView);
242 QObject::disconnect(delegate, &QAbstractItemDelegate::closeEditor,
243 q, &QAbstractItemView::closeEditor);
244 QObject::disconnect(delegate, &QAbstractItemDelegate::commitData,
245 q, &QAbstractItemView::commitData);
246 QObjectPrivate::disconnect(delegate, &QAbstractItemDelegate::sizeHintChanged,
247 this, &QAbstractItemViewPrivate::delegateSizeHintChanged);
248}
249
250void QAbstractItemViewPrivate::disconnectAll()
251{
252 Q_Q(QAbstractItemView);
253 for (const QMetaObject::Connection &connection : modelConnections)
254 QObject::disconnect(connection);
255 for (const QMetaObject::Connection &connection : scrollbarConnections)
256 QObject::disconnect(connection);
257 disconnectDelegate(itemDelegate);
258 for (QAbstractItemDelegate *delegate : std::as_const(rowDelegates))
259 disconnectDelegate(delegate);
260 for (QAbstractItemDelegate *delegate : std::as_const(columnDelegates))
261 disconnectDelegate(delegate);
262 if (model && selectionModel) {
263 QObject::disconnect(model, &QAbstractItemModel::destroyed,
264 selectionModel, &QItemSelectionModel::deleteLater);
265 }
266 if (selectionModel) {
267 QObject::disconnect(selectionModel, &QItemSelectionModel::selectionChanged,
268 q, &QAbstractItemView::selectionChanged);
269 QObject::disconnect(selectionModel, &QItemSelectionModel::currentChanged,
270 q, &QAbstractItemView::currentChanged);
271 }
272 for (const auto &info : std::as_const(indexEditorHash)) {
273 if (!info.isStatic && info.widget)
274 QObject::disconnect(info.widget, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
275 }
276#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
277 QObject::disconnect(scollerConnection);
278#endif
279}
280
281/*!
282 \class QAbstractItemView
283
284 \brief The QAbstractItemView class provides the basic functionality for
285 item view classes.
286
287 \ingroup model-view
288 \inmodule QtWidgets
289
290 QAbstractItemView class is the base class for every standard view
291 that uses a QAbstractItemModel. QAbstractItemView is an abstract
292 class and cannot itself be instantiated. It provides a standard
293 interface for interoperating with models through the signals and
294 slots mechanism, enabling subclasses to be kept up-to-date with
295 changes to their models. This class provides standard support for
296 keyboard and mouse navigation, viewport scrolling, item editing,
297 and selections. The keyboard navigation implements this
298 functionality:
299
300 \table
301 \header
302 \li Keys
303 \li Functionality
304 \row
305 \li Arrow keys
306 \li Changes the current item and selects it.
307 \row
308 \li Ctrl+Arrow keys
309 \li Changes the current item but does not select it.
310 \row
311 \li Shift+Arrow keys
312 \li Changes the current item and selects it. The previously
313 selected item(s) is not deselected.
314 \row
315 \li Ctrl+Space
316 \li Toggles selection of the current item.
317 \row
318 \li Tab/Backtab
319 \li Changes the current item to the next/previous item.
320 \row
321 \li Home/End
322 \li Selects the first/last item in the model.
323 \row
324 \li Page up/Page down
325 \li Scrolls the rows shown up/down by the number of
326 visible rows in the view.
327 \row
328 \li Ctrl+A
329 \li Selects all items in the model.
330 \endtable
331
332 Note that the above table assumes that the
333 \l{selectionMode}{selection mode} allows the operations. For
334 instance, you cannot select items if the selection mode is
335 QAbstractItemView::NoSelection.
336
337 The QAbstractItemView class is one of the \l{Model/View Classes}
338 and is part of Qt's \l{Model/View Programming}{model/view framework}.
339
340 The view classes that inherit QAbstractItemView only need
341 to implement their own view-specific functionality, such as
342 drawing items, returning the geometry of items, finding items,
343 etc.
344
345 QAbstractItemView provides common slots such as edit() and
346 setCurrentIndex(). Many protected slots are also provided, including
347 dataChanged(), rowsInserted(), rowsAboutToBeRemoved(), selectionChanged(),
348 and currentChanged().
349
350 The root item is returned by rootIndex(), and the current item by
351 currentIndex(). To make sure that an item is visible use
352 scrollTo().
353
354 Some of QAbstractItemView's functions are concerned with
355 scrolling, for example setHorizontalScrollMode() and
356 setVerticalScrollMode(). To set the range of the scroll bars, you
357 can, for example, reimplement the view's resizeEvent() function:
358
359 \snippet code/src_gui_itemviews_qabstractitemview.cpp 0
360
361 Note that the range is not updated until the widget is shown.
362
363 Several other functions are concerned with selection control; for
364 example setSelectionMode(), and setSelectionBehavior(). This class
365 provides a default selection model to work with
366 (selectionModel()), but this can be replaced by using
367 setSelectionModel() with an instance of QItemSelectionModel.
368
369 For complete control over the display and editing of items you can
370 specify a delegate with setItemDelegate().
371
372 QAbstractItemView provides a lot of protected functions. Some are
373 concerned with editing, for example, edit(), and commitData(),
374 whilst others are keyboard and mouse event handlers.
375
376 \note If you inherit QAbstractItemView and intend to update the contents
377 of the viewport, you should use viewport->update() instead of
378 \l{QWidget::update()}{update()} as all painting operations take place on the
379 viewport.
380
381 \sa {View Classes}, {Model/View Programming}, QAbstractItemModel
382*/
383
384/*!
385 \enum QAbstractItemView::SelectionMode
386
387 This enum indicates how the view responds to user selections:
388
389 \value SingleSelection When the user selects an item, any already-selected
390 item becomes unselected. It is possible for the user to deselect the selected
391 item by pressing the Ctrl key when clicking the selected item.
392
393 \value ContiguousSelection When the user selects an item in the usual way,
394 the selection is cleared and the new item selected. However, if the user
395 presses the Shift key while clicking on an item, all items between the
396 current item and the clicked item are selected or unselected, depending on
397 the state of the clicked item.
398
399 \value ExtendedSelection When the user selects an item in the usual way,
400 the selection is cleared and the new item selected. However, if the user
401 presses the Ctrl key when clicking on an item, the clicked item gets
402 toggled and all other items are left untouched. If the user presses the
403 Shift key while clicking on an item, all items between the current item
404 and the clicked item are selected or unselected, depending on the state of
405 the clicked item. Multiple items can be selected by dragging the mouse over
406 them.
407
408 \value MultiSelection When the user selects an item in the usual way, the
409 selection status of that item is toggled and the other items are left
410 alone. Multiple items can be toggled by dragging the mouse over them.
411
412 \value NoSelection Items cannot be selected.
413
414 The most commonly used modes are SingleSelection and ExtendedSelection.
415*/
416
417/*!
418 \enum QAbstractItemView::SelectionBehavior
419
420 \value SelectItems Selecting single items.
421 \value SelectRows Selecting only rows.
422 \value SelectColumns Selecting only columns.
423*/
424
425/*!
426 \enum QAbstractItemView::ScrollHint
427
428 \value EnsureVisible Scroll to ensure that the item is visible.
429 \value PositionAtTop Scroll to position the item at the top of the
430 viewport.
431 \value PositionAtBottom Scroll to position the item at the bottom of the
432 viewport.
433 \value PositionAtCenter Scroll to position the item at the center of the
434 viewport.
435*/
436
437
438/*!
439 \enum QAbstractItemView::EditTrigger
440
441 This enum describes actions which will initiate item editing.
442
443 \value NoEditTriggers No editing possible.
444 \value CurrentChanged Editing start whenever current item changes.
445 \value DoubleClicked Editing starts when an item is double clicked.
446 \value SelectedClicked Editing starts when clicking on an already selected
447 item.
448 \value EditKeyPressed Editing starts when the platform edit key has been
449 pressed over an item.
450 \value AnyKeyPressed Editing starts when any key is pressed over an item.
451 \value AllEditTriggers Editing starts for all above actions.
452*/
453
454/*!
455 \enum QAbstractItemView::CursorAction
456
457 This enum describes the different ways to navigate between items,
458 \sa moveCursor()
459
460 \value MoveUp Move to the item above the current item.
461 \value MoveDown Move to the item below the current item.
462 \value MoveLeft Move to the item left of the current item.
463 \value MoveRight Move to the item right of the current item.
464 \value MoveHome Move to the top-left corner item.
465 \value MoveEnd Move to the bottom-right corner item.
466 \value MovePageUp Move one page up above the current item.
467 \value MovePageDown Move one page down below the current item.
468 \value MoveNext Move to the item after the current item.
469 \value MovePrevious Move to the item before the current item.
470*/
471
472/*!
473 \enum QAbstractItemView::State
474
475 Describes the different states the view can be in. This is usually
476 only interesting when reimplementing your own view.
477
478 \value NoState The is the default state.
479 \value DraggingState The user is dragging items.
480 \value DragSelectingState The user is selecting items.
481 \value EditingState The user is editing an item in a widget editor.
482 \value ExpandingState The user is opening a branch of items.
483 \value CollapsingState The user is closing a branch of items.
484 \value AnimatingState The item view is performing an animation.
485*/
486
487/*!
488 \enum QAbstractItemView::ScrollMode
489
490 Describes how the scrollbar should behave. When setting the scroll mode
491 to ScrollPerPixel the single step size will adjust automatically unless
492 it was set explicitly using \l{QAbstractSlider::}{setSingleStep()}.
493 The automatic adjustment can be restored by setting the single step size to -1.
494
495 \value ScrollPerItem The view will scroll the contents one item at a time.
496 \value ScrollPerPixel The view will scroll the contents one pixel at a time.
497*/
498
499/*!
500 \fn QRect QAbstractItemView::visualRect(const QModelIndex &index) const = 0
501 Returns the rectangle on the viewport occupied by the item at \a index.
502
503 If your item is displayed in several areas then visualRect should return
504 the primary area that contains index and not the complete area that index
505 might encompasses, touch or cause drawing.
506
507 In the base class this is a pure virtual function.
508
509 \sa indexAt(), visualRegionForSelection()
510*/
511
512/*!
513 \fn void QAbstractItemView::scrollTo(const QModelIndex &index, ScrollHint hint) = 0
514
515 Scrolls the view if necessary to ensure that the item at \a index
516 is visible. The view will try to position the item according to the given \a hint.
517
518 In the base class this is a pure virtual function.
519*/
520
521/*!
522 \fn QModelIndex QAbstractItemView::indexAt(const QPoint &point) const = 0
523
524 Returns the model index of the item at the viewport coordinates \a point.
525
526 In the base class this is a pure virtual function.
527
528 \sa visualRect()
529*/
530
531/*!
532 \fn void QAbstractItemView::activated(const QModelIndex &index)
533
534 This signal is emitted when the item specified by \a index is
535 activated by the user. How to activate items depends on the
536 platform; e.g., by single- or double-clicking the item, or by
537 pressing the Return or Enter key when the item is current.
538
539 \sa clicked(), doubleClicked(), entered(), pressed()
540*/
541
542/*!
543 \fn void QAbstractItemView::entered(const QModelIndex &index)
544
545 This signal is emitted when the mouse cursor enters the item
546 specified by \a index.
547 Mouse tracking needs to be enabled for this feature to work.
548
549 \sa viewportEntered(), activated(), clicked(), doubleClicked(), pressed()
550*/
551
552/*!
553 \fn void QAbstractItemView::viewportEntered()
554
555 This signal is emitted when the mouse cursor enters the viewport.
556 Mouse tracking needs to be enabled for this feature to work.
557
558 \sa entered()
559*/
560
561/*!
562 \fn void QAbstractItemView::pressed(const QModelIndex &index)
563
564 This signal is emitted when a mouse button is pressed. The item
565 the mouse was pressed on is specified by \a index. The signal is
566 only emitted when the index is valid.
567
568 Use the QGuiApplication::mouseButtons() function to get the state
569 of the mouse buttons.
570
571 \sa activated(), clicked(), doubleClicked(), entered()
572*/
573
574/*!
575 \fn void QAbstractItemView::clicked(const QModelIndex &index)
576
577 This signal is emitted when a mouse button is left-clicked. The item
578 the mouse was clicked on is specified by \a index. The signal is
579 only emitted when the index is valid.
580
581 \sa activated(), doubleClicked(), entered(), pressed()
582*/
583
584/*!
585 \fn void QAbstractItemView::doubleClicked(const QModelIndex &index)
586
587 This signal is emitted when a mouse button is double-clicked. The
588 item the mouse was double-clicked on is specified by \a index.
589 The signal is only emitted when the index is valid.
590
591 \sa clicked(), activated()
592*/
593
594/*!
595 \fn QModelIndex QAbstractItemView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) = 0
596
597 Returns a QModelIndex object pointing to the next object in the view,
598 based on the given \a cursorAction and keyboard modifiers specified
599 by \a modifiers.
600
601 In the base class this is a pure virtual function.
602*/
603
604/*!
605 \fn int QAbstractItemView::horizontalOffset() const = 0
606
607 Returns the horizontal offset of the view.
608
609 In the base class this is a pure virtual function.
610
611 \sa verticalOffset()
612*/
613
614/*!
615 \fn int QAbstractItemView::verticalOffset() const = 0
616
617 Returns the vertical offset of the view.
618
619 In the base class this is a pure virtual function.
620
621 \sa horizontalOffset()
622*/
623
624/*!
625 \fn bool QAbstractItemView::isIndexHidden(const QModelIndex &index) const
626
627 Returns \c true if the item referred to by the given \a index is hidden in the view,
628 otherwise returns \c false.
629
630 Hiding is a view specific feature. For example in TableView a column can be marked
631 as hidden or a row in the TreeView.
632
633 In the base class this is a pure virtual function.
634*/
635
636/*!
637 \fn void QAbstractItemView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags)
638
639 Applies the selection \a flags to the items in or touched by the
640 rectangle, \a rect.
641
642 When implementing your own itemview setSelection should call
643 selectionModel()->select(selection, flags) where selection
644 is either an empty QModelIndex or a QItemSelection that contains
645 all items that are contained in \a rect.
646
647 \sa selectionCommand(), selectedIndexes()
648*/
649
650/*!
651 \fn QRegion QAbstractItemView::visualRegionForSelection(const QItemSelection &selection) const = 0
652
653 Returns the region from the viewport of the items in the given
654 \a selection.
655
656 In the base class this is a pure virtual function.
657
658 \sa visualRect(), selectedIndexes()
659*/
660
661/*!
662 Constructs an abstract item view with the given \a parent.
663*/
664QAbstractItemView::QAbstractItemView(QWidget *parent)
665 : QAbstractScrollArea(*(new QAbstractItemViewPrivate), parent)
666{
667 d_func()->init();
668}
669
670/*!
671 \internal
672*/
673QAbstractItemView::QAbstractItemView(QAbstractItemViewPrivate &dd, QWidget *parent)
674 : QAbstractScrollArea(dd, parent)
675{
676 d_func()->init();
677}
678
679/*!
680 Destroys the view.
681*/
682QAbstractItemView::~QAbstractItemView()
683{
684 Q_D(QAbstractItemView);
685 // stop these timers here before ~QObject
686 d->delayedReset.stop();
687 d->updateTimer.stop();
688 d->delayedEditing.stop();
689 d->delayedAutoScroll.stop();
690 d->autoScrollTimer.stop();
691 d->delayedLayout.stop();
692 d->fetchMoreTimer.stop();
693 d->disconnectAll();
694}
695
696/*!
697 Sets the \a model for the view to present.
698
699 This function will create and set a new selection model, replacing any
700 model that was previously set with setSelectionModel(). However, the old
701 selection model will not be deleted as it may be shared between several
702 views. We recommend that you delete the old selection model if it is no
703 longer required. This is done with the following code:
704
705 \snippet code/src_gui_itemviews_qabstractitemview.cpp 2
706
707 If both the old model and the old selection model do not have parents, or
708 if their parents are long-lived objects, it may be preferable to call their
709 deleteLater() functions to explicitly delete them.
710
711 The view \e{does not} take ownership of the model unless it is the model's
712 parent object because the model may be shared between many different views.
713
714 \sa selectionModel(), setSelectionModel()
715*/
716void QAbstractItemView::setModel(QAbstractItemModel *model)
717{
718 Q_D(QAbstractItemView);
719 if (model == d->model)
720 return;
721 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
722 for (const QMetaObject::Connection &connection : d->modelConnections)
723 disconnect(connection);
724 }
725 d->model = (model ? model : QAbstractItemModelPrivate::staticEmptyModel());
726
727 if (d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
728 d->modelConnections = {
729 QObjectPrivate::connect(d->model, &QAbstractItemModel::destroyed,
730 d, &QAbstractItemViewPrivate::modelDestroyed),
731 QObject::connect(d->model, &QAbstractItemModel::dataChanged,
732 this, &QAbstractItemView::dataChanged),
733 QObjectPrivate::connect(d->model, &QAbstractItemModel::headerDataChanged,
734 d, &QAbstractItemViewPrivate::headerDataChanged),
735 QObject::connect(d->model, &QAbstractItemModel::rowsInserted,
736 this, &QAbstractItemView::rowsInserted),
737 QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsInserted,
738 d, &QAbstractItemViewPrivate::rowsInserted),
739 QObject::connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved,
740 this, &QAbstractItemView::rowsAboutToBeRemoved),
741 QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsRemoved,
742 d, &QAbstractItemViewPrivate::rowsRemoved),
743 QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsMoved,
744 d, &QAbstractItemViewPrivate::rowsMoved),
745 QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved,
746 d, &QAbstractItemViewPrivate::columnsAboutToBeRemoved),
747 QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsRemoved,
748 d, &QAbstractItemViewPrivate::columnsRemoved),
749 QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsInserted,
750 d, &QAbstractItemViewPrivate::columnsInserted),
751 QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsMoved,
752 d, &QAbstractItemViewPrivate::columnsMoved),
753 QObject::connect(d->model, &QAbstractItemModel::modelReset,
754 this, &QAbstractItemView::reset),
755 QObjectPrivate::connect(d->model, &QAbstractItemModel::layoutChanged,
756 d, &QAbstractItemViewPrivate::layoutChanged),
757 };
758 }
759
760 QItemSelectionModel *selection_model = new QItemSelectionModel(d->model, this);
761 connect(d->model, &QAbstractItemModel::destroyed,
762 selection_model, &QItemSelectionModel::deleteLater);
763 setSelectionModel(selection_model);
764
765 reset(); // kill editors, set new root and do layout
766}
767
768/*!
769 Returns the model that this view is presenting.
770*/
771QAbstractItemModel *QAbstractItemView::model() const
772{
773 Q_D(const QAbstractItemView);
774 return (d->model == QAbstractItemModelPrivate::staticEmptyModel() ? nullptr : d->model);
775}
776
777/*!
778 Sets the current selection model to the given \a selectionModel.
779
780 Note that, if you call setModel() after this function, the given \a selectionModel
781 will be replaced by one created by the view.
782
783 \note It is up to the application to delete the old selection model if it is no
784 longer needed; i.e., if it is not being used by other views. This will happen
785 automatically when its parent object is deleted. However, if it does not have a
786 parent, or if the parent is a long-lived object, it may be preferable to call its
787 deleteLater() function to explicitly delete it.
788
789 \sa selectionModel(), setModel(), clearSelection()
790*/
791void QAbstractItemView::setSelectionModel(QItemSelectionModel *selectionModel)
792{
793 // ### if the given model is null, we should use the original selection model
794 Q_ASSERT(selectionModel);
795 Q_D(QAbstractItemView);
796
797 if (Q_UNLIKELY(selectionModel->model() != d->model)) {
798 qWarning("QAbstractItemView::setSelectionModel() failed: "
799 "Trying to set a selection model, which works on "
800 "a different model than the view.");
801 return;
802 }
803
804 QItemSelection oldSelection;
805 QModelIndex oldCurrentIndex;
806
807 if (d->selectionModel) {
808 if (d->selectionModel->model() == selectionModel->model()) {
809 oldSelection = d->selectionModel->selection();
810 oldCurrentIndex = d->selectionModel->currentIndex();
811 }
812 disconnect(d->selectionModel, &QItemSelectionModel::selectionChanged,
813 this, &QAbstractItemView::selectionChanged);
814 disconnect(d->selectionModel, &QItemSelectionModel::currentChanged,
815 this, &QAbstractItemView::currentChanged);
816 }
817
818 d->selectionModel = selectionModel;
819
820 if (d->selectionModel) {
821 connect(d->selectionModel, &QItemSelectionModel::selectionChanged,
822 this, &QAbstractItemView::selectionChanged);
823 connect(d->selectionModel, &QItemSelectionModel::currentChanged,
824 this, &QAbstractItemView::currentChanged);
825
826 selectionChanged(d->selectionModel->selection(), oldSelection);
827 currentChanged(d->selectionModel->currentIndex(), oldCurrentIndex);
828 }
829}
830
831/*!
832 Returns the current selection model.
833
834 \sa setSelectionModel(), selectedIndexes()
835*/
836QItemSelectionModel* QAbstractItemView::selectionModel() const
837{
838 Q_D(const QAbstractItemView);
839 return d->selectionModel;
840}
841
842/*!
843 Sets the item delegate for this view and its model to \a delegate.
844 This is useful if you want complete control over the editing and
845 display of items.
846
847 Any existing delegate will be removed, but not deleted. QAbstractItemView
848 does not take ownership of \a delegate.
849
850 \warning You should not share the same instance of a delegate between views.
851 Doing so can cause incorrect or unintuitive editing behavior since each
852 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
853 signal, and attempt to access, modify or close an editor that has already been closed.
854
855 \sa itemDelegate()
856*/
857void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate)
858{
859 Q_D(QAbstractItemView);
860 if (delegate == d->itemDelegate)
861 return;
862
863 if (d->itemDelegate) {
864 if (d->delegateRefCount(d->itemDelegate) == 1)
865 d->disconnectDelegate(d->itemDelegate);
866 }
867
868 if (delegate) {
869 if (d->delegateRefCount(delegate) == 0)
870 d->connectDelegate(delegate);
871 }
872 d->itemDelegate = delegate;
873 viewport()->update();
874 d->doDelayedItemsLayout();
875}
876
877/*!
878 Returns the item delegate used by this view and model. This is
879 either one set with setItemDelegate(), or the default one.
880
881 \sa setItemDelegate()
882*/
883QAbstractItemDelegate *QAbstractItemView::itemDelegate() const
884{
885 return d_func()->itemDelegate;
886}
887
888/*!
889 \reimp
890*/
891QVariant QAbstractItemView::inputMethodQuery(Qt::InputMethodQuery query) const
892{
893 Q_D(const QAbstractItemView);
894 const QModelIndex current = currentIndex();
895 QVariant result;
896 if (current.isValid()) {
897 if (QWidget *currentEditor;
898 d->waitForIMCommit && (currentEditor = d->editorForIndex(current).widget)) {
899 // An editor is open but the initial preedit is still ongoing. Delegate
900 // queries to the editor and map coordinates from editor to this view.
901 result = currentEditor->inputMethodQuery(query);
902 if (result.typeId() == QMetaType::QRect) {
903 const QRect editorRect = result.value<QRect>();
904 result = QRect(currentEditor->mapTo(this, editorRect.topLeft()), editorRect.size());
905 }
906 } else if (query == Qt::ImCursorRectangle) {
907 result = visualRect(current);
908 }
909 }
910 if (!result.isValid())
911 result = QAbstractScrollArea::inputMethodQuery(query);
912 return result;
913}
914
915/*!
916 Sets the given item \a delegate used by this view and model for the given
917 \a row. All items on \a row will be drawn and managed by \a delegate
918 instead of using the default delegate (i.e., itemDelegate()).
919
920 Any existing row delegate for \a row will be removed, but not
921 deleted. QAbstractItemView does not take ownership of \a delegate.
922
923 \note If a delegate has been assigned to both a row and a column, the row
924 delegate (i.e., this delegate) will take precedence and manage the
925 intersecting cell index.
926
927 \warning You should not share the same instance of a delegate between views.
928 Doing so can cause incorrect or unintuitive editing behavior since each
929 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
930 signal, and attempt to access, modify or close an editor that has already been closed.
931
932 \sa itemDelegateForRow(), setItemDelegateForColumn(), itemDelegate()
933*/
934void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)
935{
936 Q_D(QAbstractItemView);
937 if (QAbstractItemDelegate *rowDelegate = d->rowDelegates.value(row, nullptr)) {
938 if (d->delegateRefCount(rowDelegate) == 1)
939 d->disconnectDelegate(rowDelegate);
940 d->rowDelegates.remove(row);
941 }
942 if (delegate) {
943 if (d->delegateRefCount(delegate) == 0)
944 d->connectDelegate(delegate);
945 d->rowDelegates.insert(row, delegate);
946 }
947 viewport()->update();
948 d->doDelayedItemsLayout();
949}
950
951/*!
952 Returns the item delegate used by this view and model for the given \a row,
953 or \nullptr if no delegate has been assigned. You can call itemDelegate()
954 to get a pointer to the current delegate for a given index.
955
956 \sa setItemDelegateForRow(), itemDelegateForColumn(), setItemDelegate()
957*/
958QAbstractItemDelegate *QAbstractItemView::itemDelegateForRow(int row) const
959{
960 Q_D(const QAbstractItemView);
961 return d->rowDelegates.value(row, nullptr);
962}
963
964/*!
965 Sets the given item \a delegate used by this view and model for the given
966 \a column. All items on \a column will be drawn and managed by \a delegate
967 instead of using the default delegate (i.e., itemDelegate()).
968
969 Any existing column delegate for \a column will be removed, but not
970 deleted. QAbstractItemView does not take ownership of \a delegate.
971
972 \note If a delegate has been assigned to both a row and a column, the row
973 delegate will take precedence and manage the intersecting cell index.
974
975 \warning You should not share the same instance of a delegate between views.
976 Doing so can cause incorrect or unintuitive editing behavior since each
977 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
978 signal, and attempt to access, modify or close an editor that has already been closed.
979
980 \sa itemDelegateForColumn(), setItemDelegateForRow(), itemDelegate()
981*/
982void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
983{
984 Q_D(QAbstractItemView);
985 if (QAbstractItemDelegate *columnDelegate = d->columnDelegates.value(column, nullptr)) {
986 if (d->delegateRefCount(columnDelegate) == 1)
987 d->disconnectDelegate(columnDelegate);
988 d->columnDelegates.remove(column);
989 }
990 if (delegate) {
991 if (d->delegateRefCount(delegate) == 0)
992 d->connectDelegate(delegate);
993 d->columnDelegates.insert(column, delegate);
994 }
995 viewport()->update();
996 d->doDelayedItemsLayout();
997}
998
999/*!
1000 Returns the item delegate used by this view and model for the given \a
1001 column. You can call itemDelegate() to get a pointer to the current delegate
1002 for a given index.
1003
1004 \sa setItemDelegateForColumn(), itemDelegateForRow(), itemDelegate()
1005*/
1006QAbstractItemDelegate *QAbstractItemView::itemDelegateForColumn(int column) const
1007{
1008 Q_D(const QAbstractItemView);
1009 return d->columnDelegates.value(column, nullptr);
1010}
1011
1012/*!
1013 \fn QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) const
1014 \deprecated Use itemDelegateForIndex() instead.
1015 Returns the item delegate used by this view and model for
1016 the given \a index.
1017*/
1018
1019/*!
1020 \since 6.0
1021
1022 Returns the item delegate used by this view and model for
1023 the given \a index.
1024
1025 \sa setItemDelegate(), setItemDelegateForRow(), setItemDelegateForColumn()
1026*/
1027QAbstractItemDelegate *QAbstractItemView::itemDelegateForIndex(const QModelIndex &index) const
1028{
1029 Q_D(const QAbstractItemView);
1030 return d->delegateForIndex(index);
1031}
1032
1033/*!
1034 \property QAbstractItemView::selectionMode
1035 \brief which selection mode the view operates in
1036
1037 This property controls whether the user can select one or many items
1038 and, in many-item selections, whether the selection must be a
1039 continuous range of items.
1040
1041 \sa SelectionMode, SelectionBehavior
1042*/
1043void QAbstractItemView::setSelectionMode(SelectionMode mode)
1044{
1045 Q_D(QAbstractItemView);
1046 d->selectionMode = mode;
1047}
1048
1049QAbstractItemView::SelectionMode QAbstractItemView::selectionMode() const
1050{
1051 Q_D(const QAbstractItemView);
1052 return d->selectionMode;
1053}
1054
1055/*!
1056 \property QAbstractItemView::selectionBehavior
1057 \brief which selection behavior the view uses
1058
1059 This property holds whether selections are done
1060 in terms of single items, rows or columns.
1061
1062 \sa SelectionMode, SelectionBehavior
1063*/
1064
1065void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
1066{
1067 Q_D(QAbstractItemView);
1068 d->selectionBehavior = behavior;
1069}
1070
1071QAbstractItemView::SelectionBehavior QAbstractItemView::selectionBehavior() const
1072{
1073 Q_D(const QAbstractItemView);
1074 return d->selectionBehavior;
1075}
1076
1077/*!
1078 Sets the current item to be the item at \a index.
1079
1080 Unless the current selection mode is
1081 \l{QAbstractItemView::}{NoSelection}, the item is also selected.
1082 Note that this function also updates the starting position for any
1083 new selections the user performs.
1084
1085 To set an item as the current item without selecting it, call
1086
1087 \c{selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);}
1088
1089 \sa currentIndex(), currentChanged(), selectionMode
1090*/
1091void QAbstractItemView::setCurrentIndex(const QModelIndex &index)
1092{
1093 Q_D(QAbstractItemView);
1094 if (d->selectionModel && (!index.isValid() || d->isIndexEnabled(index))) {
1095 QItemSelectionModel::SelectionFlags command = selectionCommand(index, nullptr);
1096 d->selectionModel->setCurrentIndex(index, command);
1097 d->currentIndexSet = true;
1098 }
1099}
1100
1101/*!
1102 Returns the model index of the current item.
1103
1104 \sa setCurrentIndex()
1105*/
1106QModelIndex QAbstractItemView::currentIndex() const
1107{
1108 Q_D(const QAbstractItemView);
1109 return d->selectionModel ? d->selectionModel->currentIndex() : QModelIndex();
1110}
1111
1112
1113/*!
1114 Reset the internal state of the view.
1115
1116 \warning This function will reset open editors, scroll bar positions,
1117 selections, etc. Existing changes will not be committed. If you would like
1118 to save your changes when resetting the view, you can reimplement this
1119 function, commit your changes, and then call the superclass'
1120 implementation.
1121*/
1122void QAbstractItemView::reset()
1123{
1124 Q_D(QAbstractItemView);
1125 d->delayedReset.stop(); //make sure we stop the timer
1126 // Taking a copy because releaseEditor() eventurally calls deleteLater() on the
1127 // editor, which calls QCoreApplication::postEvent(), the latter may invoke unknown
1128 // code that may modify d->indexEditorHash.
1129 const auto copy = d->indexEditorHash;
1130 for (const auto &[index, info] : copy.asKeyValueRange()) {
1131 if (info.widget)
1132 d->releaseEditor(info.widget.data(), d->indexForEditor(info.widget.data()));
1133 }
1134 d->editorIndexHash.clear();
1135 d->indexEditorHash.clear();
1136 d->persistent.clear();
1137 d->currentIndexSet = false;
1138 setState(NoState);
1139 setRootIndex(QModelIndex());
1140 if (d->selectionModel)
1141 d->selectionModel->reset();
1142#if QT_CONFIG(accessibility)
1143 if (QAccessible::isActive()) {
1144 QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::ModelReset);
1145 QAccessible::updateAccessibility(&accessibleEvent);
1146 }
1147#endif
1148 d->updateGeometry();
1149}
1150
1151/*!
1152 Sets the root item to the item at the given \a index.
1153
1154 \sa rootIndex()
1155*/
1156void QAbstractItemView::setRootIndex(const QModelIndex &index)
1157{
1158 Q_D(QAbstractItemView);
1159 if (Q_UNLIKELY(index.isValid() && index.model() != d->model)) {
1160 qWarning("QAbstractItemView::setRootIndex failed : index must be from the currently set model");
1161 return;
1162 }
1163 d->root = index;
1164#if QT_CONFIG(accessibility)
1165 if (QAccessible::isActive()) {
1166 QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::ModelReset);
1167 QAccessible::updateAccessibility(&accessibleEvent);
1168 }
1169#endif
1170 d->doDelayedItemsLayout();
1171 d->updateGeometry();
1172}
1173
1174/*!
1175 Returns the model index of the model's root item. The root item is
1176 the parent item to the view's toplevel items. The root can be invalid.
1177
1178 \sa setRootIndex()
1179*/
1180QModelIndex QAbstractItemView::rootIndex() const
1181{
1182 return QModelIndex(d_func()->root);
1183}
1184
1185/*!
1186 Selects all items in the view.
1187 This function will use the selection behavior
1188 set on the view when selecting.
1189
1190 \sa setSelection(), selectedIndexes(), clearSelection()
1191*/
1192void QAbstractItemView::selectAll()
1193{
1194 Q_D(QAbstractItemView);
1195 const SelectionMode mode = d->selectionMode;
1196 switch (mode) {
1197 case MultiSelection:
1198 case ExtendedSelection:
1199 d->selectAll(QItemSelectionModel::ClearAndSelect
1200 | d->selectionBehaviorFlags());
1201 break;
1202 case NoSelection:
1203 case ContiguousSelection:
1204 if (d->model->hasChildren(d->root))
1205 d->selectAll(selectionCommand(d->model->index(0, 0, d->root)));
1206 break;
1207 case SingleSelection:
1208 break;
1209 }
1210}
1211
1212/*!
1213 Starts editing the item corresponding to the given \a index if it is
1214 editable.
1215
1216 Note that this function does not change the current index. Since the current
1217 index defines the next and previous items to edit, users may find that
1218 keyboard navigation does not work as expected. To provide consistent navigation
1219 behavior, call setCurrentIndex() before this function with the same model
1220 index.
1221
1222 \sa QModelIndex::flags()
1223*/
1224void QAbstractItemView::edit(const QModelIndex &index)
1225{
1226 Q_D(QAbstractItemView);
1227 if (Q_UNLIKELY(!d->isIndexValid(index)))
1228 qWarning("edit: index was invalid");
1229 if (Q_UNLIKELY(!edit(index, AllEditTriggers, nullptr)))
1230 qWarning("edit: editing failed");
1231}
1232
1233/*!
1234 Deselects all selected items. The current index will not be changed.
1235
1236 \sa setSelection(), selectAll()
1237*/
1238void QAbstractItemView::clearSelection()
1239{
1240 Q_D(QAbstractItemView);
1241 if (d->selectionModel)
1242 d->selectionModel->clearSelection();
1243}
1244
1245/*!
1246 \internal
1247
1248 This function is intended to lay out the items in the view.
1249 The default implementation just calls updateGeometries() and updates the viewport.
1250*/
1251void QAbstractItemView::doItemsLayout()
1252{
1253 Q_D(QAbstractItemView);
1254 d->interruptDelayedItemsLayout();
1255 updateGeometries();
1256 d->viewport->update();
1257}
1258
1259/*!
1260 \property QAbstractItemView::editTriggers
1261 \brief which actions will initiate item editing
1262
1263 This property is a selection of flags defined by
1264 \l{EditTrigger}, combined using the OR
1265 operator. The view will only initiate the editing of an item if the
1266 action performed is set in this property.
1267
1268 The default value is:
1269 \list
1270 \li for QTableView: DoubleClicked|AnyKeyPressed
1271 \li for all other views: DoubleClicked|EditKeyPressed
1272 \endlist
1273*/
1274void QAbstractItemView::setEditTriggers(EditTriggers actions)
1275{
1276 Q_D(QAbstractItemView);
1277 d->editTriggers = actions;
1278}
1279
1280QAbstractItemView::EditTriggers QAbstractItemView::editTriggers() const
1281{
1282 Q_D(const QAbstractItemView);
1283 return d->editTriggers;
1284}
1285
1286/*!
1287 \property QAbstractItemView::verticalScrollMode
1288 \brief how the view scrolls its contents in the vertical direction
1289
1290 This property controls how the view scroll its contents vertically.
1291 Scrolling can be done either per pixel or per item. Its default value
1292 comes from the style via the QStyle::SH_ItemView_ScrollMode style hint.
1293*/
1294
1295void QAbstractItemView::setVerticalScrollMode(ScrollMode mode)
1296{
1297 Q_D(QAbstractItemView);
1298 d->verticalScrollModeSet = true;
1299 if (mode == d->verticalScrollMode)
1300 return;
1301 QModelIndex topLeft = indexAt(QPoint(0, 0));
1302 d->verticalScrollMode = mode;
1303 if (mode == ScrollPerItem)
1304 verticalScrollBar()->d_func()->itemviewChangeSingleStep(1); // setSingleStep(-1) => step with 1
1305 else
1306 verticalScrollBar()->setSingleStep(-1); // Ensure that the view can update single step
1307 updateGeometries(); // update the scroll bars
1308 scrollTo(topLeft, QAbstractItemView::PositionAtTop);
1309}
1310
1311QAbstractItemView::ScrollMode QAbstractItemView::verticalScrollMode() const
1312{
1313 Q_D(const QAbstractItemView);
1314 return d->verticalScrollMode;
1315}
1316
1317void QAbstractItemView::resetVerticalScrollMode()
1318{
1319 auto sm = static_cast<ScrollMode>(style()->styleHint(QStyle::SH_ItemView_ScrollMode, nullptr, this, nullptr));
1320 setVerticalScrollMode(sm);
1321 d_func()->verticalScrollModeSet = false;
1322}
1323
1324/*!
1325 \property QAbstractItemView::horizontalScrollMode
1326 \brief how the view scrolls its contents in the horizontal direction
1327
1328 This property controls how the view scroll its contents horizontally.
1329 Scrolling can be done either per pixel or per item. Its default value
1330 comes from the style via the QStyle::SH_ItemView_ScrollMode style hint.
1331*/
1332
1333void QAbstractItemView::setHorizontalScrollMode(ScrollMode mode)
1334{
1335 Q_D(QAbstractItemView);
1336 d->horizontalScrollModeSet = true;
1337 if (mode == d->horizontalScrollMode)
1338 return;
1339 d->horizontalScrollMode = mode;
1340 if (mode == ScrollPerItem)
1341 horizontalScrollBar()->d_func()->itemviewChangeSingleStep(1); // setSingleStep(-1) => step with 1
1342 else
1343 horizontalScrollBar()->setSingleStep(-1); // Ensure that the view can update single step
1344 updateGeometries(); // update the scroll bars
1345}
1346
1347QAbstractItemView::ScrollMode QAbstractItemView::horizontalScrollMode() const
1348{
1349 Q_D(const QAbstractItemView);
1350 return d->horizontalScrollMode;
1351}
1352
1353void QAbstractItemView::resetHorizontalScrollMode()
1354{
1355 auto sm = static_cast<ScrollMode>(style()->styleHint(QStyle::SH_ItemView_ScrollMode, nullptr, this, nullptr));
1356 setHorizontalScrollMode(sm);
1357 d_func()->horizontalScrollModeSet = false;
1358}
1359
1360#if QT_CONFIG(draganddrop)
1361/*!
1362 \property QAbstractItemView::dragDropOverwriteMode
1363 \brief the view's drag and drop behavior
1364
1365 If its value is \c true, the selected data will overwrite the
1366 existing item data when dropped, while moving the data will clear
1367 the item. If its value is \c false, the selected data will be
1368 inserted as a new item when the data is dropped. When the data is
1369 moved, the item is removed as well.
1370
1371 The default value is \c false, as in the QListView and QTreeView
1372 subclasses. In the QTableView subclass, on the other hand, the
1373 property has been set to \c true.
1374
1375 Note: This is not intended to prevent overwriting of items.
1376 The model's implementation of flags() should do that by not
1377 returning Qt::ItemIsDropEnabled.
1378
1379 \sa dragDropMode
1380*/
1381void QAbstractItemView::setDragDropOverwriteMode(bool overwrite)
1382{
1383 Q_D(QAbstractItemView);
1384 d->overwrite = overwrite;
1385}
1386
1387bool QAbstractItemView::dragDropOverwriteMode() const
1388{
1389 Q_D(const QAbstractItemView);
1390 return d->overwrite;
1391}
1392#endif
1393
1394/*!
1395 \property QAbstractItemView::autoScroll
1396 \brief whether autoscrolling in drag move events is enabled
1397
1398 If this property is set to true (the default), the
1399 QAbstractItemView automatically scrolls the contents of the view
1400 if the user drags within 16 pixels of the viewport edge. If the current
1401 item changes, then the view will scroll automatically to ensure that the
1402 current item is fully visible.
1403
1404 This property only works if the viewport accepts drops. Autoscroll is
1405 switched off by setting this property to false.
1406*/
1407
1408void QAbstractItemView::setAutoScroll(bool enable)
1409{
1410 Q_D(QAbstractItemView);
1411 d->autoScroll = enable;
1412}
1413
1414bool QAbstractItemView::hasAutoScroll() const
1415{
1416 Q_D(const QAbstractItemView);
1417 return d->autoScroll;
1418}
1419
1420/*!
1421 \property QAbstractItemView::autoScrollMargin
1422 \brief the size of the area when auto scrolling is triggered
1423
1424 This property controls the size of the area at the edge of the viewport that
1425 triggers autoscrolling. The default value is 16 pixels.
1426*/
1427void QAbstractItemView::setAutoScrollMargin(int margin)
1428{
1429 Q_D(QAbstractItemView);
1430 d->autoScrollMargin = margin;
1431}
1432
1433int QAbstractItemView::autoScrollMargin() const
1434{
1435 Q_D(const QAbstractItemView);
1436 return d->autoScrollMargin;
1437}
1438
1439/*!
1440 \property QAbstractItemView::tabKeyNavigation
1441 \brief whether item navigation with tab and backtab is enabled.
1442*/
1443
1444void QAbstractItemView::setTabKeyNavigation(bool enable)
1445{
1446 Q_D(QAbstractItemView);
1447 d->tabKeyNavigation = enable;
1448}
1449
1450bool QAbstractItemView::tabKeyNavigation() const
1451{
1452 Q_D(const QAbstractItemView);
1453 return d->tabKeyNavigation;
1454}
1455
1456/*!
1457 \since 5.2
1458 \reimp
1459*/
1460QSize QAbstractItemView::viewportSizeHint() const
1461{
1462 return QAbstractScrollArea::viewportSizeHint();
1463}
1464
1465#if QT_CONFIG(draganddrop)
1466/*!
1467 \property QAbstractItemView::showDropIndicator
1468 \brief whether the drop indicator is shown when dragging items and dropping.
1469
1470 \sa dragEnabled, DragDropMode, dragDropOverwriteMode, acceptDrops
1471*/
1472
1473void QAbstractItemView::setDropIndicatorShown(bool enable)
1474{
1475 Q_D(QAbstractItemView);
1476 d->showDropIndicator = enable;
1477}
1478
1479bool QAbstractItemView::showDropIndicator() const
1480{
1481 Q_D(const QAbstractItemView);
1482 return d->showDropIndicator;
1483}
1484
1485/*!
1486 \property QAbstractItemView::dragEnabled
1487 \brief whether the view supports dragging of its own items
1488
1489 \sa showDropIndicator, DragDropMode, dragDropOverwriteMode, acceptDrops
1490*/
1491
1492void QAbstractItemView::setDragEnabled(bool enable)
1493{
1494 Q_D(QAbstractItemView);
1495 d->dragEnabled = enable;
1496}
1497
1498bool QAbstractItemView::dragEnabled() const
1499{
1500 Q_D(const QAbstractItemView);
1501 return d->dragEnabled;
1502}
1503
1504/*!
1505 \enum QAbstractItemView::DragDropMode
1506
1507 Describes the various drag and drop events the view can act upon.
1508 By default the view does not support dragging or dropping (\c
1509 NoDragDrop).
1510
1511 \value NoDragDrop Does not support dragging or dropping.
1512 \value DragOnly The view supports dragging of its own items
1513 \value DropOnly The view accepts drops
1514 \value DragDrop The view supports both dragging and dropping
1515 \value InternalMove The view accepts move (\b{not copy}) operations only
1516 from itself.
1517
1518 Note that the model used needs to provide support for drag and drop operations.
1519
1520 \sa setDragDropMode(), {Using drag and drop with item views}
1521*/
1522
1523/*!
1524 \property QAbstractItemView::dragDropMode
1525 \brief the drag and drop event the view will act upon
1526
1527 \sa showDropIndicator, dragDropOverwriteMode
1528*/
1529void QAbstractItemView::setDragDropMode(DragDropMode behavior)
1530{
1531 Q_D(QAbstractItemView);
1532 d->dragDropMode = behavior;
1533 setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove);
1534 setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove);
1535}
1536
1537QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const
1538{
1539 Q_D(const QAbstractItemView);
1540 DragDropMode setBehavior = d->dragDropMode;
1541 if (!dragEnabled() && !acceptDrops())
1542 return NoDragDrop;
1543
1544 if (dragEnabled() && !acceptDrops())
1545 return DragOnly;
1546
1547 if (!dragEnabled() && acceptDrops())
1548 return DropOnly;
1549
1550 if (dragEnabled() && acceptDrops()) {
1551 if (setBehavior == InternalMove)
1552 return setBehavior;
1553 else
1554 return DragDrop;
1555 }
1556
1557 return NoDragDrop;
1558}
1559
1560/*!
1561 \property QAbstractItemView::defaultDropAction
1562 \brief the drop action that will be used by default in QAbstractItemView::drag().
1563
1564 If the property is not set, the drop action is CopyAction when the supported
1565 actions support CopyAction.
1566
1567 \sa showDropIndicator, dragDropOverwriteMode
1568*/
1569void QAbstractItemView::setDefaultDropAction(Qt::DropAction dropAction)
1570{
1571 Q_D(QAbstractItemView);
1572 d->defaultDropAction = dropAction;
1573}
1574
1575Qt::DropAction QAbstractItemView::defaultDropAction() const
1576{
1577 Q_D(const QAbstractItemView);
1578 return d->defaultDropAction;
1579}
1580
1581#endif // QT_CONFIG(draganddrop)
1582
1583/*!
1584 \property QAbstractItemView::alternatingRowColors
1585 \brief whether to draw the background using alternating colors
1586
1587 If this property is \c true, the item background will be drawn using
1588 QPalette::Base and QPalette::AlternateBase; otherwise the background
1589 will be drawn using the QPalette::Base color.
1590
1591 By default, this property is \c false.
1592*/
1593void QAbstractItemView::setAlternatingRowColors(bool enable)
1594{
1595 Q_D(QAbstractItemView);
1596 d->alternatingColors = enable;
1597 if (isVisible())
1598 d->viewport->update();
1599}
1600
1601bool QAbstractItemView::alternatingRowColors() const
1602{
1603 Q_D(const QAbstractItemView);
1604 return d->alternatingColors;
1605}
1606
1607/*!
1608 \property QAbstractItemView::iconSize
1609 \brief the size of items' icons
1610
1611 Setting this property when the view is visible will cause the
1612 items to be laid out again.
1613*/
1614void QAbstractItemView::setIconSize(const QSize &size)
1615{
1616 Q_D(QAbstractItemView);
1617 if (size == d->iconSize)
1618 return;
1619 d->iconSize = size;
1620 d->doDelayedItemsLayout();
1621 emit iconSizeChanged(size);
1622}
1623
1624QSize QAbstractItemView::iconSize() const
1625{
1626 Q_D(const QAbstractItemView);
1627 return d->iconSize;
1628}
1629
1630/*!
1631 \property QAbstractItemView::textElideMode
1632
1633 \brief the position of the "..." in elided text.
1634
1635 The default value for all item views is Qt::ElideRight.
1636*/
1637void QAbstractItemView::setTextElideMode(Qt::TextElideMode mode)
1638{
1639 Q_D(QAbstractItemView);
1640 d->textElideMode = mode;
1641}
1642
1643Qt::TextElideMode QAbstractItemView::textElideMode() const
1644{
1645 return d_func()->textElideMode;
1646}
1647
1648/*!
1649 \reimp
1650*/
1651bool QAbstractItemView::focusNextPrevChild(bool next)
1652{
1653 Q_D(QAbstractItemView);
1654 if (d->tabKeyNavigation && isVisible() && isEnabled() && d->viewport->isEnabled()) {
1655 QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
1656 keyPressEvent(&event);
1657 if (event.isAccepted())
1658 return true;
1659 }
1660 return QAbstractScrollArea::focusNextPrevChild(next);
1661}
1662
1663/*!
1664 \reimp
1665*/
1666bool QAbstractItemView::event(QEvent *event)
1667{
1668 Q_D(QAbstractItemView);
1669 switch (event->type()) {
1670 case QEvent::Paint:
1671 //we call this here because the scrollbars' visibility might be altered
1672 //so this can't be done in the paintEvent method
1673 d->executePostedLayout(); //make sure we set the layout properly
1674 break;
1675 case QEvent::Show:
1676 d->executePostedLayout(); //make sure we set the layout properly
1677 if (d->shouldScrollToCurrentOnShow) {
1678 d->shouldScrollToCurrentOnShow = false;
1679 const QModelIndex current = currentIndex();
1680 if (current.isValid() && (d->state == QAbstractItemView::EditingState || d->autoScroll))
1681 scrollTo(current);
1682 }
1683 break;
1684 case QEvent::LocaleChange:
1685 viewport()->update();
1686 break;
1687 case QEvent::LayoutDirectionChange:
1688 case QEvent::ApplicationLayoutDirectionChange:
1689 updateGeometries();
1690 break;
1691 case QEvent::StyleChange:
1692 doItemsLayout();
1693 if (!d->verticalScrollModeSet)
1694 resetVerticalScrollMode();
1695 if (!d->horizontalScrollModeSet)
1696 resetHorizontalScrollMode();
1697 break;
1698 case QEvent::FocusOut:
1699 d->checkPersistentEditorFocus();
1700 break;
1701 case QEvent::FontChange:
1702 d->doDelayedItemsLayout(); // the size of the items will change
1703 break;
1704 default:
1705 break;
1706 }
1707 return QAbstractScrollArea::event(event);
1708}
1709
1710/*!
1711 \fn bool QAbstractItemView::viewportEvent(QEvent *event)
1712
1713 This function is used to handle tool tips, and What's
1714 This? mode, if the given \a event is a QEvent::ToolTip,or a
1715 QEvent::WhatsThis. It passes all other
1716 events on to its base class viewportEvent() handler.
1717
1718 Returns \c true if \a event has been recognized and processed; otherwise,
1719 returns \c false.
1720*/
1721bool QAbstractItemView::viewportEvent(QEvent *event)
1722{
1723 Q_D(QAbstractItemView);
1724 switch (event->type()) {
1725 case QEvent::Paint:
1726 // Similar to pre-painting in QAbstractItemView::event to update scrollbar
1727 // visibility, make sure that all pending layout requests have been executed
1728 // so that the view's data structures are up-to-date before rendering.
1729 d->executePostedLayout();
1730 break;
1731 case QEvent::HoverMove:
1732 case QEvent::HoverEnter:
1733 d->setHoverIndex(indexAt(static_cast<QHoverEvent*>(event)->position().toPoint()));
1734 break;
1735 case QEvent::HoverLeave:
1736 d->setHoverIndex(QModelIndex());
1737 break;
1738 case QEvent::Enter:
1739 d->viewportEnteredNeeded = true;
1740 break;
1741 case QEvent::Leave:
1742 d->setHoverIndex(QModelIndex()); // If we've left, no hover should be needed anymore
1743 #if QT_CONFIG(statustip)
1744 if (d->shouldClearStatusTip && d->parent) {
1745 QString empty;
1746 QStatusTipEvent tip(empty);
1747 QCoreApplication::sendEvent(d->parent, &tip);
1748 d->shouldClearStatusTip = false;
1749 }
1750 #endif
1751 d->enteredIndex = QModelIndex();
1752 break;
1753 case QEvent::ToolTip:
1754 case QEvent::QueryWhatsThis:
1755 case QEvent::WhatsThis: {
1756 QHelpEvent *he = static_cast<QHelpEvent*>(event);
1757 const QModelIndex index = indexAt(he->pos());
1758 QStyleOptionViewItem option;
1759 initViewItemOption(&option);
1760 option.rect = visualRect(index);
1761 option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
1762
1763 QAbstractItemDelegate *delegate = itemDelegateForIndex(index);
1764 if (!delegate)
1765 return false;
1766 return delegate->helpEvent(he, this, option, index);
1767 }
1768 case QEvent::FontChange:
1769 d->doDelayedItemsLayout(); // the size of the items will change
1770 break;
1771 case QEvent::WindowActivate:
1772 case QEvent::WindowDeactivate:
1773 d->viewport->update();
1774 break;
1775 case QEvent::ScrollPrepare:
1776 executeDelayedItemsLayout();
1777#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
1778 d->scollerConnection = QObjectPrivate::connect(
1779 QScroller::scroller(d->viewport), &QScroller::stateChanged,
1780 d, &QAbstractItemViewPrivate::scrollerStateChanged,
1781 Qt::UniqueConnection);
1782#endif
1783 break;
1784
1785 default:
1786 break;
1787 }
1788 return QAbstractScrollArea::viewportEvent(event);
1789}
1790
1791/*!
1792 This function is called with the given \a event when a mouse button is pressed
1793 while the cursor is inside the widget. If a valid item is pressed on it is made
1794 into the current item. This function emits the pressed() signal.
1795*/
1796void QAbstractItemView::mousePressEvent(QMouseEvent *event)
1797{
1798 Q_D(QAbstractItemView);
1799 d->releaseFromDoubleClick = false;
1800 d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling
1801 QPoint pos = event->position().toPoint();
1802 QPersistentModelIndex index = indexAt(pos);
1803
1804 // this is the mouse press event that closed the last editor (via focus event)
1805 d->pressClosedEditor = d->pressClosedEditorWatcher.isActive() && d->lastEditedIndex == index;
1806
1807 if (!d->selectionModel || (d->state == EditingState && d->hasEditor(index)))
1808 return;
1809
1810 d->pressedAlreadySelected = d->selectionModel->isSelected(index);
1811 d->pressedIndex = index;
1812 d->pressedModifiers = event->modifiers();
1813 QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
1814 d->noSelectionOnMousePress = command == QItemSelectionModel::NoUpdate || !index.isValid();
1815 QPoint offset = d->offset();
1816 d->draggedPosition = pos;
1817 d->draggedPositionOffset = offset;
1818
1819#if QT_CONFIG(draganddrop)
1820 // update the pressed position when drag was enable
1821 if (d->dragEnabled)
1822 d->pressedPosition = d->draggedPosition + d->draggedPositionOffset;
1823#endif
1824
1825 if (!(command & QItemSelectionModel::Current)) {
1826 d->pressedPosition = pos + offset;
1827 d->currentSelectionStartIndex = index;
1828 }
1829 else if (!d->currentSelectionStartIndex.isValid())
1830 d->currentSelectionStartIndex = currentIndex();
1831
1832 if (edit(index, NoEditTriggers, event))
1833 return;
1834
1835 if (index.isValid() && d->isIndexEnabled(index)) {
1836 // we disable scrollTo for mouse press so the item doesn't change position
1837 // when the user is interacting with it (ie. clicking on it)
1838 bool autoScroll = d->autoScroll;
1839 d->autoScroll = false;
1840 d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
1841 d->autoScroll = autoScroll;
1842 if (command.testFlag(QItemSelectionModel::Toggle)) {
1843 command &= ~QItemSelectionModel::Toggle;
1844 d->ctrlDragSelectionFlag = d->selectionModel->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
1845 command |= d->ctrlDragSelectionFlag;
1846 }
1847
1848 if (!(command & QItemSelectionModel::Current)) {
1849 setSelection(QRect(pos, QSize(1, 1)), command);
1850 } else {
1851 QRect rect(visualRect(d->currentSelectionStartIndex).center(), pos);
1852 setSelection(rect, command);
1853 }
1854
1855 // signal handlers may change the model
1856 emit pressed(index);
1857 if (d->autoScroll) {
1858 //we delay the autoscrolling to filter out double click event
1859 //100 is to be sure that there won't be a double-click misinterpreted as a 2 single clicks
1860 d->delayedAutoScroll.start(QApplication::doubleClickInterval()+100, this);
1861 }
1862
1863 } else {
1864 // Forces a finalize() even if mouse is pressed, but not on a item
1865 d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select);
1866 }
1867}
1868
1869/*!
1870 This function is called with the given \a event when a mouse move event is
1871 sent to the widget. If a selection is in progress and new items are moved
1872 over the selection is extended; if a drag is in progress it is continued.
1873*/
1874void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
1875{
1876 Q_D(QAbstractItemView);
1877 QPoint bottomRight = event->position().toPoint();
1878
1879 d->draggedPosition = bottomRight;
1880 d->draggedPositionOffset = d->offset();
1881
1882 if (state() == ExpandingState || state() == CollapsingState)
1883 return;
1884
1885#if QT_CONFIG(draganddrop)
1886 if (state() == DraggingState) {
1887 d->maybeStartDrag(bottomRight);
1888 return;
1889 }
1890#endif // QT_CONFIG(draganddrop)
1891
1892 QPersistentModelIndex index = indexAt(bottomRight);
1893 QModelIndex buddy = d->model->buddy(d->pressedIndex);
1894 if ((state() == EditingState && d->hasEditor(buddy))
1895 || edit(index, NoEditTriggers, event))
1896 return;
1897
1898 const QPoint topLeft =
1899 d->selectionMode != SingleSelection ? d->pressedPosition - d->offset() : bottomRight;
1900
1901 d->checkMouseMove(index);
1902
1903#if QT_CONFIG(draganddrop)
1904 if (d->pressedIndex.isValid()
1905 && d->dragEnabled
1906 && (state() != DragSelectingState)
1907 && (event->buttons() != Qt::NoButton)
1908 && !d->selectedDraggableIndexes().isEmpty()) {
1909 setState(DraggingState);
1910 d->maybeStartDrag(bottomRight);
1911 return;
1912 }
1913#endif
1914
1915 if ((event->buttons() & Qt::LeftButton) && d->selectionAllowed(index) && d->selectionModel) {
1916 setState(DragSelectingState);
1917 QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
1918 if (d->ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) {
1919 command &= ~QItemSelectionModel::Toggle;
1920 command |= d->ctrlDragSelectionFlag;
1921 }
1922
1923 // Do the normalize ourselves, since QRect::normalized() is flawed
1924 QRect selectionRect = QRect(topLeft, bottomRight);
1925 setSelection(selectionRect, command);
1926
1927 // set at the end because it might scroll the view
1928 if (index.isValid() && (index != d->selectionModel->currentIndex()) && d->isIndexEnabled(index))
1929 d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
1930 else if (d->shouldAutoScroll(event->pos()) && !d->autoScrollTimer.isActive())
1931 startAutoScroll();
1932 }
1933}
1934
1935/*!
1936 This function is called with the given \a event when a mouse button is released,
1937 after a mouse press event on the widget. If a user presses the mouse inside your
1938 widget and then drags the mouse to another location before releasing the mouse button,
1939 your widget receives the release event. The function will emit the clicked() signal if an
1940 item was being pressed.
1941*/
1942void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event)
1943{
1944 Q_D(QAbstractItemView);
1945 const bool releaseFromDoubleClick = d->releaseFromDoubleClick;
1946 d->releaseFromDoubleClick = false;
1947
1948 QPoint pos = event->position().toPoint();
1949 QPersistentModelIndex index = indexAt(pos);
1950
1951 if (state() == EditingState) {
1952 if (d->isIndexValid(index)
1953 && d->isIndexEnabled(index)
1954 && d->sendDelegateEvent(index, event))
1955 update(index);
1956 return;
1957 }
1958
1959 bool click = (index == d->pressedIndex && index.isValid() && !releaseFromDoubleClick);
1960 bool selectedClicked = click && d->pressedAlreadySelected
1961 && (event->button() == Qt::LeftButton)
1962 && (event->modifiers() == Qt::NoModifier);
1963 EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers);
1964 const bool edited = click && !d->pressClosedEditor ? edit(index, trigger, event) : false;
1965
1966 d->ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;
1967
1968 if (d->selectionModel && d->noSelectionOnMousePress) {
1969 d->noSelectionOnMousePress = false;
1970 if (!d->pressClosedEditor)
1971 d->selectionModel->select(index, selectionCommand(index, event));
1972 }
1973
1974 d->pressClosedEditor = false;
1975 setState(NoState);
1976
1977 if (click) {
1978 if (event->button() == Qt::LeftButton)
1979 emit clicked(index);
1980 if (edited)
1981 return;
1982 QStyleOptionViewItem option;
1983 initViewItemOption(&option);
1984 if (d->pressedAlreadySelected)
1985 option.state |= QStyle::State_Selected;
1986 if ((d->model->flags(index) & Qt::ItemIsEnabled)
1987 && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
1988 emit activated(index);
1989 }
1990}
1991
1992/*!
1993 This function is called with the given \a event when a mouse button is
1994 double clicked inside the widget. If the double-click is on a valid item it
1995 emits the doubleClicked() signal and calls edit() on the item.
1996*/
1997void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
1998{
1999 Q_D(QAbstractItemView);
2000
2001 QModelIndex index = indexAt(event->position().toPoint());
2002 if (!index.isValid()
2003 || !d->isIndexEnabled(index)
2004 || (d->pressedIndex != index)) {
2005 QMouseEvent me(QEvent::MouseButtonPress,
2006 event->position(), event->scenePosition(), event->globalPosition(),
2007 event->button(), event->buttons(), event->modifiers(),
2008 event->source(), event->pointingDevice());
2009 mousePressEvent(&me);
2010 return;
2011 }
2012 // signal handlers may change the model
2013 QPersistentModelIndex persistent = index;
2014 emit doubleClicked(persistent);
2015 if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
2016 && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
2017 emit activated(persistent);
2018 d->releaseFromDoubleClick = true;
2019}
2020
2021#if QT_CONFIG(draganddrop)
2022
2023/*!
2024 This function is called with the given \a event when a drag and drop operation enters
2025 the widget. If the drag is over a valid dropping place (e.g. over an item that
2026 accepts drops), the event is accepted; otherwise it is ignored.
2027
2028 \sa dropEvent(), startDrag()
2029*/
2030void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event)
2031{
2032 if (dragDropMode() == InternalMove
2033 && (event->source() != this|| !(event->possibleActions() & Qt::MoveAction)))
2034 return;
2035
2036 if (d_func()->canDrop(event)) {
2037 event->accept();
2038 setState(DraggingState);
2039 } else {
2040 event->ignore();
2041 }
2042}
2043
2044/*!
2045 This function is called continuously with the given \a event during a drag and
2046 drop operation over the widget. It can cause the view to scroll if, for example,
2047 the user drags a selection to view's right or bottom edge. In this case, the
2048 event will be accepted; otherwise it will be ignored.
2049
2050 \sa dropEvent(), startDrag()
2051*/
2052void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event)
2053{
2054 Q_D(QAbstractItemView);
2055 d->draggedPosition = event->position().toPoint();
2056 d->draggedPositionOffset = d->offset();
2057 if (dragDropMode() == InternalMove
2058 && (event->source() != this || !(event->possibleActions() & Qt::MoveAction)))
2059 return;
2060
2061 // ignore by default
2062 event->ignore();
2063
2064 QModelIndex index = indexAt(event->position().toPoint());
2065 d->hover = index;
2066 if (!d->droppingOnItself(event, index)
2067 && d->canDrop(event)) {
2068
2069 if (index.isValid() && d->showDropIndicator) {
2070 QRect rect = visualRect(index);
2071 d->dropIndicatorPosition = d->position(event->position().toPoint(), rect, index);
2072 if (d->selectionBehavior == QAbstractItemView::SelectRows
2073 && d->dropIndicatorPosition != OnViewport
2074 && (d->dropIndicatorPosition != OnItem || event->source() == this)) {
2075 if (index.column() > 0)
2076 rect = visualRect(index.siblingAtColumn(0));
2077 rect.setWidth(viewport()->width() - 1 - rect.x());
2078 }
2079 switch (d->dropIndicatorPosition) {
2080 case AboveItem:
2081 if (d->isIndexDropEnabled(index.parent())) {
2082 d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0);
2083 event->acceptProposedAction();
2084 } else {
2085 d->dropIndicatorRect = QRect();
2086 }
2087 break;
2088 case BelowItem:
2089 if (d->isIndexDropEnabled(index.parent())) {
2090 d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0);
2091 event->acceptProposedAction();
2092 } else {
2093 d->dropIndicatorRect = QRect();
2094 }
2095 break;
2096 case OnItem:
2097 if (d->isIndexDropEnabled(index)) {
2098 d->dropIndicatorRect = rect;
2099 event->acceptProposedAction();
2100 } else {
2101 d->dropIndicatorRect = QRect();
2102 }
2103 break;
2104 case OnViewport:
2105 d->dropIndicatorRect = QRect();
2106 if (d->isIndexDropEnabled(rootIndex())) {
2107 event->acceptProposedAction(); // allow dropping in empty areas
2108 }
2109 break;
2110 }
2111 } else {
2112 d->dropIndicatorRect = QRect();
2113 d->dropIndicatorPosition = OnViewport;
2114 if (d->isIndexDropEnabled(rootIndex())) {
2115 event->acceptProposedAction(); // allow dropping in empty areas
2116 }
2117 }
2118 d->viewport->update();
2119 } // can drop
2120
2121 if (d->shouldAutoScroll(event->position().toPoint()))
2122 startAutoScroll();
2123}
2124
2125/*!
2126 \internal
2127 Return true if this is a move from ourself and \a index is a child of the selection that
2128 is being moved.
2129 */
2130bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index)
2131{
2132 Q_Q(QAbstractItemView);
2133 Qt::DropAction dropAction = event->dropAction();
2134 if (q->dragDropMode() == QAbstractItemView::InternalMove)
2135 dropAction = Qt::MoveAction;
2136 if (event->source() == q
2137 && event->possibleActions() & Qt::MoveAction
2138 && dropAction == Qt::MoveAction) {
2139 QModelIndexList selectedIndexes = q->selectedIndexes();
2140 QModelIndex child = index;
2141 while (child.isValid() && child != root) {
2142 if (selectedIndexes.contains(child))
2143 return true;
2144 child = child.parent();
2145 }
2146 }
2147 return false;
2148}
2149
2150/*!
2151 \fn void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *event)
2152
2153 This function is called when the item being dragged leaves the view.
2154 The \a event describes the state of the drag and drop operation.
2155*/
2156void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *)
2157{
2158 Q_D(QAbstractItemView);
2159 stopAutoScroll();
2160 setState(NoState);
2161 d->hover = QModelIndex();
2162 d->viewport->update();
2163}
2164
2165/*!
2166 This function is called with the given \a event when a drop event occurs over
2167 the widget. If the model accepts the even position the drop event is accepted;
2168 otherwise it is ignored.
2169
2170 \sa startDrag()
2171*/
2172void QAbstractItemView::dropEvent(QDropEvent *event)
2173{
2174 Q_D(QAbstractItemView);
2175 if (dragDropMode() == InternalMove) {
2176 if (event->source() != this || !(event->possibleActions() & Qt::MoveAction))
2177 return;
2178 }
2179
2180 QModelIndex index;
2181 int col = -1;
2182 int row = -1;
2183 if (d->dropOn(event, &row, &col, &index)) {
2184 const Qt::DropAction action = dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction();
2185 if (d->model->dropMimeData(event->mimeData(), action, row, col, index)) {
2186 if (action != event->dropAction()) {
2187 event->setDropAction(action);
2188 event->accept();
2189 } else {
2190 event->acceptProposedAction();
2191 }
2192 }
2193 }
2194 stopAutoScroll();
2195 setState(NoState);
2196 d->viewport->update();
2197}
2198
2199/*!
2200 If the event hasn't already been accepted, determines the index to drop on.
2201
2202 if (row == -1 && col == -1)
2203 // append to this drop index
2204 else
2205 // place at row, col in drop index
2206
2207 If it returns \c true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop.
2208 \internal
2209 */
2210bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
2211{
2212 Q_Q(QAbstractItemView);
2213 if (event->isAccepted())
2214 return false;
2215
2216 QModelIndex index;
2217 // rootIndex() (i.e. the viewport) might be a valid index
2218 if (viewport->rect().contains(event->position().toPoint())) {
2219 index = q->indexAt(event->position().toPoint());
2220 if (!index.isValid())
2221 index = root;
2222 }
2223
2224 // If we are allowed to do the drop
2225 if (model->supportedDropActions() & event->dropAction()) {
2226 int row = -1;
2227 int col = -1;
2228 if (index != root) {
2229 dropIndicatorPosition = position(event->position().toPoint(), q->visualRect(index), index);
2230 switch (dropIndicatorPosition) {
2231 case QAbstractItemView::AboveItem:
2232 row = index.row();
2233 col = index.column();
2234 index = index.parent();
2235 break;
2236 case QAbstractItemView::BelowItem:
2237 row = index.row() + 1;
2238 col = index.column();
2239 index = index.parent();
2240 break;
2241 case QAbstractItemView::OnItem:
2242 case QAbstractItemView::OnViewport:
2243 break;
2244 }
2245 } else {
2246 dropIndicatorPosition = QAbstractItemView::OnViewport;
2247 }
2248 *dropIndex = index;
2249 *dropRow = row;
2250 *dropCol = col;
2251 if (!droppingOnItself(event, index))
2252 return true;
2253 }
2254 return false;
2255}
2256
2257QAbstractItemView::DropIndicatorPosition
2258QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const
2259{
2260 QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
2261 if (!overwrite) {
2262 const int margin = qBound(2, qRound(qreal(rect.height()) / 5.5), 12);
2263 if (pos.y() - rect.top() < margin) {
2264 r = QAbstractItemView::AboveItem;
2265 } else if (rect.bottom() - pos.y() < margin) {
2266 r = QAbstractItemView::BelowItem;
2267 } else if (rect.contains(pos, true)) {
2268 r = QAbstractItemView::OnItem;
2269 }
2270 } else {
2271 QRect touchingRect = rect;
2272 touchingRect.adjust(-1, -1, 1, 1);
2273 if (touchingRect.contains(pos, false)) {
2274 r = QAbstractItemView::OnItem;
2275 }
2276 }
2277
2278 if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled)))
2279 r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
2280
2281 return r;
2282}
2283
2284#endif // QT_CONFIG(draganddrop)
2285
2286/*!
2287 This function is called with the given \a event when the widget obtains the focus.
2288 By default, the event is ignored.
2289
2290 \sa setFocus(), focusOutEvent()
2291*/
2292void QAbstractItemView::focusInEvent(QFocusEvent *event)
2293{
2294 Q_D(QAbstractItemView);
2295 QAbstractScrollArea::focusInEvent(event);
2296
2297 const QItemSelectionModel* model = selectionModel();
2298 bool currentIndexValid = currentIndex().isValid();
2299
2300 if (model
2301 && !d->currentIndexSet
2302 && !currentIndexValid) {
2303 bool autoScroll = d->autoScroll;
2304 d->autoScroll = false;
2305 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
2306 if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) {
2307 selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
2308 currentIndexValid = true;
2309 }
2310 d->autoScroll = autoScroll;
2311 }
2312
2313 if (model && currentIndexValid)
2314 setAttribute(Qt::WA_InputMethodEnabled, (currentIndex().flags() & Qt::ItemIsEditable));
2315 else if (!currentIndexValid)
2316 setAttribute(Qt::WA_InputMethodEnabled, false);
2317
2318 d->viewport->update();
2319}
2320
2321/*!
2322 This function is called with the given \a event when the widget
2323 loses the focus. By default, the event is ignored.
2324
2325 \sa clearFocus(), focusInEvent()
2326*/
2327void QAbstractItemView::focusOutEvent(QFocusEvent *event)
2328{
2329 Q_D(QAbstractItemView);
2330 QAbstractScrollArea::focusOutEvent(event);
2331 d->viewport->update();
2332}
2333
2334/*!
2335 This function is called with the given \a event when a key event is sent to
2336 the widget. The default implementation handles basic cursor movement, e.g. Up,
2337 Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is
2338 emitted if the current index is valid and the activation key is pressed
2339 (e.g. Enter or Return, depending on the platform).
2340 This function is where editing is initiated by key press, e.g. if F2 is
2341 pressed.
2342
2343 \sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation
2344*/
2345void QAbstractItemView::keyPressEvent(QKeyEvent *event)
2346{
2347 Q_D(QAbstractItemView);
2348 d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling
2349
2350#ifdef QT_KEYPAD_NAVIGATION
2351 switch (event->key()) {
2352 case Qt::Key_Select:
2353 if (QApplicationPrivate::keypadNavigationEnabled()) {
2354 if (!hasEditFocus()) {
2355 setEditFocus(true);
2356 return;
2357 }
2358 }
2359 break;
2360 case Qt::Key_Back:
2361 if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()) {
2362 setEditFocus(false);
2363 } else {
2364 event->ignore();
2365 }
2366 return;
2367 case Qt::Key_Down:
2368 case Qt::Key_Up:
2369 // Let's ignore vertical navigation events, only if there is no other widget
2370 // what can take the focus in vertical direction. This means widget can handle navigation events
2371 // even the widget don't have edit focus, and there is no other widget in requested direction.
2372 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()
2373 && QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) {
2374 event->ignore();
2375 return;
2376 }
2377 break;
2378 case Qt::Key_Left:
2379 case Qt::Key_Right:
2380 // Similar logic as in up and down events
2381 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()
2382 && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this))) {
2383 event->ignore();
2384 return;
2385 }
2386 break;
2387 default:
2388 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
2389 event->ignore();
2390 return;
2391 }
2392 }
2393#endif
2394
2395#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT)
2396 if (event == QKeySequence::Copy) {
2397 const QModelIndex index = currentIndex();
2398 if (index.isValid() && d->model) {
2399 const QVariant variant = d->model->data(index, Qt::DisplayRole);
2400 if (variant.canConvert<QString>())
2401 QGuiApplication::clipboard()->setText(variant.toString());
2402 }
2403 event->accept();
2404 }
2405#endif
2406
2407 QPersistentModelIndex newCurrent;
2408 d->moveCursorUpdatedView = false;
2409 switch (event->key()) {
2410 case Qt::Key_Down:
2411 newCurrent = moveCursor(MoveDown, event->modifiers());
2412 break;
2413 case Qt::Key_Up:
2414 newCurrent = moveCursor(MoveUp, event->modifiers());
2415 break;
2416 case Qt::Key_Left:
2417 newCurrent = moveCursor(MoveLeft, event->modifiers());
2418 break;
2419 case Qt::Key_Right:
2420 newCurrent = moveCursor(MoveRight, event->modifiers());
2421 break;
2422 case Qt::Key_Home:
2423 newCurrent = moveCursor(MoveHome, event->modifiers());
2424 break;
2425 case Qt::Key_End:
2426 newCurrent = moveCursor(MoveEnd, event->modifiers());
2427 break;
2428 case Qt::Key_PageUp:
2429 newCurrent = moveCursor(MovePageUp, event->modifiers());
2430 break;
2431 case Qt::Key_PageDown:
2432 newCurrent = moveCursor(MovePageDown, event->modifiers());
2433 break;
2434 case Qt::Key_Tab:
2435 if (d->tabKeyNavigation)
2436 newCurrent = moveCursor(MoveNext, event->modifiers());
2437 break;
2438 case Qt::Key_Backtab:
2439 if (d->tabKeyNavigation)
2440 newCurrent = moveCursor(MovePrevious, event->modifiers());
2441 break;
2442 }
2443
2444 QPersistentModelIndex oldCurrent = currentIndex();
2445 if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) {
2446 if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent))
2447 setFocus();
2448 QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event);
2449 if (command != QItemSelectionModel::NoUpdate
2450 || style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, nullptr, this)) {
2451 // note that we don't check if the new current index is enabled because moveCursor() makes sure it is
2452 if (command & QItemSelectionModel::Current) {
2453 d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
2454 if (!d->currentSelectionStartIndex.isValid())
2455 d->currentSelectionStartIndex = oldCurrent;
2456 QRect rect(visualRect(d->currentSelectionStartIndex).center(), visualRect(newCurrent).center());
2457 setSelection(rect, command);
2458 } else {
2459 d->selectionModel->setCurrentIndex(newCurrent, command);
2460 d->currentSelectionStartIndex = newCurrent;
2461 if (newCurrent.isValid()) {
2462 // We copy the same behaviour as for mousePressEvent().
2463 QRect rect(visualRect(newCurrent).center(), QSize(1, 1));
2464 setSelection(rect, command);
2465 }
2466 }
2467 event->accept();
2468 return;
2469 }
2470 }
2471
2472 switch (event->key()) {
2473 // ignored keys
2474 case Qt::Key_Down:
2475 case Qt::Key_Up:
2476#ifdef QT_KEYPAD_NAVIGATION
2477 if (QApplicationPrivate::keypadNavigationEnabled()
2478 && QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) {
2479 event->accept(); // don't change focus
2480 break;
2481 }
2482#endif
2483 case Qt::Key_Left:
2484 case Qt::Key_Right:
2485#ifdef QT_KEYPAD_NAVIGATION
2486 if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
2487 && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal)
2488 || (QWidgetPrivate::inTabWidget(this) && d->model->columnCount(d->root) > 1))) {
2489 event->accept(); // don't change focus
2490 break;
2491 }
2492#endif // QT_KEYPAD_NAVIGATION
2493 case Qt::Key_Home:
2494 case Qt::Key_End:
2495 case Qt::Key_PageUp:
2496 case Qt::Key_PageDown:
2497 case Qt::Key_Escape:
2498 case Qt::Key_Shift:
2499 case Qt::Key_Control:
2500 case Qt::Key_Delete:
2501 case Qt::Key_Backspace:
2502 event->ignore();
2503 break;
2504 case Qt::Key_Space:
2505 case Qt::Key_Select:
2506 if (!edit(currentIndex(), AnyKeyPressed, event)) {
2507 if (d->selectionModel)
2508 d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event));
2509 if (event->key() == Qt::Key_Space) {
2510 keyboardSearch(event->text());
2511 event->accept();
2512 }
2513 }
2514#ifdef QT_KEYPAD_NAVIGATION
2515 if ( event->key()==Qt::Key_Select ) {
2516 // Also do Key_Enter action.
2517 if (currentIndex().isValid()) {
2518 if (state() != EditingState)
2519 emit activated(currentIndex());
2520 } else {
2521 event->ignore();
2522 }
2523 }
2524#endif
2525 break;
2526#ifdef Q_OS_MACOS
2527 case Qt::Key_Enter:
2528 case Qt::Key_Return:
2529 // Propagate the enter if you couldn't edit the item and there are no
2530 // current editors (if there are editors, the event was most likely propagated from it).
2531 if (!edit(currentIndex(), EditKeyPressed, event) && d->editorIndexHash.isEmpty())
2532 event->ignore();
2533 break;
2534#else
2535 case Qt::Key_F2:
2536 if (!edit(currentIndex(), EditKeyPressed, event))
2537 event->ignore();
2538 break;
2539 case Qt::Key_Enter:
2540 case Qt::Key_Return:
2541 // ### we can't open the editor on enter, because
2542 // some widgets will forward the enter event back
2543 // to the viewport, starting an endless loop
2544 if (state() != EditingState || hasFocus()) {
2545 if (currentIndex().isValid())
2546 emit activated(currentIndex());
2547 event->ignore();
2548 }
2549 break;
2550#endif
2551 default: {
2552#ifndef QT_NO_SHORTCUT
2553 if (event == QKeySequence::SelectAll && selectionMode() != NoSelection) {
2554 selectAll();
2555 break;
2556 }
2557#endif
2558#ifdef Q_OS_MACOS
2559 if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) {
2560 emit activated(currentIndex());
2561 break;
2562 }
2563#endif
2564 bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
2565 if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) {
2566 keyboardSearch(event->text());
2567 event->accept();
2568 } else {
2569 event->ignore();
2570 }
2571 break; }
2572 }
2573 if (d->moveCursorUpdatedView)
2574 event->accept();
2575}
2576
2577/*!
2578 This function is called with the given \a event when a resize event is sent to
2579 the widget.
2580
2581 \sa QWidget::resizeEvent()
2582*/
2583void QAbstractItemView::resizeEvent(QResizeEvent *event)
2584{
2585 QAbstractScrollArea::resizeEvent(event);
2586 updateGeometries();
2587}
2588
2589/*!
2590 This function is called with the given \a event when a timer event is sent
2591 to the widget.
2592
2593 \sa QObject::timerEvent()
2594*/
2595void QAbstractItemView::timerEvent(QTimerEvent *event)
2596{
2597 Q_D(QAbstractItemView);
2598 if (event->timerId() == d->fetchMoreTimer.timerId())
2599 d->fetchMore();
2600 else if (event->timerId() == d->delayedReset.timerId())
2601 reset();
2602 else if (event->timerId() == d->autoScrollTimer.timerId())
2603 doAutoScroll();
2604 else if (event->timerId() == d->updateTimer.timerId())
2605 d->updateDirtyRegion();
2606 else if (event->timerId() == d->delayedEditing.timerId()) {
2607 d->delayedEditing.stop();
2608 edit(currentIndex());
2609 } else if (event->timerId() == d->delayedLayout.timerId()) {
2610 d->delayedLayout.stop();
2611 if (isVisible()) {
2612 d->interruptDelayedItemsLayout();
2613 doItemsLayout();
2614 const QModelIndex current = currentIndex();
2615 if (current.isValid() && d->state == QAbstractItemView::EditingState)
2616 scrollTo(current);
2617 }
2618 } else if (event->timerId() == d->delayedAutoScroll.timerId()) {
2619 d->delayedAutoScroll.stop();
2620 //end of the timer: if the current item is still the same as the one when the mouse press occurred
2621 //we only get here if there was no double click
2622 if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex())
2623 scrollTo(d->pressedIndex);
2624 } else if (event->timerId() == d->pressClosedEditorWatcher.timerId()) {
2625 d->pressClosedEditorWatcher.stop();
2626 }
2627}
2628
2629/*!
2630 \reimp
2631*/
2632void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event)
2633{
2634 Q_D(QAbstractItemView);
2635 // When QAbstractItemView::AnyKeyPressed is used, a new IM composition might
2636 // start before the editor widget acquires focus. Changing focus would interrupt
2637 // the composition, so we keep focus on the view until that first composition
2638 // is complete, and pass QInputMethoEvents on to the editor widget so that the
2639 // user gets the expected feedback. See also inputMethodQuery, which redirects
2640 // calls to the editor widget during that period.
2641 bool forwardEventToEditor = false;
2642 const bool commit = !event->commitString().isEmpty();
2643 const bool preediting = !event->preeditString().isEmpty();
2644 if (QWidget *currentEditor = d->editorForIndex(currentIndex()).widget) {
2645 if (d->waitForIMCommit) {
2646 if (commit || !preediting) {
2647 // commit or cancel
2648 d->waitForIMCommit = false;
2649 QApplication::sendEvent(currentEditor, event);
2650 if (!commit) {
2651 QAbstractItemDelegate *delegate = itemDelegateForIndex(currentIndex());
2652 if (delegate)
2653 delegate->setEditorData(currentEditor, currentIndex());
2654 d->selectAllInEditor(currentEditor);
2655 }
2656 if (currentEditor->focusPolicy() != Qt::NoFocus)
2657 currentEditor->setFocus();
2658 } else {
2659 // more pre-editing
2660 QApplication::sendEvent(currentEditor, event);
2661 }
2662 return;
2663 }
2664 } else if (preediting) {
2665 // don't set focus when the editor opens
2666 d->waitForIMCommit = true;
2667 // but pass preedit on to editor
2668 forwardEventToEditor = true;
2669 } else if (!commit) {
2670 event->ignore();
2671 return;
2672 }
2673 if (!edit(currentIndex(), AnyKeyPressed, event)) {
2674 d->waitForIMCommit = false;
2675 if (commit)
2676 keyboardSearch(event->commitString());
2677 event->ignore();
2678 } else if (QWidget *currentEditor; forwardEventToEditor
2679 && (currentEditor = d->editorForIndex(currentIndex()).widget)) {
2680 QApplication::sendEvent(currentEditor, event);
2681 }
2682}
2683
2684#if QT_CONFIG(draganddrop)
2685/*!
2686 \enum QAbstractItemView::DropIndicatorPosition
2687
2688 This enum indicates the position of the drop indicator in
2689 relation to the index at the current mouse position:
2690
2691 \value OnItem The item will be dropped on the index.
2692
2693 \value AboveItem The item will be dropped above the index.
2694
2695 \value BelowItem The item will be dropped below the index.
2696
2697 \value OnViewport The item will be dropped onto a region of the viewport with
2698 no items. The way each view handles items dropped onto the viewport depends on
2699 the behavior of the underlying model in use.
2700*/
2701
2702
2703/*!
2704 Returns the position of the drop indicator in relation to the closest item.
2705*/
2706QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const
2707{
2708 Q_D(const QAbstractItemView);
2709 return d->dropIndicatorPosition;
2710}
2711#endif
2712
2713/*!
2714 This convenience function returns a list of all selected and
2715 non-hidden item indexes in the view. The list contains no
2716 duplicates, and is not sorted.
2717
2718 \sa QItemSelectionModel::selectedIndexes()
2719*/
2720QModelIndexList QAbstractItemView::selectedIndexes() const
2721{
2722 Q_D(const QAbstractItemView);
2723 QModelIndexList indexes;
2724 if (d->selectionModel) {
2725 indexes = d->selectionModel->selectedIndexes();
2726 auto isHidden = [this](const QModelIndex &idx) {
2727 return isIndexHidden(idx);
2728 };
2729 indexes.removeIf(isHidden);
2730 }
2731 return indexes;
2732}
2733
2734/*!
2735 Starts editing the item at \a index, creating an editor if
2736 necessary, and returns \c true if the view's \l{State} is now
2737 EditingState; otherwise returns \c false.
2738
2739 The action that caused the editing process is described by
2740 \a trigger, and the associated event is specified by \a event.
2741
2742 Editing can be forced by specifying the \a trigger to be
2743 QAbstractItemView::AllEditTriggers.
2744
2745 \sa closeEditor()
2746*/
2747bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
2748{
2749 Q_D(QAbstractItemView);
2750
2751 if (!d->isIndexValid(index))
2752 return false;
2753
2754 if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(nullptr) : d->editorForIndex(index).widget.data())) {
2755 if (w->focusPolicy() == Qt::NoFocus)
2756 return false;
2757 if (!d->waitForIMCommit)
2758 w->setFocus();
2759 else
2760 updateMicroFocus();
2761 return true;
2762 }
2763
2764 if (trigger == DoubleClicked) {
2765 d->delayedEditing.stop();
2766 d->delayedAutoScroll.stop();
2767 } else if (trigger == CurrentChanged) {
2768 d->delayedEditing.stop();
2769 }
2770
2771 // in case e.g. setData() triggers a reset()
2772 QPersistentModelIndex safeIndex(index);
2773
2774 if (d->sendDelegateEvent(index, event)) {
2775 update(safeIndex);
2776 return true;
2777 }
2778
2779 if (!safeIndex.isValid()) {
2780 return false;
2781 }
2782
2783 // save the previous trigger before updating
2784 EditTriggers lastTrigger = d->lastTrigger;
2785 d->lastTrigger = trigger;
2786
2787 if (!d->shouldEdit(trigger, d->model->buddy(safeIndex)))
2788 return false;
2789
2790 if (d->delayedEditing.isActive())
2791 return false;
2792
2793 // we will receive a mouseButtonReleaseEvent after a
2794 // mouseDoubleClickEvent, so we need to check the previous trigger
2795 if (lastTrigger == DoubleClicked && trigger == SelectedClicked)
2796 return false;
2797
2798 // we may get a double click event later
2799 if (trigger == SelectedClicked)
2800 d->delayedEditing.start(QApplication::doubleClickInterval(), this);
2801 else
2802 d->openEditor(safeIndex, d->shouldForwardEvent(trigger, event) ? event : nullptr);
2803
2804 return true;
2805}
2806
2807/*!
2808 \internal
2809 Updates the data shown in the open editor widgets in the view.
2810*/
2811void QAbstractItemView::updateEditorData()
2812{
2813 Q_D(QAbstractItemView);
2814 d->updateEditorData(QModelIndex(), QModelIndex());
2815}
2816
2817/*!
2818 \internal
2819 Updates the geometry of the open editor widgets in the view.
2820*/
2821void QAbstractItemView::updateEditorGeometries()
2822{
2823 Q_D(QAbstractItemView);
2824 if (d->editorIndexHash.isEmpty())
2825 return;
2826 if (d->delayedPendingLayout) {
2827 // doItemsLayout() will end up calling this function again
2828 d->executePostedLayout();
2829 return;
2830 }
2831 QStyleOptionViewItem option;
2832 initViewItemOption(&option);
2833 QEditorIndexHash::iterator it = d->editorIndexHash.begin();
2834 QWidgetList editorsToRelease;
2835 QWidgetList editorsToHide;
2836 while (it != d->editorIndexHash.end()) {
2837 QModelIndex index = it.value();
2838 QWidget *editor = it.key();
2839 if (index.isValid() && editor) {
2840 option.rect = visualRect(index);
2841 if (option.rect.isValid()) {
2842 editor->show();
2843 QAbstractItemDelegate *delegate = itemDelegateForIndex(index);
2844 if (delegate)
2845 delegate->updateEditorGeometry(editor, option, index);
2846 } else {
2847 editorsToHide << editor;
2848 }
2849 ++it;
2850 } else {
2851 d->indexEditorHash.remove(it.value());
2852 it = d->editorIndexHash.erase(it);
2853 editorsToRelease << editor;
2854 }
2855 }
2856
2857 //we hide and release the editor outside of the loop because it might change the focus and try
2858 //to change the editors hashes.
2859 for (int i = 0; i < editorsToHide.size(); ++i) {
2860 editorsToHide.at(i)->hide();
2861 }
2862 for (int i = 0; i < editorsToRelease.size(); ++i) {
2863 d->releaseEditor(editorsToRelease.at(i));
2864 }
2865}
2866
2867/*!
2868 Updates the geometry of the child widgets of the view.
2869*/
2870void QAbstractItemView::updateGeometries()
2871{
2872 Q_D(QAbstractItemView);
2873 updateEditorGeometries();
2874 d->fetchMoreTimer.start(0, this); //fetch more later
2875 d->updateGeometry();
2876}
2877
2878/*!
2879 \internal
2880*/
2881void QAbstractItemView::verticalScrollbarValueChanged(int value)
2882{
2883 Q_D(QAbstractItemView);
2884 if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
2885 d->model->fetchMore(d->root);
2886 QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
2887 if (viewport()->rect().contains(posInVp))
2888 d->checkMouseMove(posInVp);
2889}
2890
2891/*!
2892 \internal
2893*/
2894void QAbstractItemView::horizontalScrollbarValueChanged(int value)
2895{
2896 Q_D(QAbstractItemView);
2897 if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
2898 d->model->fetchMore(d->root);
2899 QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
2900 if (viewport()->rect().contains(posInVp))
2901 d->checkMouseMove(posInVp);
2902}
2903
2904/*!
2905 \internal
2906*/
2907void QAbstractItemView::verticalScrollbarAction(int)
2908{
2909 //do nothing
2910}
2911
2912/*!
2913 \internal
2914*/
2915void QAbstractItemView::horizontalScrollbarAction(int)
2916{
2917 //do nothing
2918}
2919
2920/*!
2921 Closes the given \a editor, and releases it. The \a hint is
2922 used to specify how the view should respond to the end of the editing
2923 operation. For example, the hint may indicate that the next item in
2924 the view should be opened for editing.
2925
2926 \sa edit(), commitData()
2927*/
2928
2929void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
2930{
2931 Q_D(QAbstractItemView);
2932
2933 // Close the editor
2934 if (editor) {
2935 const bool isPersistent = d->persistent.contains(editor);
2936 const QModelIndex index = d->indexForEditor(editor);
2937 if (!index.isValid()) {
2938 if (!editor->isVisible()) {
2939 // The commit might have removed the index (e.g. it might get filtered), in
2940 // which case the editor is already hidden and scheduled for deletion. We
2941 // don't have to do anything, except reset the state, and continue with
2942 // EndEditHint processing.
2943 if (!isPersistent)
2944 setState(NoState);
2945 } else {
2946 qWarning("QAbstractItemView::closeEditor called with an editor that does not belong to this view");
2947 return;
2948 }
2949 } else {
2950 const bool hadFocus = editor->hasFocus();
2951 // start a timer that expires immediately when we return to the event loop
2952 // to identify whether this close was triggered by a mousepress-initiated
2953 // focus event
2954 d->pressClosedEditorWatcher.start(0, this);
2955 d->lastEditedIndex = index;
2956
2957 if (!isPersistent) {
2958 setState(NoState);
2959 QModelIndex index = d->indexForEditor(editor);
2960 editor->removeEventFilter(itemDelegateForIndex(index));
2961 d->removeEditor(editor);
2962 }
2963 if (hadFocus) {
2964 if (focusPolicy() != Qt::NoFocus)
2965 setFocus(); // this will send a focusLost event to the editor
2966 else
2967 editor->clearFocus();
2968 } else {
2969 d->checkPersistentEditorFocus();
2970 }
2971
2972 QPointer<QWidget> ed = editor;
2973 QCoreApplication::sendPostedEvents(editor, 0);
2974 editor = ed;
2975
2976 if (!isPersistent && editor)
2977 d->releaseEditor(editor, index);
2978 }
2979 }
2980
2981 // The EndEditHint part
2982 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::NoUpdate;
2983 if (d->selectionMode != NoSelection)
2984 flags = QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
2985 switch (hint) {
2986 case QAbstractItemDelegate::EditNextItem: {
2987 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier);
2988 if (index.isValid()) {
2989 QPersistentModelIndex persistent(index);
2990 d->selectionModel->setCurrentIndex(persistent, flags);
2991 // currentChanged signal would have already started editing
2992 if (index.flags() & Qt::ItemIsEditable
2993 && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
2994 edit(persistent);
2995 } break; }
2996 case QAbstractItemDelegate::EditPreviousItem: {
2997 QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier);
2998 if (index.isValid()) {
2999 QPersistentModelIndex persistent(index);
3000 d->selectionModel->setCurrentIndex(persistent, flags);
3001 // currentChanged signal would have already started editing
3002 if (index.flags() & Qt::ItemIsEditable
3003 && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
3004 edit(persistent);
3005 } break; }
3006 case QAbstractItemDelegate::SubmitModelCache:
3007 d->model->submit();
3008 break;
3009 case QAbstractItemDelegate::RevertModelCache:
3010 d->model->revert();
3011 break;
3012 default:
3013 break;
3014 }
3015}
3016
3017/*!
3018 Commit the data in the \a editor to the model.
3019
3020 \sa closeEditor()
3021*/
3022void QAbstractItemView::commitData(QWidget *editor)
3023{
3024 Q_D(QAbstractItemView);
3025 if (!editor || !d->itemDelegate || d->currentlyCommittingEditor)
3026 return;
3027 QModelIndex index = d->indexForEditor(editor);
3028 if (!index.isValid()) {
3029 qWarning("QAbstractItemView::commitData called with an editor that does not belong to this view");
3030 return;
3031 }
3032 d->currentlyCommittingEditor = editor;
3033 QAbstractItemDelegate *delegate = itemDelegateForIndex(index);
3034 editor->removeEventFilter(delegate);
3035 delegate->setModelData(editor, d->model, index);
3036 editor->installEventFilter(delegate);
3037 d->currentlyCommittingEditor = nullptr;
3038}
3039
3040/*!
3041 This function is called when the given \a editor has been destroyed.
3042
3043 \sa closeEditor()
3044*/
3045void QAbstractItemView::editorDestroyed(QObject *editor)
3046{
3047 Q_D(QAbstractItemView);
3048 QWidget *w = qobject_cast<QWidget*>(editor);
3049 d->removeEditor(w);
3050 d->persistent.remove(w);
3051 if (state() == EditingState)
3052 setState(NoState);
3053}
3054
3055
3056
3057/*!
3058 Moves to and selects the item best matching the string \a search.
3059 If no item is found nothing happens.
3060
3061 In the default implementation, the search is reset if \a search is empty, or
3062 the time interval since the last search has exceeded
3063 QApplication::keyboardInputInterval().
3064*/
3065void QAbstractItemView::keyboardSearch(const QString &search)
3066{
3067 Q_D(QAbstractItemView);
3068 if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
3069 return;
3070
3071 QModelIndex start = currentIndex().isValid() ? currentIndex()
3072 : d->model->index(0, 0, d->root);
3073 bool skipRow = false;
3074 bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
3075 qint64 keyboardInputTimeElapsed;
3076 if (keyboardTimeWasValid)
3077 keyboardInputTimeElapsed = d->keyboardInputTime.restart();
3078 else
3079 d->keyboardInputTime.start();
3080 if (search.isEmpty() || !keyboardTimeWasValid
3081 || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
3082 d->keyboardInput = search;
3083 skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
3084 } else {
3085 d->keyboardInput += search;
3086 }
3087
3088 // special case for searches with same key like 'aaaaa'
3089 bool sameKey = false;
3090 if (d->keyboardInput.size() > 1) {
3091 int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.size() - 1));
3092 sameKey = (c == d->keyboardInput.size());
3093 if (sameKey)
3094 skipRow = true;
3095 }
3096
3097 // skip if we are searching for the same key or a new search started
3098 if (skipRow) {
3099 QModelIndex parent = start.parent();
3100 int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0;
3101 start = d->model->index(newRow, start.column(), parent);
3102 }
3103
3104 // search from start with wraparound
3105 QModelIndex current = start;
3106 QModelIndexList match;
3107 QModelIndex firstMatch;
3108 QModelIndex startMatch;
3109 QModelIndexList previous;
3110 do {
3111 match = d->model->match(current, Qt::DisplayRole, d->keyboardInput);
3112 if (match == previous)
3113 break;
3114 firstMatch = match.value(0);
3115 previous = match;
3116 if (firstMatch.isValid()) {
3117 if (d->isIndexEnabled(firstMatch)) {
3118 setCurrentIndex(firstMatch);
3119 break;
3120 }
3121 int row = firstMatch.row() + 1;
3122 if (row >= d->model->rowCount(firstMatch.parent()))
3123 row = 0;
3124 current = firstMatch.sibling(row, firstMatch.column());
3125
3126 //avoid infinite loop if all the matching items are disabled.
3127 if (!startMatch.isValid())
3128 startMatch = firstMatch;
3129 else if (startMatch == firstMatch)
3130 break;
3131 }
3132 } while (current != start && firstMatch.isValid());
3133}
3134
3135/*!
3136 Returns the size hint for the item with the specified \a index or
3137 an invalid size for invalid indexes.
3138
3139 \sa sizeHintForRow(), sizeHintForColumn()
3140*/
3141QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const
3142{
3143 Q_D(const QAbstractItemView);
3144 if (!d->isIndexValid(index))
3145 return QSize();
3146 const auto delegate = itemDelegateForIndex(index);
3147 QStyleOptionViewItem option;
3148 initViewItemOption(&option);
3149 return delegate ? delegate->sizeHint(option, index) : QSize();
3150}
3151
3152/*!
3153 Returns the height size hint for the specified \a row or -1 if
3154 there is no model.
3155
3156 The returned height is calculated using the size hints of the
3157 given \a row's items, i.e. the returned value is the maximum
3158 height among the items. Note that to control the height of a row,
3159 you must reimplement the QAbstractItemDelegate::sizeHint()
3160 function.
3161
3162 This function is used in views with a vertical header to find the
3163 size hint for a header section based on the contents of the given
3164 \a row.
3165
3166 \sa sizeHintForColumn()
3167*/
3168int QAbstractItemView::sizeHintForRow(int row) const
3169{
3170 Q_D(const QAbstractItemView);
3171
3172 if (row < 0 || row >= d->model->rowCount(d->root))
3173 return -1;
3174
3175 ensurePolished();
3176
3177 QStyleOptionViewItem option;
3178 initViewItemOption(&option);
3179 int height = 0;
3180 int colCount = d->model->columnCount(d->root);
3181 for (int c = 0; c < colCount; ++c) {
3182 const QModelIndex index = d->model->index(row, c, d->root);
3183 if (QWidget *editor = d->editorForIndex(index).widget.data())
3184 height = qMax(height, editor->height());
3185 if (const QAbstractItemDelegate *delegate = itemDelegateForIndex(index))
3186 height = qMax(height, delegate->sizeHint(option, index).height());
3187 }
3188 return height;
3189}
3190
3191/*!
3192 Returns the width size hint for the specified \a column or -1 if there is no model.
3193
3194 This function is used in views with a horizontal header to find the size hint for
3195 a header section based on the contents of the given \a column.
3196
3197 \sa sizeHintForRow()
3198*/
3199int QAbstractItemView::sizeHintForColumn(int column) const
3200{
3201 Q_D(const QAbstractItemView);
3202
3203 if (column < 0 || column >= d->model->columnCount(d->root))
3204 return -1;
3205
3206 ensurePolished();
3207
3208 QStyleOptionViewItem option;
3209 initViewItemOption(&option);
3210 int width = 0;
3211 int rows = d->model->rowCount(d->root);
3212 for (int r = 0; r < rows; ++r) {
3213 const QModelIndex index = d->model->index(r, column, d->root);
3214 if (QWidget *editor = d->editorForIndex(index).widget.data())
3215 width = qMax(width, editor->sizeHint().width());
3216 if (const QAbstractItemDelegate *delegate = itemDelegateForIndex(index))
3217 width = qMax(width, delegate->sizeHint(option, index).width());
3218 }
3219 return width;
3220}
3221
3222/*!
3223 \property QAbstractItemView::updateThreshold
3224 \since 6.9
3225 This property holds the amount of changed indexes to directly trigger
3226 a full update of the view inside dataChanged().
3227
3228 The algorithm inside dataChanged() tries to minimize a full update of the
3229 view by calculating if the changed indexes are visible or not. For very
3230 large models, with a lot of large changes, this might take longer than the
3231 actual update so it's counter-productive. This property gives the ability
3232 to control the algorithm to skip the check and directly trigger a full
3233 update when the amount of changed indexes exceeds the given value.
3234
3235 The default value is 200.
3236
3237 \sa dataChanged()
3238*/
3239int QAbstractItemView::updateThreshold() const
3240{
3241 Q_D(const QAbstractItemView);
3242 return d->updateThreshold;
3243}
3244
3245void QAbstractItemView::setUpdateThreshold(int threshold)
3246{
3247 Q_D(QAbstractItemView);
3248 if (d->updateThreshold == threshold)
3249 return;
3250 d->updateThreshold = threshold;
3251}
3252
3253/*!
3254 Opens a persistent editor on the item at the given \a index.
3255 If no editor exists, the delegate will create a new editor.
3256
3257 \sa closePersistentEditor(), isPersistentEditorOpen()
3258*/
3259void QAbstractItemView::openPersistentEditor(const QModelIndex &index)
3260{
3261 Q_D(QAbstractItemView);
3262 QStyleOptionViewItem options;
3263 initViewItemOption(&options);
3264 options.rect = visualRect(index);
3265 options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
3266
3267 QWidget *editor = d->editor(index, options);
3268 if (editor) {
3269 editor->show();
3270 d->persistent.insert(editor);
3271 }
3272}
3273
3274/*!
3275 Closes the persistent editor for the item at the given \a index.
3276
3277 \sa openPersistentEditor(), isPersistentEditorOpen()
3278*/
3279void QAbstractItemView::closePersistentEditor(const QModelIndex &index)
3280{
3281 Q_D(QAbstractItemView);
3282 if (QWidget *editor = d->editorForIndex(index).widget.data()) {
3283 if (index == selectionModel()->currentIndex())
3284 closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
3285 d->persistent.remove(editor);
3286 d->removeEditor(editor);
3287 d->releaseEditor(editor, index);
3288 }
3289}
3290
3291/*!
3292 \since 5.10
3293
3294 Returns whether a persistent editor is open for the item at index \a index.
3295
3296 \sa openPersistentEditor(), closePersistentEditor()
3297*/
3298bool QAbstractItemView::isPersistentEditorOpen(const QModelIndex &index) const
3299{
3300 Q_D(const QAbstractItemView);
3301 return d->editorForIndex(index).widget;
3302}
3303
3304/*!
3305 Sets the given \a widget on the item at the given \a index, passing the
3306 ownership of the widget to the viewport.
3307
3308 If \a index is invalid (e.g., if you pass the root index), this function
3309 will do nothing.
3310
3311 The given \a widget's \l{QWidget}{autoFillBackground} property must be set
3312 to true, otherwise the widget's background will be transparent, showing
3313 both the model data and the item at the given \a index.
3314
3315 \note The view takes ownership of the \a widget.
3316 This means if index widget A is replaced with index widget B, index widget A will be
3317 deleted. For example, in the code snippet below, the QLineEdit object will
3318 be deleted.
3319
3320 \snippet code/src_gui_itemviews_qabstractitemview.cpp 1
3321
3322 This function should only be used to display static content within the
3323 visible area corresponding to an item of data. If you want to display
3324 custom dynamic content or implement a custom editor widget, subclass
3325 QStyledItemDelegate instead.
3326
3327 \sa {Delegate Classes}
3328*/
3329void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
3330{
3331 Q_D(QAbstractItemView);
3332 if (!d->isIndexValid(index))
3333 return;
3334 if (indexWidget(index) == widget)
3335 return;
3336 if (QWidget *oldWidget = indexWidget(index)) {
3337 d->persistent.remove(oldWidget);
3338 d->removeEditor(oldWidget);
3339 oldWidget->removeEventFilter(this);
3340 oldWidget->deleteLater();
3341 }
3342 if (widget) {
3343 widget->setParent(viewport());
3344 d->persistent.insert(widget);
3345 d->addEditor(index, widget, true);
3346 widget->installEventFilter(this);
3347 widget->show();
3348 dataChanged(index, index); // update the geometry
3349 if (!d->delayedPendingLayout) {
3350 widget->setGeometry(visualRect(index));
3351 d->doDelayedItemsLayout(); // relayout due to updated geometry
3352 }
3353 }
3354}
3355
3356/*!
3357 Returns the widget for the item at the given \a index.
3358*/
3359QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const
3360{
3361 Q_D(const QAbstractItemView);
3362 if (d->isIndexValid(index))
3363 if (QWidget *editor = d->editorForIndex(index).widget.data())
3364 return editor;
3365
3366 return nullptr;
3367}
3368
3369/*!
3370 Scrolls the view to the top.
3371
3372 \sa scrollTo(), scrollToBottom()
3373*/
3374void QAbstractItemView::scrollToTop()
3375{
3376 verticalScrollBar()->setValue(verticalScrollBar()->minimum());
3377}
3378
3379/*!
3380 Scrolls the view to the bottom.
3381
3382 \sa scrollTo(), scrollToTop()
3383*/
3384void QAbstractItemView::scrollToBottom()
3385{
3386 Q_D(QAbstractItemView);
3387 if (d->delayedPendingLayout) {
3388 d->executePostedLayout();
3389 updateGeometries();
3390 }
3391 verticalScrollBar()->setValue(verticalScrollBar()->maximum());
3392}
3393
3394/*!
3395 Updates the area occupied by the given \a index.
3396
3397*/
3398void QAbstractItemView::update(const QModelIndex &index)
3399{
3400 Q_D(QAbstractItemView);
3401 if (index.isValid()) {
3402 const QRect rect = d->visualRect(index);
3403 //this test is important for performance reason
3404 //For example in dataChanged we simply update all the cells without checking
3405 //it can be a major bottleneck to update rects that aren't even part of the viewport
3406 if (d->viewport->rect().intersects(rect))
3407 d->viewport->update(rect);
3408 }
3409}
3410
3411/*!
3412 This slot is called when items with the given \a roles are changed in the
3413 model. The changed items are those from \a topLeft to \a bottomRight
3414 inclusive. If just one item is changed \a topLeft == \a bottomRight.
3415
3416 The \a roles which have been changed can either be an empty container (meaning everything
3417 has changed), or a non-empty container with the subset of roles which have changed.
3418
3419 \note: Qt::ToolTipRole is not honored by dataChanged() in the views provided by Qt.
3420*/
3421void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
3422 const QList<int> &roles)
3423{
3424 Q_UNUSED(roles);
3425 // Single item changed
3426 Q_D(QAbstractItemView);
3427 if (topLeft == bottomRight && topLeft.isValid()) {
3428 const QEditorInfo &editorInfo = d->editorForIndex(topLeft);
3429 //we don't update the edit data if it is static
3430 if (!editorInfo.isStatic && editorInfo.widget) {
3431 QAbstractItemDelegate *delegate = itemDelegateForIndex(topLeft);
3432 if (delegate) {
3433 delegate->setEditorData(editorInfo.widget.data(), topLeft);
3434 }
3435 }
3436 if (isVisible() && !d->delayedPendingLayout) {
3437 // otherwise the items will be updated later anyway
3438 update(topLeft);
3439 }
3440 } else {
3441 d->updateEditorData(topLeft, bottomRight);
3442 if (isVisible() && !d->delayedPendingLayout) {
3443 if (!topLeft.isValid() ||
3444 topLeft.parent() != bottomRight.parent() ||
3445 topLeft.row() > bottomRight.row() ||
3446 topLeft.column() > bottomRight.column()) {
3447 // invalid parameter - call update() to redraw all
3448 qWarning().nospace() << "dataChanged() called with an invalid index range:"
3449 << "\n topleft: " << topLeft
3450 << "\n bottomRight:" << bottomRight;
3451 d->viewport->update();
3452 } else if ((bottomRight.row() - topLeft.row() + 1LL) *
3453 (bottomRight.column() - topLeft.column() + 1LL) > d->updateThreshold) {
3454 // too many indices to check - force full update
3455 d->viewport->update();
3456 } else {
3457 const QRect updateRect = d->intersectedRect(d->viewport->rect(), topLeft, bottomRight);
3458 if (!updateRect.isEmpty())
3459 d->viewport->update(updateRect);
3460 }
3461 }
3462 }
3463
3464#if QT_CONFIG(accessibility)
3465 if (QAccessible::isActive()) {
3466 QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
3467 accessibleEvent.setFirstRow(topLeft.row());
3468 accessibleEvent.setFirstColumn(topLeft.column());
3469 accessibleEvent.setLastRow(bottomRight.row());
3470 accessibleEvent.setLastColumn(bottomRight.column());
3471 QAccessible::updateAccessibility(&accessibleEvent);
3472 }
3473#endif
3474 d->updateGeometry();
3475}
3476
3477/*!
3478 This slot is called when rows are inserted. The new rows are those
3479 under the given \a parent from \a start to \a end inclusive. The
3480 base class implementation calls fetchMore() on the model to check
3481 for more data.
3482
3483 \sa rowsAboutToBeRemoved()
3484*/
3485void QAbstractItemView::rowsInserted(const QModelIndex &, int, int)
3486{
3487 if (!isVisible())
3488 d_func()->fetchMoreTimer.start(0, this); //fetch more later
3489 else
3490 updateEditorGeometries();
3491}
3492
3493/*!
3494 This slot is called when rows are about to be removed. The deleted rows are
3495 those under the given \a parent from \a start to \a end inclusive.
3496
3497 \sa rowsInserted()
3498*/
3499void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
3500{
3501 Q_D(QAbstractItemView);
3502
3503 setState(CollapsingState);
3504
3505 // Ensure one selected item in single selection mode.
3506 QModelIndex current = currentIndex();
3507 if (d->selectionMode == SingleSelection
3508 && current.isValid()
3509 && current.row() >= start
3510 && current.row() <= end
3511 && current.parent() == parent) {
3512 int totalToRemove = end - start + 1;
3513 if (d->model->rowCount(parent) <= totalToRemove) { // no more children
3514 QModelIndex index = parent;
3515 while (index != d->root && !d->isIndexEnabled(index))
3516 index = index.parent();
3517 if (index != d->root)
3518 setCurrentIndex(index);
3519 } else {
3520 int row = end + 1;
3521 QModelIndex next;
3522 const int rowCount = d->model->rowCount(parent);
3523 bool found = false;
3524 // find the next visible and enabled item
3525 while (row < rowCount && !found) {
3526 next = d->model->index(row++, current.column(), current.parent());
3527#ifdef QT_DEBUG
3528 if (!next.isValid()) {
3529 qWarning("Model unexpectedly returned an invalid index");
3530 break;
3531 }
3532#endif
3533 if (!isIndexHidden(next) && d->isIndexEnabled(next)) {
3534 found = true;
3535 break;
3536 }
3537 }
3538
3539 if (!found) {
3540 row = start - 1;
3541 // find the previous visible and enabled item
3542 while (row >= 0) {
3543 next = d->model->index(row--, current.column(), current.parent());
3544#ifdef QT_DEBUG
3545 if (!next.isValid()) {
3546 qWarning("Model unexpectedly returned an invalid index");
3547 break;
3548 }
3549#endif
3550 if (!isIndexHidden(next) && d->isIndexEnabled(next))
3551 break;
3552 }
3553 }
3554
3555 setCurrentIndex(next);
3556 }
3557 }
3558
3559 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
3560 const auto findDirectChildOf = [](const QModelIndex &parent, QModelIndex child)
3561 {
3562 while (child.isValid()) {
3563 const auto parentIndex = child.parent();
3564 if (parentIndex == parent)
3565 return child;
3566 child = parentIndex;
3567 }
3568 return QModelIndex();
3569 };
3570 QEditorIndexHash::iterator i = d->editorIndexHash.begin();
3571 while (i != d->editorIndexHash.end()) {
3572 const QModelIndex index = i.value();
3573 const QModelIndex directChild = findDirectChildOf(parent, index);
3574 if (directChild.isValid() && directChild.row() >= start && directChild.row() <= end) {
3575 QWidget *editor = i.key();
3576 QEditorInfo info = d->indexEditorHash.take(index);
3577 i = d->editorIndexHash.erase(i);
3578 if (info.widget)
3579 d->releaseEditor(editor, index);
3580 } else {
3581 ++i;
3582 }
3583 }
3584}
3585
3586/*!
3587 \internal
3588
3589 This slot is called when rows have been removed. The deleted
3590 rows are those under the given \a parent from \a start to \a end
3591 inclusive.
3592*/
3593void QAbstractItemViewPrivate::rowsRemoved(const QModelIndex &index, int start, int end)
3594{
3595 Q_UNUSED(index);
3596 Q_UNUSED(start);
3597 Q_UNUSED(end);
3598
3599 Q_Q(QAbstractItemView);
3600 if (q->isVisible())
3601 q->updateEditorGeometries();
3602 q->setState(QAbstractItemView::NoState);
3603#if QT_CONFIG(accessibility)
3604 if (QAccessible::isActive()) {
3605 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsRemoved);
3606 accessibleEvent.setFirstRow(start);
3607 accessibleEvent.setLastRow(end);
3608 QAccessible::updateAccessibility(&accessibleEvent);
3609 }
3610#endif
3611 updateGeometry();
3612}
3613
3614/*!
3615 \internal
3616
3617 This slot is called when columns are about to be removed. The deleted
3618 columns are those under the given \a parent from \a start to \a end
3619 inclusive.
3620*/
3621void QAbstractItemViewPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
3622{
3623 Q_Q(QAbstractItemView);
3624
3625 q->setState(QAbstractItemView::CollapsingState);
3626
3627 // Ensure one selected item in single selection mode.
3628 QModelIndex current = q->currentIndex();
3629 if (current.isValid()
3630 && selectionMode == QAbstractItemView::SingleSelection
3631 && current.column() >= start
3632 && current.column() <= end) {
3633 int totalToRemove = end - start + 1;
3634 if (model->columnCount(parent) < totalToRemove) { // no more columns
3635 QModelIndex index = parent;
3636 while (index.isValid() && !isIndexEnabled(index))
3637 index = index.parent();
3638 if (index.isValid())
3639 q->setCurrentIndex(index);
3640 } else {
3641 int column = end;
3642 QModelIndex next;
3643 const int columnCount = model->columnCount(current.parent());
3644 // find the next visible and enabled item
3645 while (column < columnCount) {
3646 next = model->index(current.row(), column++, current.parent());
3647#ifdef QT_DEBUG
3648 if (!next.isValid()) {
3649 qWarning("Model unexpectedly returned an invalid index");
3650 break;
3651 }
3652#endif
3653 if (!q->isIndexHidden(next) && isIndexEnabled(next))
3654 break;
3655 }
3656 q->setCurrentIndex(next);
3657 }
3658 }
3659
3660 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
3661 QEditorIndexHash::iterator it = editorIndexHash.begin();
3662 while (it != editorIndexHash.end()) {
3663 QModelIndex index = it.value();
3664 if (index.column() <= start && index.column() >= end && model->parent(index) == parent) {
3665 QWidget *editor = it.key();
3666 QEditorInfo info = indexEditorHash.take(it.value());
3667 it = editorIndexHash.erase(it);
3668 if (info.widget)
3669 releaseEditor(editor, index);
3670 } else {
3671 ++it;
3672 }
3673 }
3674
3675}
3676
3677/*!
3678 \internal
3679
3680 This slot is called when columns have been removed. The deleted
3681 rows are those under the given \a parent from \a start to \a end
3682 inclusive.
3683*/
3684void QAbstractItemViewPrivate::columnsRemoved(const QModelIndex &index, int start, int end)
3685{
3686 Q_UNUSED(index);
3687 Q_UNUSED(start);
3688 Q_UNUSED(end);
3689
3690 Q_Q(QAbstractItemView);
3691 if (q->isVisible())
3692 q->updateEditorGeometries();
3693 q->setState(QAbstractItemView::NoState);
3694#if QT_CONFIG(accessibility)
3695 if (QAccessible::isActive()) {
3696 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsRemoved);
3697 accessibleEvent.setFirstColumn(start);
3698 accessibleEvent.setLastColumn(end);
3699 QAccessible::updateAccessibility(&accessibleEvent);
3700 }
3701#endif
3702 updateGeometry();
3703}
3704
3705
3706/*!
3707 \internal
3708
3709 This slot is called when rows have been inserted.
3710*/
3711void QAbstractItemViewPrivate::rowsInserted(const QModelIndex &index, int start, int end)
3712{
3713 Q_UNUSED(index);
3714 Q_UNUSED(start);
3715 Q_UNUSED(end);
3716
3717#if QT_CONFIG(accessibility)
3718 Q_Q(QAbstractItemView);
3719 if (QAccessible::isActive()) {
3720 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsInserted);
3721 accessibleEvent.setFirstRow(start);
3722 accessibleEvent.setLastRow(end);
3723 QAccessible::updateAccessibility(&accessibleEvent);
3724 }
3725#endif
3726 updateGeometry();
3727}
3728
3729/*!
3730 \internal
3731
3732 This slot is called when columns have been inserted.
3733*/
3734void QAbstractItemViewPrivate::columnsInserted(const QModelIndex &index, int start, int end)
3735{
3736 Q_UNUSED(index);
3737 Q_UNUSED(start);
3738 Q_UNUSED(end);
3739
3740 Q_Q(QAbstractItemView);
3741 if (q->isVisible())
3742 q->updateEditorGeometries();
3743#if QT_CONFIG(accessibility)
3744 if (QAccessible::isActive()) {
3745 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsInserted);
3746 accessibleEvent.setFirstColumn(start);
3747 accessibleEvent.setLastColumn(end);
3748 QAccessible::updateAccessibility(&accessibleEvent);
3749 }
3750#endif
3751 updateGeometry();
3752}
3753
3754/*!
3755 \internal
3756*/
3757void QAbstractItemViewPrivate::modelDestroyed()
3758{
3759 model = QAbstractItemModelPrivate::staticEmptyModel();
3760 doDelayedReset();
3761}
3762
3763/*!
3764 \internal
3765
3766 This slot is called when the layout is changed.
3767*/
3768void QAbstractItemViewPrivate::layoutChanged()
3769{
3770 doDelayedItemsLayout();
3771#if QT_CONFIG(accessibility)
3772 Q_Q(QAbstractItemView);
3773 if (QAccessible::isActive()) {
3774 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ModelReset);
3775 QAccessible::updateAccessibility(&accessibleEvent);
3776 }
3777#endif
3778}
3779
3780void QAbstractItemViewPrivate::rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
3781{
3782 layoutChanged();
3783}
3784
3785void QAbstractItemViewPrivate::columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
3786{
3787 layoutChanged();
3788}
3789
3790QRect QAbstractItemViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const
3791{
3792 Q_Q(const QAbstractItemView);
3793
3794 const auto parentIdx = topLeft.parent();
3795 QRect updateRect;
3796 for (int r = topLeft.row(); r <= bottomRight.row(); ++r) {
3797 for (int c = topLeft.column(); c <= bottomRight.column(); ++c)
3798 updateRect |= q->visualRect(model->index(r, c, parentIdx));
3799 }
3800 return rect.intersected(updateRect);
3801}
3802
3803/*!
3804 This slot is called when the selection is changed. The previous
3805 selection (which may be empty), is specified by \a deselected, and the
3806 new selection by \a selected.
3807
3808 \sa setSelection()
3809*/
3810void QAbstractItemView::selectionChanged(const QItemSelection &selected,
3811 const QItemSelection &deselected)
3812{
3813 Q_D(QAbstractItemView);
3814 if (isVisible() && updatesEnabled()) {
3815 d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected));
3816 }
3817}
3818
3819/*!
3820 This slot is called when a new item becomes the current item.
3821 The previous current item is specified by the \a previous index, and the new
3822 item by the \a current index.
3823
3824 If you want to know about changes to items see the
3825 dataChanged() signal.
3826*/
3827void QAbstractItemView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3828{
3829 Q_D(QAbstractItemView);
3830 Q_ASSERT(d->model);
3831
3832 QPersistentModelIndex persistentCurrent(current); // in case commitData() moves things around (QTBUG-127852)
3833
3834 if (previous.isValid()) {
3835 QModelIndex buddy = d->model->buddy(previous);
3836 QWidget *editor = d->editorForIndex(buddy).widget.data();
3837 if (isVisible()) {
3838 update(previous);
3839 }
3840 if (editor && !d->persistent.contains(editor)) {
3841 const bool rowChanged = current.row() != previous.row();
3842 commitData(editor); // might invalidate previous, don't use after this line (QTBUG-127852)
3843 if (rowChanged)
3844 closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
3845 else
3846 closeEditor(editor, QAbstractItemDelegate::NoHint);
3847 }
3848 }
3849
3850 const QModelIndex newCurrent = persistentCurrent;
3851
3852 QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, nullptr);
3853 if ((command & QItemSelectionModel::Current) == 0)
3854 d->currentSelectionStartIndex = newCurrent;
3855
3856 if (newCurrent.isValid() && !d->autoScrollTimer.isActive()) {
3857 if (isVisible()) {
3858 if (d->autoScroll)
3859 scrollTo(newCurrent);
3860 update(newCurrent);
3861 edit(newCurrent, CurrentChanged, nullptr);
3862 if (newCurrent.row() == (d->model->rowCount(d->root) - 1))
3863 d->fetchMore();
3864 } else {
3865 d->shouldScrollToCurrentOnShow = d->autoScroll;
3866 }
3867 }
3868 setAttribute(Qt::WA_InputMethodEnabled, (newCurrent.isValid() && (newCurrent.flags() & Qt::ItemIsEditable)));
3869}
3870
3871#if QT_CONFIG(draganddrop)
3872/*!
3873 Starts a drag by calling drag->exec() using the given \a supportedActions.
3874*/
3875void QAbstractItemView::startDrag(Qt::DropActions supportedActions)
3876{
3877 Q_D(QAbstractItemView);
3878 QModelIndexList indexes = d->selectedDraggableIndexes();
3879 if (indexes.size() > 0) {
3880 QMimeData *data = d->model->mimeData(indexes);
3881 if (!data)
3882 return;
3883 QRect rect;
3884 QPixmap pixmap = d->renderToPixmap(indexes, &rect);
3885 rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
3886 QDrag *drag = new QDrag(this);
3887 drag->setPixmap(pixmap);
3888 drag->setMimeData(data);
3889 drag->setHotSpot(d->pressedPosition - rect.topLeft());
3890 Qt::DropAction defaultDropAction = Qt::IgnoreAction;
3891 if (dragDropMode() == InternalMove)
3892 supportedActions &= ~Qt::CopyAction;
3893 if (d->defaultDropAction != Qt::IgnoreAction && (supportedActions & d->defaultDropAction))
3894 defaultDropAction = d->defaultDropAction;
3895 else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove)
3896 defaultDropAction = Qt::CopyAction;
3897 d->dropEventMoved = false;
3898 if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction && !d->dropEventMoved) {
3899 if (dragDropMode() != InternalMove || drag->target() == viewport())
3900 d->clearOrRemove();
3901 }
3902 d->dropEventMoved = false;
3903 // Reset the drop indicator
3904 d->dropIndicatorRect = QRect();
3905 d->dropIndicatorPosition = OnItem;
3906 }
3907}
3908#endif // QT_CONFIG(draganddrop)
3909
3910/*!
3911 \since 6.0
3912
3913 Initialize the \a option structure with the view's palette, font, state,
3914 alignments etc.
3915
3916 \note Implementations of this methods should check the \l{QStyleOption::}{version}
3917 of the structure received, populate all members the implementation is familiar with,
3918 and set the version member to the one supported by the implementation before returning.
3919*/
3920void QAbstractItemView::initViewItemOption(QStyleOptionViewItem *option) const
3921{
3922 Q_D(const QAbstractItemView);
3923 option->initFrom(this);
3924 option->state &= ~QStyle::State_MouseOver;
3925 option->font = font();
3926
3927 // On mac the focus appearance follows window activation
3928 // not widget activation
3929 if (!hasFocus())
3930 option->state &= ~QStyle::State_Active;
3931
3932 option->state &= ~QStyle::State_HasFocus;
3933 if (d->iconSize.isValid()) {
3934 option->decorationSize = d->iconSize;
3935 } else {
3936 int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
3937 option->decorationSize = QSize(pm, pm);
3938 }
3939 option->decorationPosition = QStyleOptionViewItem::Left;
3940 option->decorationAlignment = Qt::AlignCenter;
3941 option->displayAlignment = Qt::AlignLeft|Qt::AlignVCenter;
3942 option->textElideMode = d->textElideMode;
3943 option->rect = QRect();
3944 option->showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, nullptr, this);
3945 if (d->wrapItemText)
3946 option->features = QStyleOptionViewItem::WrapText;
3947 option->locale = locale();
3948 option->locale.setNumberOptions(QLocale::OmitGroupSeparator);
3949 option->widget = this;
3950}
3951
3952/*!
3953 Returns the item view's state.
3954
3955 \sa setState()
3956*/
3957QAbstractItemView::State QAbstractItemView::state() const
3958{
3959 Q_D(const QAbstractItemView);
3960 return d->state;
3961}
3962
3963/*!
3964 Sets the item view's state to the given \a state.
3965
3966 \sa state()
3967*/
3968void QAbstractItemView::setState(State state)
3969{
3970 Q_D(QAbstractItemView);
3971 d->state = state;
3972}
3973
3974/*!
3975 Schedules a layout of the items in the view to be executed when the
3976 event processing starts.
3977
3978 Even if scheduleDelayedItemsLayout() is called multiple times before
3979 events are processed, the view will only do the layout once.
3980
3981 \sa executeDelayedItemsLayout()
3982*/
3983void QAbstractItemView::scheduleDelayedItemsLayout()
3984{
3985 Q_D(QAbstractItemView);
3986 d->doDelayedItemsLayout();
3987}
3988
3989/*!
3990 Executes the scheduled layouts without waiting for the event processing
3991 to begin.
3992
3993 \sa scheduleDelayedItemsLayout()
3994*/
3995void QAbstractItemView::executeDelayedItemsLayout()
3996{
3997 Q_D(QAbstractItemView);
3998 d->executePostedLayout();
3999}
4000
4001/*!
4002 Marks the given \a region as dirty and schedules it to be updated.
4003 You only need to call this function if you are implementing
4004 your own view subclass.
4005
4006 \sa scrollDirtyRegion(), dirtyRegionOffset()
4007*/
4008
4009void QAbstractItemView::setDirtyRegion(const QRegion &region)
4010{
4011 Q_D(QAbstractItemView);
4012 d->setDirtyRegion(region);
4013}
4014
4015/*!
4016 Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the
4017 opposite direction. You only need to call this function if you are implementing a scrolling
4018 viewport in your view subclass.
4019
4020 If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function
4021 before you call QWidget::scroll() on the viewport. Alternatively, just call update().
4022
4023 \sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion()
4024*/
4025void QAbstractItemView::scrollDirtyRegion(int dx, int dy)
4026{
4027 Q_D(QAbstractItemView);
4028 d->scrollDirtyRegion(dx, dy);
4029}
4030
4031/*!
4032 Returns the offset of the dirty regions in the view.
4033
4034 If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of
4035 QAbstractItemView, you should translate the area given by the paint event with
4036 the offset returned from this function.
4037
4038 \sa scrollDirtyRegion(), setDirtyRegion()
4039*/
4040QPoint QAbstractItemView::dirtyRegionOffset() const
4041{
4042 Q_D(const QAbstractItemView);
4043 return d->scrollDelayOffset;
4044}
4045
4046/*!
4047 \internal
4048*/
4049void QAbstractItemView::startAutoScroll()
4050{
4051 d_func()->startAutoScroll();
4052}
4053
4054/*!
4055 \internal
4056*/
4057void QAbstractItemView::stopAutoScroll()
4058{
4059 d_func()->stopAutoScroll();
4060}
4061
4062/*!
4063 \internal
4064*/
4065void QAbstractItemView::doAutoScroll()
4066{
4067 // find how much we should scroll with
4068 Q_D(QAbstractItemView);
4069 QScrollBar *verticalScroll = verticalScrollBar();
4070 QScrollBar *horizontalScroll = horizontalScrollBar();
4071
4072 // QHeaderView does not (normally) have scrollbars
4073 // It needs to use its parents scroll instead
4074 QHeaderView *hv = qobject_cast<QHeaderView*>(this);
4075 if (hv) {
4076 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget());
4077 if (parent) {
4078 if (hv->orientation() == Qt::Horizontal) {
4079 if (!hv->horizontalScrollBar() || !hv->horizontalScrollBar()->isVisible())
4080 horizontalScroll = parent->horizontalScrollBar();
4081 } else {
4082 if (!hv->verticalScrollBar() || !hv->verticalScrollBar()->isVisible())
4083 verticalScroll = parent->verticalScrollBar();
4084 }
4085 }
4086 }
4087
4088 const int verticalStep = verticalScroll->pageStep();
4089 const int horizontalStep = horizontalScroll->pageStep();
4090 if (d->autoScrollCount < qMax(verticalStep, horizontalStep))
4091 ++d->autoScrollCount;
4092
4093 const int margin = d->autoScrollMargin;
4094 const int verticalValue = verticalScroll->value();
4095 const int horizontalValue = horizontalScroll->value();
4096
4097 const QPoint pos = d->draggedPosition;
4098
4099 const QRect area = QWidgetPrivate::get(d->viewport)->clipRect();
4100
4101 // do the scrolling if we are in the scroll margins
4102 if (pos.y() - area.top() < margin)
4103 verticalScroll->setValue(verticalValue - d->autoScrollCount);
4104 else if (area.bottom() - pos.y() < margin)
4105 verticalScroll->setValue(verticalValue + d->autoScrollCount);
4106 if (pos.x() - area.left() < margin)
4107 horizontalScroll->setValue(horizontalValue - d->autoScrollCount);
4108 else if (area.right() - pos.x() < margin)
4109 horizontalScroll->setValue(horizontalValue + d->autoScrollCount);
4110 // if nothing changed, stop scrolling
4111 const bool verticalUnchanged = (verticalValue == verticalScroll->value());
4112 const bool horizontalUnchanged = (horizontalValue == horizontalScroll->value());
4113 if (verticalUnchanged && horizontalUnchanged) {
4114 stopAutoScroll();
4115 } else {
4116#if QT_CONFIG(draganddrop)
4117 d->dropIndicatorRect = QRect();
4118 d->dropIndicatorPosition = QAbstractItemView::OnViewport;
4119#endif
4120 switch (state()) {
4121 case QAbstractItemView::DragSelectingState: {
4122 // mouseMoveEvent updates the drag-selection rectangle, so fake an event. This also
4123 // updates draggedPosition taking the now scrolled viewport into account.
4124 const QPoint globalPos = d->viewport->mapToGlobal(pos);
4125 const QPoint windowPos = window()->mapFromGlobal(globalPos);
4126 QMouseEvent mm(QEvent::MouseMove, pos, windowPos, globalPos,
4127 Qt::NoButton, Qt::LeftButton, d->pressedModifiers,
4128 Qt::MouseEventSynthesizedByQt);
4129 QApplication::sendEvent(viewport(), &mm);
4130 break;
4131 }
4132 case QAbstractItemView::DraggingState: {
4133 // we can't simulate mouse (it would throw off the drag'n'drop state logic) or drag
4134 // (we don't have the mime data or the actions) move events during drag'n'drop, so
4135 // update our dragged position manually after the scroll. "pos" is the old
4136 // draggedPosition - d->offset(), and d->offset() is now updated after scrolling, so
4137 // pos + d->offset() gives us the new position.
4138 d->draggedPosition = pos;
4139 d->draggedPositionOffset = d->offset();
4140 break;
4141 }
4142 default:
4143 break;
4144 }
4145 d->viewport->update();
4146 }
4147}
4148
4149/*!
4150 Returns the SelectionFlags to be used when updating a selection model
4151 for the specified \a index. The result depends on the current
4152 selectionMode(), and on the user input event \a event, which can be
4153 \nullptr.
4154
4155 Reimplement this function to define your own selection behavior.
4156
4157 \sa setSelection()
4158*/
4159QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index,
4160 const QEvent *event) const
4161{
4162 Q_D(const QAbstractItemView);
4163 Qt::KeyboardModifiers keyModifiers = event && event->isInputEvent()
4164 ? static_cast<const QInputEvent*>(event)->modifiers()
4165 : Qt::NoModifier;
4166 switch (d->selectionMode) {
4167 case NoSelection: // Never update selection model
4168 return QItemSelectionModel::NoUpdate;
4169 case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate
4170 if (event) {
4171 switch (event->type()) {
4172 case QEvent::MouseButtonPress:
4173 // press with any modifiers on a selected item does nothing
4174 if (d->pressedAlreadySelected)
4175 return QItemSelectionModel::NoUpdate;
4176 break;
4177 case QEvent::MouseButtonRelease:
4178 // clicking into area with no items does nothing
4179 if (!index.isValid())
4180 return QItemSelectionModel::NoUpdate;
4181 Q_FALLTHROUGH();
4182 case QEvent::KeyPress:
4183 // ctrl-release on selected item deselects
4184 if ((keyModifiers & Qt::ControlModifier) && d->selectionModel->isSelected(index))
4185 return QItemSelectionModel::Deselect | d->selectionBehaviorFlags();
4186 break;
4187 default:
4188 break;
4189 }
4190 }
4191 return QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
4192 case MultiSelection:
4193 return d->multiSelectionCommand(index, event);
4194 case ExtendedSelection:
4195 return d->extendedSelectionCommand(index, event);
4196 case ContiguousSelection:
4197 return d->contiguousSelectionCommand(index, event);
4198 }
4199 return QItemSelectionModel::NoUpdate;
4200}
4201
4202QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand(
4203 const QModelIndex &index, const QEvent *event) const
4204{
4205 Q_UNUSED(index);
4206
4207 if (event) {
4208 switch (event->type()) {
4209 case QEvent::KeyPress:
4210 if (static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Space
4211 || static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Select)
4212 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4213 break;
4214 case QEvent::MouseButtonPress:
4215 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) {
4216 // since the press might start a drag, deselect only on release
4217 if (!pressedAlreadySelected
4218#if QT_CONFIG(draganddrop)
4219 || !dragEnabled || !isIndexDragEnabled(index)
4220#endif
4221 )
4222 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); // toggle
4223 }
4224 break;
4225 case QEvent::MouseButtonRelease:
4226 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) {
4227 if (pressedAlreadySelected
4228#if QT_CONFIG(draganddrop)
4229 && dragEnabled && isIndexDragEnabled(index)
4230#endif
4231 && index == pressedIndex)
4232 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4233 return QItemSelectionModel::NoUpdate|selectionBehaviorFlags(); // finalize
4234 }
4235 break;
4236 case QEvent::MouseMove:
4237 if (static_cast<const QMouseEvent*>(event)->buttons() & Qt::LeftButton)
4238 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); // toggle drag select
4239 break;
4240 default:
4241 break;
4242 }
4243 return QItemSelectionModel::NoUpdate;
4244 }
4245
4246 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4247}
4248
4249QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand(
4250 const QModelIndex &index, const QEvent *event) const
4251{
4252 Qt::KeyboardModifiers modifiers = event && event->isInputEvent()
4253 ? static_cast<const QInputEvent*>(event)->modifiers()
4254 : QGuiApplication::keyboardModifiers();
4255 if (event) {
4256 switch (event->type()) {
4257 case QEvent::MouseMove: {
4258 // Toggle on MouseMove
4259 if (modifiers & Qt::ControlModifier)
4260 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags();
4261 break;
4262 }
4263 case QEvent::MouseButtonPress: {
4264 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
4265 const bool rightButtonPressed = button & Qt::RightButton;
4266 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
4267 const bool controlKeyPressed = modifiers & Qt::ControlModifier;
4268 const bool indexIsSelected = selectionModel->isSelected(index);
4269 if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed)
4270 return QItemSelectionModel::NoUpdate;
4271 if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected)
4272 return QItemSelectionModel::NoUpdate;
4273 if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed)
4274 return QItemSelectionModel::Clear;
4275 if (!index.isValid())
4276 return QItemSelectionModel::NoUpdate;
4277 // since the press might start a drag, deselect only on release
4278 if (controlKeyPressed && !rightButtonPressed && pressedAlreadySelected
4279#if QT_CONFIG(draganddrop)
4280 && dragEnabled && isIndexDragEnabled(index)
4281#endif
4282 ) {
4283 return QItemSelectionModel::NoUpdate;
4284 }
4285 break;
4286 }
4287 case QEvent::MouseButtonRelease: {
4288 // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area
4289 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
4290 const bool rightButtonPressed = button & Qt::RightButton;
4291 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
4292 const bool controlKeyPressed = modifiers & Qt::ControlModifier;
4293 if (((index == pressedIndex && selectionModel->isSelected(index))
4294 || !index.isValid()) && state != QAbstractItemView::DragSelectingState
4295 && !shiftKeyPressed && !controlKeyPressed && (!rightButtonPressed || !index.isValid()))
4296 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4297 if (index == pressedIndex && controlKeyPressed && !rightButtonPressed
4298#if QT_CONFIG(draganddrop)
4299 && dragEnabled && isIndexDragEnabled(index)
4300#endif
4301 ) {
4302 break;
4303 }
4304 return QItemSelectionModel::NoUpdate;
4305 }
4306 case QEvent::KeyPress: {
4307 // NoUpdate on Key movement and Ctrl
4308 switch (static_cast<const QKeyEvent*>(event)->key()) {
4309 case Qt::Key_Backtab:
4310 modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab
4311 Q_FALLTHROUGH();
4312 case Qt::Key_Down:
4313 case Qt::Key_Up:
4314 case Qt::Key_Left:
4315 case Qt::Key_Right:
4316 case Qt::Key_Home:
4317 case Qt::Key_End:
4318 case Qt::Key_PageUp:
4319 case Qt::Key_PageDown:
4320 case Qt::Key_Tab:
4321 if (modifiers & Qt::ControlModifier
4322#ifdef QT_KEYPAD_NAVIGATION
4323 // Preserve historical tab order navigation behavior
4324 || QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder
4325#endif
4326 )
4327 return QItemSelectionModel::NoUpdate;
4328 break;
4329 case Qt::Key_Select:
4330 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4331 case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space
4332 if (modifiers & Qt::ControlModifier)
4333 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4334 return QItemSelectionModel::Select|selectionBehaviorFlags();
4335 default:
4336 break;
4337 }
4338 break;
4339 }
4340 default:
4341 break;
4342 }
4343 }
4344
4345 if (modifiers & Qt::ShiftModifier)
4346 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4347 if (modifiers & Qt::ControlModifier)
4348 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4349 if (state == QAbstractItemView::DragSelectingState) {
4350 //when drag-selecting we need to clear any previous selection and select the current one
4351 return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4352 }
4353
4354 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4355}
4356
4357QItemSelectionModel::SelectionFlags
4358QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index,
4359 const QEvent *event) const
4360{
4361 QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event);
4362 const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select
4363 | QItemSelectionModel::Deselect | QItemSelectionModel::Toggle
4364 | QItemSelectionModel::Current;
4365
4366 switch (flags & Mask) {
4367 case QItemSelectionModel::Clear:
4368 case QItemSelectionModel::ClearAndSelect:
4369 case QItemSelectionModel::SelectCurrent:
4370 return flags;
4371 case QItemSelectionModel::NoUpdate:
4372 if (event &&
4373 (event->type() == QEvent::MouseButtonPress
4374 || event->type() == QEvent::MouseButtonRelease))
4375 return flags;
4376 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4377 default:
4378 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4379 }
4380}
4381
4382void QAbstractItemViewPrivate::fetchMore()
4383{
4384 fetchMoreTimer.stop();
4385 if (!model->canFetchMore(root))
4386 return;
4387 int last = model->rowCount(root) - 1;
4388 if (last < 0) {
4389 model->fetchMore(root);
4390 return;
4391 }
4392
4393 QModelIndex index = model->index(last, 0, root);
4394 QRect rect = q_func()->visualRect(index);
4395 if (viewport->rect().intersects(rect))
4396 model->fetchMore(root);
4397}
4398
4399bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger,
4400 const QModelIndex &index) const
4401{
4402 if (!index.isValid())
4403 return false;
4404 Qt::ItemFlags flags = model->flags(index);
4405 if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0))
4406 return false;
4407 if (state == QAbstractItemView::EditingState)
4408 return false;
4409 if (hasEditor(index))
4410 return false;
4411 if (trigger == QAbstractItemView::AllEditTriggers) // force editing
4412 return true;
4413 if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked
4414 && !selectionModel->isSelected(index))
4415 return false;
4416 return (trigger & editTriggers);
4417}
4418
4419bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger,
4420 const QEvent *event) const
4421{
4422 if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed)
4423 return false;
4424
4425 switch (event->type()) {
4426 case QEvent::KeyPress:
4427 case QEvent::MouseButtonDblClick:
4428 case QEvent::MouseButtonPress:
4429 case QEvent::MouseButtonRelease:
4430 case QEvent::MouseMove:
4431 return true;
4432 default:
4433 break;
4434 };
4435
4436 return false;
4437}
4438
4439bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const
4440{
4441 if (!autoScroll)
4442 return false;
4443 const QRect area = QWidgetPrivate::get(viewport)->clipRect();
4444 return (pos.y() - area.top() < autoScrollMargin)
4445 || (area.bottom() - pos.y() < autoScrollMargin)
4446 || (pos.x() - area.left() < autoScrollMargin)
4447 || (area.right() - pos.x() < autoScrollMargin);
4448}
4449
4450void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay)
4451{
4452 if (!delayedPendingLayout) {
4453 delayedPendingLayout = true;
4454 delayedLayout.start(delay, q_func());
4455 }
4456}
4457
4458void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const
4459{
4460 delayedLayout.stop();
4461 delayedPendingLayout = false;
4462}
4463
4464void QAbstractItemViewPrivate::updateGeometry()
4465{
4466 Q_Q(QAbstractItemView);
4467 if (sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored)
4468 return;
4469 if (sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents || !shownOnce)
4470 q->updateGeometry();
4471}
4472
4473/*
4474 Handles selection of content for some editors containing QLineEdit.
4475
4476 ### Qt 7 This should be done by a virtual method in QAbstractItemDelegate.
4477*/
4478void QAbstractItemViewPrivate::selectAllInEditor(QWidget *editor)
4479{
4480 while (QWidget *fp = editor->focusProxy())
4481 editor = fp;
4482
4483#if QT_CONFIG(lineedit)
4484 if (QLineEdit *le = qobject_cast<QLineEdit*>(editor))
4485 le->selectAll();
4486#endif
4487#if QT_CONFIG(spinbox)
4488 if (QSpinBox *sb = qobject_cast<QSpinBox*>(editor))
4489 sb->selectAll();
4490 else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(editor))
4491 dsb->selectAll();
4492#endif
4493}
4494
4495QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index,
4496 const QStyleOptionViewItem &options)
4497{
4498 Q_Q(QAbstractItemView);
4499 QWidget *w = editorForIndex(index).widget.data();
4500 if (!w) {
4501 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4502 if (!delegate)
4503 return nullptr;
4504 w = delegate->createEditor(viewport, options, index);
4505 if (w) {
4506 w->installEventFilter(delegate);
4507 QObject::connect(w, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
4508 delegate->updateEditorGeometry(w, options, index);
4509 delegate->setEditorData(w, index);
4510 addEditor(index, w, false);
4511 if (w->parent() == viewport)
4512 QWidget::setTabOrder(q, w);
4513
4514 selectAllInEditor(w);
4515 }
4516 }
4517
4518 return w;
4519}
4520
4521void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br)
4522{
4523 Q_Q(QAbstractItemView);
4524 // we are counting on having relatively few editors
4525 const bool checkIndexes = tl.isValid() && br.isValid();
4526 const QModelIndex parent = tl.parent();
4527 // QTBUG-25370: We need to copy the indexEditorHash, because while we're
4528 // iterating over it, we are calling methods which can allow user code to
4529 // call a method on *this which can modify the member indexEditorHash.
4530 const QIndexEditorHash indexEditorHashCopy = indexEditorHash;
4531 QIndexEditorHash::const_iterator it = indexEditorHashCopy.constBegin();
4532 for (; it != indexEditorHashCopy.constEnd(); ++it) {
4533 QWidget *editor = it.value().widget.data();
4534 const QModelIndex index = it.key();
4535 if (it.value().isStatic || !editor || !index.isValid() ||
4536 (checkIndexes
4537 && (index.row() < tl.row() || index.row() > br.row()
4538 || index.column() < tl.column() || index.column() > br.column()
4539 || index.parent() != parent)))
4540 continue;
4541
4542 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4543 if (delegate) {
4544 delegate->setEditorData(editor, index);
4545 }
4546 }
4547}
4548
4549/*!
4550 \internal
4551
4552 In DND if something has been moved then this is called.
4553 Typically this means you should "remove" the selected item or row,
4554 but the behavior is view-dependent (table just clears the selected indexes for example).
4555
4556 Either remove the selected rows or clear them
4557*/
4558void QAbstractItemViewPrivate::clearOrRemove()
4559{
4560#if QT_CONFIG(draganddrop)
4561 const QItemSelection selection = selectionModel->selection();
4562 QList<QItemSelectionRange>::const_iterator it = selection.constBegin();
4563
4564 if (!overwrite) {
4565 for (; it != selection.constEnd(); ++it) {
4566 QModelIndex parent = (*it).parent();
4567 if ((*it).left() != 0)
4568 continue;
4569 if ((*it).right() != (model->columnCount(parent) - 1))
4570 continue;
4571 int count = (*it).bottom() - (*it).top() + 1;
4572 model->removeRows((*it).top(), count, parent);
4573 }
4574 } else {
4575 // we can't remove the rows so reset the items (i.e. the view is like a table)
4576 QModelIndexList list = selection.indexes();
4577 for (int i=0; i < list.size(); ++i) {
4578 QModelIndex index = list.at(i);
4579 QMap<int, QVariant> roles = model->itemData(index);
4580 for (QMap<int, QVariant>::Iterator it = roles.begin(); it != roles.end(); ++it)
4581 it.value() = QVariant();
4582 model->setItemData(index, roles);
4583 }
4584 }
4585#endif
4586}
4587
4588/*!
4589 \internal
4590
4591 When persistent aeditor gets/loses focus, we need to check
4592 and setcorrectly the current index.
4593*/
4594void QAbstractItemViewPrivate::checkPersistentEditorFocus()
4595{
4596 Q_Q(QAbstractItemView);
4597 if (QWidget *widget = QApplication::focusWidget()) {
4598 if (persistent.contains(widget)) {
4599 //a persistent editor has gained the focus
4600 QModelIndex index = indexForEditor(widget);
4601 if (selectionModel->currentIndex() != index)
4602 q->setCurrentIndex(index);
4603 }
4604 }
4605}
4606
4607
4608const QEditorInfo & QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const
4609{
4610 static QEditorInfo nullInfo;
4611
4612 // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
4613 if (indexEditorHash.isEmpty())
4614 return nullInfo;
4615
4616 QIndexEditorHash::const_iterator it = indexEditorHash.find(index);
4617 if (it == indexEditorHash.end())
4618 return nullInfo;
4619
4620 return it.value();
4621}
4622
4623bool QAbstractItemViewPrivate::hasEditor(const QModelIndex &index) const
4624{
4625 // Search's implicit cast (QModelIndex to QPersistentModelIndex) is slow; use cheap pre-test to avoid when we can.
4626 return !indexEditorHash.isEmpty() && indexEditorHash.contains(index);
4627}
4628
4629QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const
4630{
4631 // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
4632 if (indexEditorHash.isEmpty())
4633 return QModelIndex();
4634
4635 QEditorIndexHash::const_iterator it = editorIndexHash.find(editor);
4636 if (it == editorIndexHash.end())
4637 return QModelIndex();
4638
4639 return it.value();
4640}
4641
4642void QAbstractItemViewPrivate::removeEditor(QWidget *editor)
4643{
4644 Q_Q(QAbstractItemView);
4645 if (editor)
4646 QObject::disconnect(editor, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
4647 const auto it = editorIndexHash.constFind(editor);
4648 if (it != editorIndexHash.cend()) {
4649 indexEditorHash.remove(it.value());
4650 editorIndexHash.erase(it);
4651 }
4652}
4653
4654void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic)
4655{
4656 editorIndexHash.insert(editor, index);
4657 indexEditorHash.insert(index, QEditorInfo(editor, isStatic));
4658}
4659
4660bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const
4661{
4662 Q_Q(const QAbstractItemView);
4663 QModelIndex buddy = model->buddy(index);
4664 QStyleOptionViewItem options;
4665 q->initViewItemOption(&options);
4666 options.rect = q->visualRect(buddy);
4667 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
4668 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4669 return (event && delegate && delegate->editorEvent(event, model, options, buddy));
4670}
4671
4672bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event)
4673{
4674 Q_Q(QAbstractItemView);
4675
4676 QModelIndex buddy = model->buddy(index);
4677 QStyleOptionViewItem options;
4678 q->initViewItemOption(&options);
4679 options.rect = q->visualRect(buddy);
4680 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
4681
4682 QWidget *w = editor(buddy, options);
4683 if (!w)
4684 return false;
4685
4686 q->setState(QAbstractItemView::EditingState);
4687 w->show();
4688 if (!waitForIMCommit)
4689 w->setFocus();
4690 else
4691 q->updateMicroFocus();
4692
4693 if (event)
4694 QCoreApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event);
4695
4696 return true;
4697}
4698
4699/*
4700 \internal
4701
4702 returns the pair QRect/QModelIndex that should be painted on the viewports's rect
4703*/
4704
4705QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
4706{
4707 Q_ASSERT(r);
4708 Q_Q(const QAbstractItemView);
4709 QRect &rect = *r;
4710 const QRect viewportRect = viewport->rect();
4711 QItemViewPaintPairs ret;
4712 for (const auto &index : indexes) {
4713 const QRect current = q->visualRect(index);
4714 if (current.intersects(viewportRect)) {
4715 ret.append({current, index});
4716 rect |= current;
4717 }
4718 }
4719 QRect clipped = rect & viewportRect;
4720 rect.setLeft(clipped.left());
4721 rect.setRight(clipped.right());
4722 return ret;
4723}
4724
4725QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const
4726{
4727 Q_Q(const QAbstractItemView);
4728 Q_ASSERT(r);
4729 QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r);
4730 if (paintPairs.isEmpty())
4731 return QPixmap();
4732
4733 QWindow *window = windowHandle(WindowHandleMode::Closest);
4734 const qreal scale = window ? window->devicePixelRatio() : qreal(1);
4735
4736 QPixmap pixmap(r->size() * scale);
4737 pixmap.setDevicePixelRatio(scale);
4738
4739 pixmap.fill(Qt::transparent);
4740 QPainter painter(&pixmap);
4741 QStyleOptionViewItem option;
4742 q->initViewItemOption(&option);
4743 option.state |= QStyle::State_Selected;
4744 for (int j = 0; j < paintPairs.size(); ++j) {
4745 option.rect = paintPairs.at(j).rect.translated(-r->topLeft());
4746 const QModelIndex &current = paintPairs.at(j).index;
4747 adjustViewOptionsForIndex(&option, current);
4748 q->itemDelegateForIndex(current)->paint(&painter, option, current);
4749 }
4750 return pixmap;
4751}
4752
4753void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
4754{
4755 if (!selectionModel)
4756 return;
4757 if (!model->hasChildren(root))
4758 return;
4759
4760 QItemSelection selection;
4761 QModelIndex tl = model->index(0, 0, root);
4762 QModelIndex br = model->index(model->rowCount(root) - 1,
4763 model->columnCount(root) - 1,
4764 root);
4765 selection.append(QItemSelectionRange(tl, br));
4766 selectionModel->select(selection, command);
4767}
4768
4769#if QT_CONFIG(draganddrop)
4770QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const
4771{
4772 Q_Q(const QAbstractItemView);
4773 QModelIndexList indexes = q->selectedIndexes();
4774 auto isNotDragEnabled = [this](const QModelIndex &index) {
4775 return !isIndexDragEnabled(index);
4776 };
4777 indexes.removeIf(isNotDragEnabled);
4778 return indexes;
4779}
4780
4781void QAbstractItemViewPrivate::maybeStartDrag(QPoint eventPosition)
4782{
4783 Q_Q(QAbstractItemView);
4784
4785 const QPoint topLeft = pressedPosition - offset();
4786 if ((topLeft - eventPosition).manhattanLength() > QApplication::startDragDistance()) {
4787 pressedIndex = QModelIndex();
4788 q->startDrag(model->supportedDragActions());
4789 q->setState(QAbstractItemView::NoState); // the startDrag will return when the dnd operation
4790 // is done
4791 q->stopAutoScroll();
4792 }
4793}
4794#endif
4795
4796/*!
4797 \reimp
4798*/
4799
4800bool QAbstractItemView::eventFilter(QObject *object, QEvent *event)
4801{
4802 Q_D(QAbstractItemView);
4803 if (object == this || object == viewport() || event->type() != QEvent::FocusIn)
4804 return QAbstractScrollArea::eventFilter(object, event);
4805 QWidget *widget = qobject_cast<QWidget *>(object);
4806 // If it is not a persistent widget then we did not install
4807 // the event filter on it, so assume a base implementation is
4808 // filtering
4809 if (!widget || !d->persistent.contains(widget))
4810 return QAbstractScrollArea::eventFilter(object, event);
4811 setCurrentIndex(d->indexForEditor(widget));
4812 return false;
4813}
4814
4815QT_END_NAMESPACE
4816
4817#include "moc_qabstractitemview.cpp"