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