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 setState(NoState);
2012
2013 if (click) {
2014 if (event->button() == Qt::LeftButton)
2015 emit clicked(index);
2016 if (edited)
2017 return;
2018 QStyleOptionViewItem option;
2019 initViewItemOption(&option);
2020 if (d->pressedAlreadySelected)
2021 option.state |= QStyle::State_Selected;
2022 if ((d->model->flags(index) & Qt::ItemIsEnabled)
2023 && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
2024 emit activated(index);
2025 }
2026}
2027
2028/*!
2029 This function is called with the given \a event when a mouse button is
2030 double clicked inside the widget. If the double-click is on a valid item it
2031 emits the doubleClicked() signal and calls edit() on the item.
2032*/
2033void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
2034{
2035 Q_D(QAbstractItemView);
2036
2037 QModelIndex index = indexAt(event->position().toPoint());
2038 if (!index.isValid()
2039 || !d->isIndexEnabled(index)
2040 || (d->pressedIndex != index)) {
2041 QMouseEvent me(QEvent::MouseButtonPress,
2042 event->position(), event->scenePosition(), event->globalPosition(),
2043 event->button(), event->buttons(), event->modifiers(),
2044 event->source(), event->pointingDevice());
2045 mousePressEvent(&me);
2046 return;
2047 }
2048 // signal handlers may change the model
2049 QPersistentModelIndex persistent = index;
2050 emit doubleClicked(persistent);
2051 if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
2052 && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
2053 emit activated(persistent);
2054 d->releaseFromDoubleClick = true;
2055}
2056
2057#if QT_CONFIG(draganddrop)
2058
2059/*!
2060 This function is called with the given \a event when a drag and drop operation enters
2061 the widget. If the drag is over a valid dropping place (e.g. over an item that
2062 accepts drops), the event is accepted; otherwise it is ignored.
2063
2064 \sa dropEvent(), startDrag()
2065*/
2066void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event)
2067{
2068 if (dragDropMode() == InternalMove
2069 && (event->source() != this|| !(event->possibleActions() & Qt::MoveAction)))
2070 return;
2071
2072 if (d_func()->canDrop(event)) {
2073 event->accept();
2074 setState(DraggingState);
2075 } else {
2076 event->ignore();
2077 }
2078}
2079
2080/*!
2081 This function is called continuously with the given \a event during a drag and
2082 drop operation over the widget. It can cause the view to scroll if, for example,
2083 the user drags a selection to view's right or bottom edge. In this case, the
2084 event will be accepted; otherwise it will be ignored.
2085
2086 \sa dropEvent(), startDrag()
2087*/
2088void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event)
2089{
2090 Q_D(QAbstractItemView);
2091 d->draggedPosition = event->position().toPoint() + d->offset();
2092 if (dragDropMode() == InternalMove
2093 && (event->source() != this || !(event->possibleActions() & Qt::MoveAction)))
2094 return;
2095
2096 // ignore by default
2097 event->ignore();
2098
2099 QModelIndex index = indexAt(event->position().toPoint());
2100 d->hover = index;
2101 if (!d->droppingOnItself(event, index)
2102 && d->canDrop(event)) {
2103
2104 if (index.isValid() && d->showDropIndicator) {
2105 QRect rect = visualRect(index);
2106 d->dropIndicatorPosition = d->position(event->position().toPoint(), rect, index);
2107 if (d->selectionBehavior == QAbstractItemView::SelectRows
2108 && d->dropIndicatorPosition != OnViewport
2109 && (d->dropIndicatorPosition != OnItem || event->source() == this)) {
2110 const int maxCol = d->model->columnCount(index.parent()) - 1;
2111 const auto idx = index.column() > 0 ? index.siblingAtColumn(0) : index;
2112 rect = d->intersectedRect(viewport()->rect(), idx, idx.siblingAtColumn(maxCol));
2113 }
2114 switch (d->dropIndicatorPosition) {
2115 case AboveItem:
2116 if (d->isIndexDropEnabled(index.parent())) {
2117 d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0);
2118 event->acceptProposedAction();
2119 } else {
2120 d->dropIndicatorRect = QRect();
2121 }
2122 break;
2123 case BelowItem:
2124 if (d->isIndexDropEnabled(index.parent())) {
2125 d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0);
2126 event->acceptProposedAction();
2127 } else {
2128 d->dropIndicatorRect = QRect();
2129 }
2130 break;
2131 case OnItem:
2132 if (d->isIndexDropEnabled(index)) {
2133 d->dropIndicatorRect = rect;
2134 event->acceptProposedAction();
2135 } else {
2136 d->dropIndicatorRect = QRect();
2137 }
2138 break;
2139 case OnViewport:
2140 d->dropIndicatorRect = QRect();
2141 if (d->isIndexDropEnabled(rootIndex())) {
2142 event->acceptProposedAction(); // allow dropping in empty areas
2143 }
2144 break;
2145 }
2146 } else {
2147 d->dropIndicatorRect = QRect();
2148 d->dropIndicatorPosition = OnViewport;
2149 if (d->isIndexDropEnabled(rootIndex())) {
2150 event->acceptProposedAction(); // allow dropping in empty areas
2151 }
2152 }
2153 d->viewport->update();
2154 } // can drop
2155
2156 if (d->shouldAutoScroll(event->position().toPoint()))
2157 startAutoScroll();
2158}
2159
2160/*!
2161 \internal
2162 Return true if this is a move from ourself and \a index is a child of the selection that
2163 is being moved.
2164 */
2165bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index)
2166{
2167 Q_Q(QAbstractItemView);
2168 Qt::DropAction dropAction = event->dropAction();
2169 if (q->dragDropMode() == QAbstractItemView::InternalMove)
2170 dropAction = Qt::MoveAction;
2171 if (event->source() == q
2172 && event->possibleActions() & Qt::MoveAction
2173 && dropAction == Qt::MoveAction) {
2174 QModelIndexList selectedIndexes = q->selectedIndexes();
2175 QModelIndex child = index;
2176 while (child.isValid() && child != root) {
2177 if (selectedIndexes.contains(child))
2178 return true;
2179 child = child.parent();
2180 }
2181 }
2182 return false;
2183}
2184
2185/*!
2186 \fn void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *event)
2187
2188 This function is called when the item being dragged leaves the view.
2189 The \a event describes the state of the drag and drop operation.
2190*/
2191void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *)
2192{
2193 Q_D(QAbstractItemView);
2194 stopAutoScroll();
2195 setState(NoState);
2196 d->hover = QModelIndex();
2197 d->viewport->update();
2198}
2199
2200/*!
2201 This function is called with the given \a event when a drop event occurs over
2202 the widget. If the model accepts the even position the drop event is accepted;
2203 otherwise it is ignored.
2204
2205 \sa startDrag()
2206*/
2207void QAbstractItemView::dropEvent(QDropEvent *event)
2208{
2209 Q_D(QAbstractItemView);
2210 if (dragDropMode() == InternalMove) {
2211 if (event->source() != this || !(event->possibleActions() & Qt::MoveAction))
2212 return;
2213 }
2214
2215 QModelIndex index;
2216 int col = -1;
2217 int row = -1;
2218 if (d->dropOn(event, &row, &col, &index)) {
2219 const Qt::DropAction action = dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction();
2220 if (d->model->dropMimeData(event->mimeData(), action, row, col, index)) {
2221 if (action != event->dropAction()) {
2222 event->setDropAction(action);
2223 event->accept();
2224 } else {
2225 event->acceptProposedAction();
2226 }
2227 }
2228 }
2229 stopAutoScroll();
2230 setState(NoState);
2231 d->viewport->update();
2232}
2233
2234/*!
2235 If the event hasn't already been accepted, determines the index to drop on.
2236
2237 if (row == -1 && col == -1)
2238 // append to this drop index
2239 else
2240 // place at row, col in drop index
2241
2242 If it returns \c true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop.
2243 \internal
2244 */
2245bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
2246{
2247 Q_Q(QAbstractItemView);
2248 if (event->isAccepted())
2249 return false;
2250
2251 QModelIndex index;
2252 // rootIndex() (i.e. the viewport) might be a valid index
2253 if (viewport->rect().contains(event->position().toPoint())) {
2254 index = q->indexAt(event->position().toPoint());
2255 if (!index.isValid())
2256 index = root;
2257 }
2258
2259 // If we are allowed to do the drop
2260 if (model->supportedDropActions() & event->dropAction()) {
2261 int row = -1;
2262 int col = -1;
2263 if (index != root) {
2264 dropIndicatorPosition = position(event->position().toPoint(), q->visualRect(index), index);
2265 switch (dropIndicatorPosition) {
2266 case QAbstractItemView::AboveItem:
2267 row = index.row();
2268 col = index.column();
2269 index = index.parent();
2270 break;
2271 case QAbstractItemView::BelowItem:
2272 row = index.row() + 1;
2273 col = index.column();
2274 index = index.parent();
2275 break;
2276 case QAbstractItemView::OnItem:
2277 case QAbstractItemView::OnViewport:
2278 break;
2279 }
2280 } else {
2281 dropIndicatorPosition = QAbstractItemView::OnViewport;
2282 }
2283 *dropIndex = index;
2284 *dropRow = row;
2285 *dropCol = col;
2286 if (!droppingOnItself(event, index))
2287 return true;
2288 }
2289 return false;
2290}
2291
2292QAbstractItemView::DropIndicatorPosition
2293QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const
2294{
2295 QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
2296 if (!overwrite) {
2297 const int margin = qBound(2, qRound(qreal(rect.height()) / 5.5), 12);
2298 if (pos.y() - rect.top() < margin) {
2299 r = QAbstractItemView::AboveItem;
2300 } else if (rect.bottom() - pos.y() < margin) {
2301 r = QAbstractItemView::BelowItem;
2302 } else if (rect.contains(pos, true)) {
2303 r = QAbstractItemView::OnItem;
2304 }
2305 } else {
2306 QRect touchingRect = rect;
2307 touchingRect.adjust(-1, -1, 1, 1);
2308 if (touchingRect.contains(pos, false)) {
2309 r = QAbstractItemView::OnItem;
2310 }
2311 }
2312
2313 if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled)))
2314 r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
2315
2316 return r;
2317}
2318
2319#endif // QT_CONFIG(draganddrop)
2320
2321/*!
2322 This function is called with the given \a event when the widget obtains the focus.
2323 By default, the event is ignored.
2324
2325 \sa setFocus(), focusOutEvent()
2326*/
2327void QAbstractItemView::focusInEvent(QFocusEvent *event)
2328{
2329 Q_D(QAbstractItemView);
2330 QAbstractScrollArea::focusInEvent(event);
2331
2332 const QItemSelectionModel* model = selectionModel();
2333 bool currentIndexValid = currentIndex().isValid();
2334
2335 if (model
2336 && !d->currentIndexSet
2337 && !currentIndexValid) {
2338 bool autoScroll = d->autoScroll;
2339 d->autoScroll = false;
2340 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
2341 if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) {
2342 selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
2343 currentIndexValid = true;
2344 }
2345 d->autoScroll = autoScroll;
2346 }
2347
2348 if (model && currentIndexValid)
2349 setAttribute(Qt::WA_InputMethodEnabled, (currentIndex().flags() & Qt::ItemIsEditable));
2350 else if (!currentIndexValid)
2351 setAttribute(Qt::WA_InputMethodEnabled, false);
2352
2353 d->viewport->update();
2354}
2355
2356/*!
2357 This function is called with the given \a event when the widget
2358 loses the focus. By default, the event is ignored.
2359
2360 \sa clearFocus(), focusInEvent()
2361*/
2362void QAbstractItemView::focusOutEvent(QFocusEvent *event)
2363{
2364 Q_D(QAbstractItemView);
2365 QAbstractScrollArea::focusOutEvent(event);
2366 d->viewport->update();
2367}
2368
2369/*!
2370 This function is called with the given \a event when a key event is sent to
2371 the widget. The default implementation handles basic cursor movement, e.g. Up,
2372 Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is
2373 emitted if the current index is valid and the activation key is pressed
2374 (e.g. Enter or Return, depending on the platform).
2375 This function is where editing is initiated by key press, e.g. if F2 is
2376 pressed.
2377
2378 \sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation
2379*/
2380void QAbstractItemView::keyPressEvent(QKeyEvent *event)
2381{
2382 Q_D(QAbstractItemView);
2383 d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling
2384
2385#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT)
2386 if (event == QKeySequence::Copy) {
2387 const QModelIndex index = currentIndex();
2388 if (index.isValid() && d->model) {
2389 const QVariant variant = d->model->data(index, Qt::DisplayRole);
2390 if (variant.canConvert<QString>())
2391 QGuiApplication::clipboard()->setText(variant.toString());
2392 }
2393 event->accept();
2394 }
2395#endif
2396
2397 QPersistentModelIndex newCurrent;
2398 d->moveCursorUpdatedView = false;
2399 switch (event->key()) {
2400 case Qt::Key_Down:
2401 newCurrent = moveCursor(MoveDown, event->modifiers());
2402 break;
2403 case Qt::Key_Up:
2404 newCurrent = moveCursor(MoveUp, event->modifiers());
2405 break;
2406 case Qt::Key_Left:
2407 newCurrent = moveCursor(MoveLeft, event->modifiers());
2408 break;
2409 case Qt::Key_Right:
2410 newCurrent = moveCursor(MoveRight, event->modifiers());
2411 break;
2412 case Qt::Key_Home:
2413 newCurrent = moveCursor(MoveHome, event->modifiers());
2414 break;
2415 case Qt::Key_End:
2416 newCurrent = moveCursor(MoveEnd, event->modifiers());
2417 break;
2418 case Qt::Key_PageUp:
2419 newCurrent = moveCursor(MovePageUp, event->modifiers());
2420 break;
2421 case Qt::Key_PageDown:
2422 newCurrent = moveCursor(MovePageDown, event->modifiers());
2423 break;
2424 case Qt::Key_Tab:
2425 if (d->tabKeyNavigation)
2426 newCurrent = moveCursor(MoveNext, event->modifiers());
2427 break;
2428 case Qt::Key_Backtab:
2429 if (d->tabKeyNavigation)
2430 newCurrent = moveCursor(MovePrevious, event->modifiers());
2431 break;
2432 }
2433
2434 QPersistentModelIndex oldCurrent = currentIndex();
2435 if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) {
2436 if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent))
2437 setFocus();
2438 QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event);
2439 if (command != QItemSelectionModel::NoUpdate
2440 || style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, nullptr, this)) {
2441 // note that we don't check if the new current index is enabled because moveCursor() makes sure it is
2442 if (command & QItemSelectionModel::Current) {
2443 d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
2444 if (!d->currentSelectionStartIndex.isValid())
2445 d->currentSelectionStartIndex = oldCurrent;
2446 QRect rect(visualRect(d->currentSelectionStartIndex).center(), visualRect(newCurrent).center());
2447 setSelection(rect, command);
2448 } else {
2449 d->selectionModel->setCurrentIndex(newCurrent, command);
2450 d->currentSelectionStartIndex = newCurrent;
2451 if (newCurrent.isValid()) {
2452 // We copy the same behaviour as for mousePressEvent().
2453 QRect rect(visualRect(newCurrent).center(), QSize(1, 1));
2454 setSelection(rect, command);
2455 }
2456 }
2457 event->accept();
2458 return;
2459 }
2460 }
2461
2462 switch (event->key()) {
2463 // ignored keys
2464 case Qt::Key_Down:
2465 case Qt::Key_Up:
2466 case Qt::Key_Left:
2467 case Qt::Key_Right:
2468 case Qt::Key_Home:
2469 case Qt::Key_End:
2470 case Qt::Key_PageUp:
2471 case Qt::Key_PageDown:
2472 case Qt::Key_Escape:
2473 case Qt::Key_Shift:
2474 case Qt::Key_Control:
2475 case Qt::Key_Delete:
2476 case Qt::Key_Backspace:
2477 event->ignore();
2478 break;
2479 case Qt::Key_Space:
2480 case Qt::Key_Select:
2481 if (!edit(currentIndex(), AnyKeyPressed, event)) {
2482 if (d->selectionModel)
2483 d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event));
2484 if (event->key() == Qt::Key_Space) {
2485 keyboardSearch(event->text());
2486 event->accept();
2487 }
2488 }
2489 break;
2490#ifdef Q_OS_MACOS
2491 case Qt::Key_Enter:
2492 case Qt::Key_Return:
2493 // Propagate the enter if you couldn't edit the item and there are no
2494 // current editors (if there are editors, the event was most likely propagated from it).
2495 if (!edit(currentIndex(), EditKeyPressed, event) && d->editorIndexHash.isEmpty())
2496 event->ignore();
2497 break;
2498#else
2499 case Qt::Key_F2:
2500 if (!edit(currentIndex(), EditKeyPressed, event))
2501 event->ignore();
2502 break;
2503 case Qt::Key_Enter:
2504 case Qt::Key_Return:
2505 // ### we can't open the editor on enter, because
2506 // some widgets will forward the enter event back
2507 // to the viewport, starting an endless loop
2508 if (state() != EditingState || hasFocus()) {
2509 if (currentIndex().isValid())
2510 emit activated(currentIndex());
2511 event->ignore();
2512 }
2513 break;
2514#endif
2515 default: {
2516#ifndef QT_NO_SHORTCUT
2517 if (event == QKeySequence::SelectAll && selectionMode() != NoSelection) {
2518 selectAll();
2519 break;
2520 }
2521#endif
2522#ifdef Q_OS_MACOS
2523 if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) {
2524 emit activated(currentIndex());
2525 break;
2526 }
2527#endif
2528 bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
2529 if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) {
2530 keyboardSearch(event->text());
2531 event->accept();
2532 } else {
2533 event->ignore();
2534 }
2535 break; }
2536 }
2537 if (d->moveCursorUpdatedView)
2538 event->accept();
2539}
2540
2541/*!
2542 This function is called with the given \a event when a resize event is sent to
2543 the widget.
2544
2545 \sa QWidget::resizeEvent()
2546*/
2547void QAbstractItemView::resizeEvent(QResizeEvent *event)
2548{
2549 QAbstractScrollArea::resizeEvent(event);
2550 updateGeometries();
2551}
2552
2553/*!
2554 This function is called with the given \a event when a timer event is sent
2555 to the widget.
2556
2557 \sa QObject::timerEvent()
2558*/
2559void QAbstractItemView::timerEvent(QTimerEvent *event)
2560{
2561 Q_D(QAbstractItemView);
2562 if (event->timerId() == d->fetchMoreTimer.timerId())
2563 d->fetchMore();
2564 else if (event->timerId() == d->delayedReset.timerId())
2565 reset();
2566 else if (event->timerId() == d->autoScrollTimer.timerId())
2567 doAutoScroll();
2568 else if (event->timerId() == d->updateTimer.timerId())
2569 d->updateDirtyRegion();
2570 else if (event->timerId() == d->delayedEditing.timerId()) {
2571 d->delayedEditing.stop();
2572 edit(currentIndex());
2573 } else if (event->timerId() == d->delayedLayout.timerId()) {
2574 d->delayedLayout.stop();
2575 if (isVisible()) {
2576 d->interruptDelayedItemsLayout();
2577 doItemsLayout();
2578 const QModelIndex current = currentIndex();
2579 if (current.isValid() && d->state == QAbstractItemView::EditingState)
2580 scrollTo(current);
2581 }
2582 } else if (event->timerId() == d->delayedAutoScroll.timerId()) {
2583 d->delayedAutoScroll.stop();
2584 //end of the timer: if the current item is still the same as the one when the mouse press occurred
2585 //we only get here if there was no double click
2586 if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex())
2587 scrollTo(d->pressedIndex);
2588 } else if (event->timerId() == d->pressClosedEditorWatcher.timerId()) {
2589 d->pressClosedEditorWatcher.stop();
2590 }
2591}
2592
2593/*!
2594 \reimp
2595*/
2596void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event)
2597{
2598 Q_D(QAbstractItemView);
2599 // When QAbstractItemView::AnyKeyPressed is used, a new IM composition might
2600 // start before the editor widget acquires focus. Changing focus would interrupt
2601 // the composition, so we keep focus on the view until that first composition
2602 // is complete, and pass QInputMethoEvents on to the editor widget so that the
2603 // user gets the expected feedback. See also inputMethodQuery, which redirects
2604 // calls to the editor widget during that period.
2605 bool forwardEventToEditor = false;
2606 const bool commit = !event->commitString().isEmpty();
2607 const bool preediting = !event->preeditString().isEmpty();
2608 if (QWidget *currentEditor = d->editorForIndex(currentIndex()).widget) {
2609 if (d->waitForIMCommit) {
2610 if (commit || !preediting) {
2611 // commit or cancel
2612 d->waitForIMCommit = false;
2613 QApplication::sendEvent(currentEditor, event);
2614 if (!commit) {
2615 QAbstractItemDelegate *delegate = itemDelegateForIndex(currentIndex());
2616 if (delegate)
2617 delegate->setEditorData(currentEditor, currentIndex());
2618 d->selectAllInEditor(currentEditor);
2619 }
2620 if (currentEditor->focusPolicy() != Qt::NoFocus)
2621 currentEditor->setFocus();
2622 } else {
2623 // more pre-editing
2624 QApplication::sendEvent(currentEditor, event);
2625 }
2626 return;
2627 }
2628 } else if (preediting) {
2629 // don't set focus when the editor opens
2630 d->waitForIMCommit = true;
2631 // but pass preedit on to editor
2632 forwardEventToEditor = true;
2633 } else if (!commit) {
2634 event->ignore();
2635 return;
2636 }
2637 if (!edit(currentIndex(), AnyKeyPressed, event)) {
2638 d->waitForIMCommit = false;
2639 if (commit)
2640 keyboardSearch(event->commitString());
2641 event->ignore();
2642 } else if (QWidget *currentEditor; forwardEventToEditor
2643 && (currentEditor = d->editorForIndex(currentIndex()).widget)) {
2644 QApplication::sendEvent(currentEditor, event);
2645 }
2646}
2647
2648#if QT_CONFIG(draganddrop)
2649/*!
2650 \enum QAbstractItemView::DropIndicatorPosition
2651
2652 This enum indicates the position of the drop indicator in
2653 relation to the index at the current mouse position:
2654
2655 \value OnItem The item will be dropped on the index.
2656
2657 \value AboveItem The item will be dropped above the index.
2658
2659 \value BelowItem The item will be dropped below the index.
2660
2661 \value OnViewport The item will be dropped onto a region of the viewport with
2662 no items. The way each view handles items dropped onto the viewport depends on
2663 the behavior of the underlying model in use.
2664*/
2665
2666
2667/*!
2668 Returns the position of the drop indicator in relation to the closest item.
2669*/
2670QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const
2671{
2672 Q_D(const QAbstractItemView);
2673 return d->dropIndicatorPosition;
2674}
2675#endif
2676
2677/*!
2678 This convenience function returns a list of all selected and
2679 non-hidden item indexes in the view. The list contains no
2680 duplicates, and is not sorted.
2681
2682 \sa QItemSelectionModel::selectedIndexes()
2683*/
2684QModelIndexList QAbstractItemView::selectedIndexes() const
2685{
2686 Q_D(const QAbstractItemView);
2687 QModelIndexList indexes;
2688 if (d->selectionModel) {
2689 indexes = d->selectionModel->selectedIndexes();
2690 auto isHidden = [this](const QModelIndex &idx) {
2691 return isIndexHidden(idx);
2692 };
2693 indexes.removeIf(isHidden);
2694 }
2695 return indexes;
2696}
2697
2698/*!
2699 Starts editing the item at \a index, creating an editor if
2700 necessary, and returns \c true if the view's \l{State} is now
2701 EditingState; otherwise returns \c false.
2702
2703 The action that caused the editing process is described by
2704 \a trigger, and the associated event is specified by \a event.
2705
2706 Editing can be forced by specifying the \a trigger to be
2707 QAbstractItemView::AllEditTriggers.
2708
2709 \sa closeEditor()
2710*/
2711bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
2712{
2713 Q_D(QAbstractItemView);
2714
2715 if (!d->isIndexValid(index))
2716 return false;
2717
2718 if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(nullptr) : d->editorForIndex(index).widget.data())) {
2719 if (w->focusPolicy() == Qt::NoFocus)
2720 return false;
2721 if (!d->waitForIMCommit)
2722 w->setFocus();
2723 else
2724 updateMicroFocus();
2725 return true;
2726 }
2727
2728 if (trigger == DoubleClicked) {
2729 d->delayedEditing.stop();
2730 d->delayedAutoScroll.stop();
2731 } else if (trigger == CurrentChanged) {
2732 d->delayedEditing.stop();
2733 }
2734
2735 // in case e.g. setData() triggers a reset()
2736 QPersistentModelIndex safeIndex(index);
2737
2738 if (d->sendDelegateEvent(index, event)) {
2739 update(safeIndex);
2740 return true;
2741 }
2742
2743 if (!safeIndex.isValid()) {
2744 return false;
2745 }
2746
2747 // save the previous trigger before updating
2748 EditTriggers lastTrigger = d->lastTrigger;
2749 d->lastTrigger = trigger;
2750
2751 if (!d->shouldEdit(trigger, d->model->buddy(safeIndex)))
2752 return false;
2753
2754 if (d->delayedEditing.isActive())
2755 return false;
2756
2757 // we will receive a mouseButtonReleaseEvent after a
2758 // mouseDoubleClickEvent, so we need to check the previous trigger
2759 if (lastTrigger == DoubleClicked && trigger == SelectedClicked)
2760 return false;
2761
2762 // we may get a double click event later
2763 if (trigger == SelectedClicked)
2764 d->delayedEditing.start(QApplication::doubleClickInterval(), this);
2765 else
2766 d->openEditor(safeIndex, d->shouldForwardEvent(trigger, event) ? event : nullptr);
2767
2768 return true;
2769}
2770
2771/*!
2772 \internal
2773 Updates the data shown in the open editor widgets in the view.
2774*/
2775void QAbstractItemView::updateEditorData()
2776{
2777 Q_D(QAbstractItemView);
2778 d->updateEditorData(QModelIndex(), QModelIndex());
2779}
2780
2781/*!
2782 \internal
2783 Updates the geometry of the open editor widgets in the view.
2784*/
2785void QAbstractItemView::updateEditorGeometries()
2786{
2787 Q_D(QAbstractItemView);
2788 if (d->editorIndexHash.isEmpty())
2789 return;
2790 if (d->delayedPendingLayout) {
2791 // doItemsLayout() will end up calling this function again
2792 d->executePostedLayout();
2793 return;
2794 }
2795 QStyleOptionViewItem option;
2796 initViewItemOption(&option);
2797 QEditorIndexHash::iterator it = d->editorIndexHash.begin();
2798 QWidgetList editorsToRelease;
2799 QWidgetList editorsToHide;
2800 while (it != d->editorIndexHash.end()) {
2801 QModelIndex index = it.value();
2802 QWidget *editor = it.key();
2803 if (index.isValid() && editor) {
2804 option.rect = visualRect(index);
2805 if (option.rect.isValid()) {
2806 editor->show();
2807 QAbstractItemDelegate *delegate = itemDelegateForIndex(index);
2808 if (delegate)
2809 delegate->updateEditorGeometry(editor, option, index);
2810 } else {
2811 editorsToHide << editor;
2812 }
2813 ++it;
2814 } else {
2815 d->indexEditorHash.remove(it.value());
2816 it = d->editorIndexHash.erase(it);
2817 editorsToRelease << editor;
2818 }
2819 }
2820
2821 //we hide and release the editor outside of the loop because it might change the focus and try
2822 //to change the editors hashes.
2823 for (int i = 0; i < editorsToHide.size(); ++i) {
2824 editorsToHide.at(i)->hide();
2825 }
2826 for (int i = 0; i < editorsToRelease.size(); ++i) {
2827 d->releaseEditor(editorsToRelease.at(i));
2828 }
2829}
2830
2831/*!
2832 Updates the geometry of the child widgets of the view.
2833*/
2834void QAbstractItemView::updateGeometries()
2835{
2836 Q_D(QAbstractItemView);
2837 updateEditorGeometries();
2838 d->fetchMoreTimer.start(0, this); //fetch more later
2839 d->updateGeometry();
2840}
2841
2842/*!
2843 \internal
2844*/
2845void QAbstractItemView::verticalScrollbarValueChanged(int value)
2846{
2847 Q_D(QAbstractItemView);
2848 if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
2849 d->model->fetchMore(d->root);
2850 QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
2851 if (viewport()->rect().contains(posInVp))
2852 d->checkMouseMove(posInVp);
2853}
2854
2855/*!
2856 \internal
2857*/
2858void QAbstractItemView::horizontalScrollbarValueChanged(int value)
2859{
2860 Q_D(QAbstractItemView);
2861 if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
2862 d->model->fetchMore(d->root);
2863 QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
2864 if (viewport()->rect().contains(posInVp))
2865 d->checkMouseMove(posInVp);
2866}
2867
2868/*!
2869 \internal
2870*/
2871void QAbstractItemView::verticalScrollbarAction(int)
2872{
2873 //do nothing
2874}
2875
2876/*!
2877 \internal
2878*/
2879void QAbstractItemView::horizontalScrollbarAction(int)
2880{
2881 //do nothing
2882}
2883
2884/*!
2885 Closes the given \a editor, and releases it. The \a hint is
2886 used to specify how the view should respond to the end of the editing
2887 operation. For example, the hint may indicate that the next item in
2888 the view should be opened for editing.
2889
2890 \sa edit(), commitData()
2891*/
2892
2893void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
2894{
2895 Q_D(QAbstractItemView);
2896
2897 // Close the editor
2898 if (editor) {
2899 const bool isPersistent = d->persistent.contains(editor);
2900 const QModelIndex index = d->indexForEditor(editor);
2901 if (!index.isValid()) {
2902 if (!editor->isVisible()) {
2903 // The commit might have removed the index (e.g. it might get filtered), in
2904 // which case the editor is already hidden and scheduled for deletion. We
2905 // don't have to do anything, except reset the state, and continue with
2906 // EndEditHint processing.
2907 if (!isPersistent)
2908 setState(NoState);
2909 } else {
2910 qWarning("QAbstractItemView::closeEditor called with an editor that does not belong to this view");
2911 return;
2912 }
2913 } else {
2914 const bool hadFocus = editor->hasFocus();
2915 // start a timer that expires immediately when we return to the event loop
2916 // to identify whether this close was triggered by a mousepress-initiated
2917 // focus event
2918 d->pressClosedEditorWatcher.start(0, this);
2919 d->lastEditedIndex = index;
2920
2921 if (!isPersistent) {
2922 setState(NoState);
2923 QModelIndex index = d->indexForEditor(editor);
2924 editor->removeEventFilter(itemDelegateForIndex(index));
2925 d->removeEditor(editor);
2926 }
2927 if (hadFocus) {
2928 if (focusPolicy() != Qt::NoFocus)
2929 setFocus(); // this will send a focusLost event to the editor
2930 else
2931 editor->clearFocus();
2932 } else {
2933 d->checkPersistentEditorFocus();
2934 }
2935
2936 QPointer<QWidget> ed = editor;
2937 QCoreApplication::sendPostedEvents(editor, 0);
2938 editor = ed;
2939
2940 if (!isPersistent && editor)
2941 d->releaseEditor(editor, index);
2942 }
2943 }
2944
2945 // The EndEditHint part
2946 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::NoUpdate;
2947 if (d->selectionMode != NoSelection)
2948 flags = QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
2949 switch (hint) {
2950 case QAbstractItemDelegate::EditNextItem: {
2951 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier);
2952 if (index.isValid()) {
2953 QPersistentModelIndex persistent(index);
2954 d->selectionModel->setCurrentIndex(persistent, flags);
2955 // currentChanged signal would have already started editing
2956 if (index.flags() & Qt::ItemIsEditable
2957 && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
2958 edit(persistent);
2959 } break; }
2960 case QAbstractItemDelegate::EditPreviousItem: {
2961 QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier);
2962 if (index.isValid()) {
2963 QPersistentModelIndex persistent(index);
2964 d->selectionModel->setCurrentIndex(persistent, flags);
2965 // currentChanged signal would have already started editing
2966 if (index.flags() & Qt::ItemIsEditable
2967 && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
2968 edit(persistent);
2969 } break; }
2970 case QAbstractItemDelegate::SubmitModelCache:
2971 d->model->submit();
2972 break;
2973 case QAbstractItemDelegate::RevertModelCache:
2974 d->model->revert();
2975 break;
2976 default:
2977 break;
2978 }
2979}
2980
2981/*!
2982 Commit the data in the \a editor to the model.
2983
2984 \sa closeEditor()
2985*/
2986void QAbstractItemView::commitData(QWidget *editor)
2987{
2988 Q_D(QAbstractItemView);
2989 if (!editor || !d->itemDelegate || d->currentlyCommittingEditor)
2990 return;
2991 QModelIndex index = d->indexForEditor(editor);
2992 if (!index.isValid()) {
2993 qWarning("QAbstractItemView::commitData called with an editor that does not belong to this view");
2994 return;
2995 }
2996 d->currentlyCommittingEditor = editor;
2997 QAbstractItemDelegate *delegate = itemDelegateForIndex(index);
2998 editor->removeEventFilter(delegate);
2999 delegate->setModelData(editor, d->model, index);
3000 editor->installEventFilter(delegate);
3001 d->currentlyCommittingEditor = nullptr;
3002}
3003
3004/*!
3005 This function is called when the given \a editor has been destroyed.
3006
3007 \sa closeEditor()
3008*/
3009void QAbstractItemView::editorDestroyed(QObject *editor)
3010{
3011 Q_D(QAbstractItemView);
3012 QWidget *w = qobject_cast<QWidget*>(editor);
3013 d->removeEditor(w);
3014 d->persistent.remove(w);
3015 if (state() == EditingState)
3016 setState(NoState);
3017}
3018
3019
3020
3021/*!
3022 Moves to and selects the item best matching the string \a search.
3023 If no item is found nothing happens.
3024
3025 In the default implementation, the search is reset if \a search is empty, or
3026 the time interval since the last search has exceeded
3027 QApplication::keyboardInputInterval().
3028*/
3029void QAbstractItemView::keyboardSearch(const QString &search)
3030{
3031 Q_D(QAbstractItemView);
3032 if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
3033 return;
3034
3035 QModelIndex start = currentIndex().isValid() ? currentIndex()
3036 : d->model->index(0, 0, d->root);
3037 bool skipRow = false;
3038 bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
3039 qint64 keyboardInputTimeElapsed;
3040 if (keyboardTimeWasValid)
3041 keyboardInputTimeElapsed = d->keyboardInputTime.restart();
3042 else
3043 d->keyboardInputTime.start();
3044 if (search.isEmpty() || !keyboardTimeWasValid
3045 || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
3046 d->keyboardInput = search;
3047 skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
3048 } else {
3049 d->keyboardInput += search;
3050 }
3051
3052 // special case for searches with same key like 'aaaaa'
3053 bool sameKey = false;
3054 if (d->keyboardInput.size() > 1) {
3055 int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.size() - 1));
3056 sameKey = (c == d->keyboardInput.size());
3057 if (sameKey)
3058 skipRow = true;
3059 }
3060
3061 // skip if we are searching for the same key or a new search started
3062 if (skipRow) {
3063 QModelIndex parent = start.parent();
3064 int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0;
3065 start = d->model->index(newRow, start.column(), parent);
3066 }
3067
3068 // search from start with wraparound
3069 QModelIndex current = start;
3070 QModelIndexList match;
3071 QModelIndex firstMatch;
3072 QModelIndex startMatch;
3073 QModelIndexList previous;
3074 do {
3075 match = d->model->match(current, Qt::DisplayRole, d->keyboardInput, 1,
3076 d->keyboardSearchFlags);
3077 if (match == previous)
3078 break;
3079 firstMatch = match.value(0);
3080 previous = match;
3081 if (firstMatch.isValid()) {
3082 if (d->isIndexEnabled(firstMatch)) {
3083 setCurrentIndex(firstMatch);
3084 break;
3085 }
3086 int row = firstMatch.row() + 1;
3087 if (row >= d->model->rowCount(firstMatch.parent()))
3088 row = 0;
3089 current = firstMatch.sibling(row, firstMatch.column());
3090
3091 //avoid infinite loop if all the matching items are disabled.
3092 if (!startMatch.isValid())
3093 startMatch = firstMatch;
3094 else if (startMatch == firstMatch)
3095 break;
3096 }
3097 } while (current != start && firstMatch.isValid());
3098}
3099
3100/*!
3101 Returns the size hint for the item with the specified \a index or
3102 an invalid size for invalid indexes.
3103
3104 \sa sizeHintForRow(), sizeHintForColumn()
3105*/
3106QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const
3107{
3108 Q_D(const QAbstractItemView);
3109 if (!d->isIndexValid(index))
3110 return QSize();
3111 const auto delegate = itemDelegateForIndex(index);
3112 QStyleOptionViewItem option;
3113 initViewItemOption(&option);
3114 return delegate ? delegate->sizeHint(option, index) : QSize();
3115}
3116
3117/*!
3118 Returns the height size hint for the specified \a row or -1 if
3119 there is no model.
3120
3121 The returned height is calculated using the size hints of the
3122 given \a row's items, i.e. the returned value is the maximum
3123 height among the items. Note that to control the height of a row,
3124 you must reimplement the QAbstractItemDelegate::sizeHint()
3125 function.
3126
3127 This function is used in views with a vertical header to find the
3128 size hint for a header section based on the contents of the given
3129 \a row.
3130
3131 \sa sizeHintForColumn()
3132*/
3133int QAbstractItemView::sizeHintForRow(int row) const
3134{
3135 Q_D(const QAbstractItemView);
3136
3137 if (row < 0 || row >= d->model->rowCount(d->root))
3138 return -1;
3139
3140 ensurePolished();
3141
3142 QStyleOptionViewItem option;
3143 initViewItemOption(&option);
3144 int height = 0;
3145 int colCount = d->model->columnCount(d->root);
3146 for (int c = 0; c < colCount; ++c) {
3147 const QModelIndex index = d->model->index(row, c, d->root);
3148 if (QWidget *editor = d->editorForIndex(index).widget.data())
3149 height = qMax(height, editor->height());
3150 if (const QAbstractItemDelegate *delegate = itemDelegateForIndex(index))
3151 height = qMax(height, delegate->sizeHint(option, index).height());
3152 }
3153 return height;
3154}
3155
3156/*!
3157 Returns the width size hint for the specified \a column or -1 if there is no model.
3158
3159 This function is used in views with a horizontal header to find the size hint for
3160 a header section based on the contents of the given \a column.
3161
3162 \sa sizeHintForRow()
3163*/
3164int QAbstractItemView::sizeHintForColumn(int column) const
3165{
3166 Q_D(const QAbstractItemView);
3167
3168 if (column < 0 || column >= d->model->columnCount(d->root))
3169 return -1;
3170
3171 ensurePolished();
3172
3173 QStyleOptionViewItem option;
3174 initViewItemOption(&option);
3175 int width = 0;
3176 int rows = d->model->rowCount(d->root);
3177 for (int r = 0; r < rows; ++r) {
3178 const QModelIndex index = d->model->index(r, column, d->root);
3179 if (QWidget *editor = d->editorForIndex(index).widget.data())
3180 width = qMax(width, editor->sizeHint().width());
3181 if (const QAbstractItemDelegate *delegate = itemDelegateForIndex(index))
3182 width = qMax(width, delegate->sizeHint(option, index).width());
3183 }
3184 return width;
3185}
3186
3187/*!
3188 \property QAbstractItemView::updateThreshold
3189 \since 6.9
3190 This property holds the amount of changed indexes to directly trigger
3191 a full update of the view inside dataChanged().
3192
3193 The algorithm inside dataChanged() tries to minimize a full update of the
3194 view by calculating if the changed indexes are visible or not. For very
3195 large models, with a lot of large changes, this might take longer than the
3196 actual update so it's counter-productive. This property gives the ability
3197 to control the algorithm to skip the check and directly trigger a full
3198 update when the amount of changed indexes exceeds the given value.
3199
3200 The default value is 200.
3201
3202 \sa dataChanged()
3203*/
3204int QAbstractItemView::updateThreshold() const
3205{
3206 Q_D(const QAbstractItemView);
3207 return d->updateThreshold;
3208}
3209
3210void QAbstractItemView::setUpdateThreshold(int threshold)
3211{
3212 Q_D(QAbstractItemView);
3213 if (d->updateThreshold == threshold)
3214 return;
3215 d->updateThreshold = threshold;
3216}
3217
3218/*!
3219 \property QAbstractItemView::keyboardSearchFlags
3220 \since 6.11
3221 This property determines how the default implementation of
3222 keyboardSearch() matches the given string against the model's data.
3223
3224 The default value is \c{Qt::MatchStartsWith|Qt::MatchWrap}.
3225
3226 \sa keyboardSearch()
3227 \sa QAbstractItemModel::match()
3228*/
3229
3230Qt::MatchFlags QAbstractItemView::keyboardSearchFlags() const
3231{
3232 Q_D(const QAbstractItemView);
3233 return d->keyboardSearchFlags;
3234}
3235
3236void QAbstractItemView::setKeyboardSearchFlags(Qt::MatchFlags searchFlags)
3237{
3238 Q_D(QAbstractItemView);
3239 d->keyboardSearchFlags = searchFlags;
3240}
3241
3242/*!
3243 Opens a persistent editor on the item at the given \a index.
3244 If no editor exists, the delegate will create a new editor.
3245
3246 \sa closePersistentEditor(), isPersistentEditorOpen()
3247*/
3248void QAbstractItemView::openPersistentEditor(const QModelIndex &index)
3249{
3250 Q_D(QAbstractItemView);
3251 QStyleOptionViewItem options;
3252 initViewItemOption(&options);
3253 options.rect = visualRect(index);
3254 options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
3255
3256 QWidget *editor = d->editor(index, options);
3257 if (editor) {
3258 editor->show();
3259 d->persistent.insert(editor);
3260 }
3261}
3262
3263/*!
3264 Closes the persistent editor for the item at the given \a index.
3265
3266 \sa openPersistentEditor(), isPersistentEditorOpen()
3267*/
3268void QAbstractItemView::closePersistentEditor(const QModelIndex &index)
3269{
3270 Q_D(QAbstractItemView);
3271 if (QWidget *editor = d->editorForIndex(index).widget.data()) {
3272 if (index == selectionModel()->currentIndex())
3273 closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
3274 d->persistent.remove(editor);
3275 d->removeEditor(editor);
3276 d->releaseEditor(editor, index);
3277 }
3278}
3279
3280/*!
3281 \since 5.10
3282
3283 Returns whether a persistent editor is open for the item at index \a index.
3284
3285 \sa openPersistentEditor(), closePersistentEditor()
3286*/
3287bool QAbstractItemView::isPersistentEditorOpen(const QModelIndex &index) const
3288{
3289 Q_D(const QAbstractItemView);
3290 QWidget *editor = d->editorForIndex(index).widget;
3291 return editor && d->persistent.contains(editor);
3292}
3293
3294/*!
3295 Sets the given \a widget on the item at the given \a index, passing the
3296 ownership of the widget to the viewport.
3297
3298 If \a index is invalid (e.g., if you pass the root index), this function
3299 will do nothing.
3300
3301 The given \a widget's \l{QWidget}{autoFillBackground} property must be set
3302 to true, otherwise the widget's background will be transparent, showing
3303 both the model data and the item at the given \a index.
3304
3305 \note The view takes ownership of the \a widget.
3306 This means if index widget A is replaced with index widget B, index widget A will be
3307 deleted. For example, in the code snippet below, the QLineEdit object will
3308 be deleted.
3309
3310 \snippet code/src_gui_itemviews_qabstractitemview.cpp 1
3311
3312 This function should only be used to display static content within the
3313 visible area corresponding to an item of data. If you want to display
3314 custom dynamic content or implement a custom editor widget, subclass
3315 QStyledItemDelegate instead.
3316
3317 \sa {Delegate Classes}
3318*/
3319void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
3320{
3321 Q_D(QAbstractItemView);
3322 if (!d->isIndexValid(index))
3323 return;
3324 if (indexWidget(index) == widget)
3325 return;
3326 if (QWidget *oldWidget = indexWidget(index)) {
3327 d->persistent.remove(oldWidget);
3328 d->removeEditor(oldWidget);
3329 oldWidget->removeEventFilter(this);
3330 oldWidget->deleteLater();
3331 }
3332 if (widget) {
3333 widget->setParent(viewport());
3334 d->persistent.insert(widget);
3335 d->addEditor(index, widget, true);
3336 widget->installEventFilter(this);
3337 widget->show();
3338 dataChanged(index, index); // update the geometry
3339 if (!d->delayedPendingLayout) {
3340 widget->setGeometry(visualRect(index));
3341 d->doDelayedItemsLayout(); // relayout due to updated geometry
3342 }
3343 }
3344}
3345
3346/*!
3347 Returns the widget for the item at the given \a index.
3348*/
3349QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const
3350{
3351 Q_D(const QAbstractItemView);
3352 if (d->isIndexValid(index))
3353 if (QWidget *editor = d->editorForIndex(index).widget.data())
3354 return editor;
3355
3356 return nullptr;
3357}
3358
3359/*!
3360 Scrolls the view to the top.
3361
3362 \sa scrollTo(), scrollToBottom()
3363*/
3364void QAbstractItemView::scrollToTop()
3365{
3366 verticalScrollBar()->setValue(verticalScrollBar()->minimum());
3367}
3368
3369/*!
3370 Scrolls the view to the bottom.
3371
3372 \sa scrollTo(), scrollToTop()
3373*/
3374void QAbstractItemView::scrollToBottom()
3375{
3376 Q_D(QAbstractItemView);
3377 if (d->delayedPendingLayout) {
3378 d->executePostedLayout();
3379 updateGeometries();
3380 }
3381 verticalScrollBar()->setValue(verticalScrollBar()->maximum());
3382}
3383
3384/*!
3385 Updates the area occupied by the given \a index.
3386
3387*/
3388void QAbstractItemView::update(const QModelIndex &index)
3389{
3390 Q_D(QAbstractItemView);
3391 if (index.isValid()) {
3392 const QRect rect = d->visualRect(index);
3393 //this test is important for performance reason
3394 //For example in dataChanged we simply update all the cells without checking
3395 //it can be a major bottleneck to update rects that aren't even part of the viewport
3396 if (d->viewport->rect().intersects(rect))
3397 d->viewport->update(rect);
3398 }
3399}
3400
3401/*!
3402 This slot is called when items with the given \a roles are changed in the
3403 model. The changed items are those from \a topLeft to \a bottomRight
3404 inclusive. If just one item is changed \a topLeft == \a bottomRight.
3405
3406 The \a roles which have been changed can either be an empty container (meaning everything
3407 has changed), or a non-empty container with the subset of roles which have changed.
3408
3409 \note: Qt::ToolTipRole is not honored by dataChanged() in the views provided by Qt.
3410*/
3411void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
3412 const QList<int> &roles)
3413{
3414 Q_UNUSED(roles);
3415 // Single item changed
3416 Q_D(QAbstractItemView);
3417 if (topLeft == bottomRight && topLeft.isValid()) {
3418 const QEditorInfo &editorInfo = d->editorForIndex(topLeft);
3419 //we don't update the edit data if it is static
3420 if (!editorInfo.isStatic && editorInfo.widget) {
3421 QAbstractItemDelegate *delegate = itemDelegateForIndex(topLeft);
3422 if (delegate) {
3423 delegate->setEditorData(editorInfo.widget.data(), topLeft);
3424 }
3425 }
3426 if (isVisible() && !d->delayedPendingLayout) {
3427 // otherwise the items will be updated later anyway
3428 update(topLeft);
3429 }
3430 } else {
3431 d->updateEditorData(topLeft, bottomRight);
3432 if (isVisible() && !d->delayedPendingLayout) {
3433 if (!topLeft.isValid() ||
3434 topLeft.parent() != bottomRight.parent() ||
3435 topLeft.row() > bottomRight.row() ||
3436 topLeft.column() > bottomRight.column()) {
3437 // invalid parameter - call update() to redraw all
3438 qWarning().nospace() << "dataChanged() called with an invalid index range:"
3439 << "\n topleft: " << topLeft
3440 << "\n bottomRight:" << bottomRight;
3441 d->viewport->update();
3442 } else if ((bottomRight.row() - topLeft.row() + 1LL) *
3443 (bottomRight.column() - topLeft.column() + 1LL) > d->updateThreshold) {
3444 // too many indices to check - force full update
3445 d->viewport->update();
3446 } else {
3447 const QRect updateRect = d->intersectedRect(d->viewport->rect(), topLeft, bottomRight);
3448 if (!updateRect.isEmpty())
3449 d->viewport->update(updateRect);
3450 }
3451 }
3452 }
3453
3454#if QT_CONFIG(accessibility)
3455 if (QAccessible::isActive()) {
3456 QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
3457 accessibleEvent.setFirstRow(topLeft.row());
3458 accessibleEvent.setFirstColumn(topLeft.column());
3459 accessibleEvent.setLastRow(bottomRight.row());
3460 accessibleEvent.setLastColumn(bottomRight.column());
3461 QAccessible::updateAccessibility(&accessibleEvent);
3462
3463 // send accessibility events as needed when current item is modified
3464 if (topLeft.isValid() && topLeft == bottomRight && topLeft == currentIndex())
3465 d->updateItemAccessibility(topLeft, roles);
3466 }
3467#endif
3468 d->updateGeometry();
3469}
3470
3471/*!
3472 This slot is called when rows are inserted. The new rows are those
3473 under the given \a parent from \a start to \a end inclusive. The
3474 base class implementation calls fetchMore() on the model to check
3475 for more data.
3476
3477 \sa rowsAboutToBeRemoved()
3478*/
3479void QAbstractItemView::rowsInserted(const QModelIndex &, int, int)
3480{
3481 if (!isVisible())
3482 d_func()->fetchMoreTimer.start(0, this); //fetch more later
3483 else
3484 updateEditorGeometries();
3485}
3486
3487/*!
3488 This slot is called when rows are about to be removed. The deleted rows are
3489 those under the given \a parent from \a start to \a end inclusive.
3490
3491 \sa rowsInserted()
3492*/
3493void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
3494{
3495 Q_D(QAbstractItemView);
3496
3497 setState(CollapsingState);
3498
3499 // Ensure one selected item in single selection mode.
3500 QModelIndex current = currentIndex();
3501 if (d->selectionMode == SingleSelection
3502 && current.isValid()
3503 && current.row() >= start
3504 && current.row() <= end
3505 && current.parent() == parent) {
3506 int totalToRemove = end - start + 1;
3507 if (d->model->rowCount(parent) <= totalToRemove) { // no more children
3508 QModelIndex index = parent;
3509 while (index != d->root && !d->isIndexEnabled(index))
3510 index = index.parent();
3511 if (index != d->root)
3512 setCurrentIndex(index);
3513 } else {
3514 int row = end + 1;
3515 QModelIndex next;
3516 const int rowCount = d->model->rowCount(parent);
3517 bool found = false;
3518 // find the next visible and enabled item
3519 while (row < rowCount && !found) {
3520 next = d->model->index(row++, current.column(), current.parent());
3521#ifdef QT_DEBUG
3522 if (!next.isValid()) {
3523 qWarning("Model unexpectedly returned an invalid index");
3524 break;
3525 }
3526#endif
3527 if (!isIndexHidden(next) && d->isIndexEnabled(next)) {
3528 found = true;
3529 break;
3530 }
3531 }
3532
3533 if (!found) {
3534 row = start - 1;
3535 // find the previous visible and enabled item
3536 while (row >= 0) {
3537 next = d->model->index(row--, current.column(), current.parent());
3538#ifdef QT_DEBUG
3539 if (!next.isValid()) {
3540 qWarning("Model unexpectedly returned an invalid index");
3541 break;
3542 }
3543#endif
3544 if (!isIndexHidden(next) && d->isIndexEnabled(next))
3545 break;
3546 }
3547 }
3548
3549 setCurrentIndex(next);
3550 }
3551 }
3552
3553 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
3554 const auto findDirectChildOf = [](const QModelIndex &parent, QModelIndex child)
3555 {
3556 while (child.isValid()) {
3557 const auto parentIndex = child.parent();
3558 if (parentIndex == parent)
3559 return child;
3560 child = parentIndex;
3561 }
3562 return QModelIndex();
3563 };
3564 QEditorIndexHash::iterator i = d->editorIndexHash.begin();
3565 while (i != d->editorIndexHash.end()) {
3566 const QModelIndex index = i.value();
3567 const QModelIndex directChild = findDirectChildOf(parent, index);
3568 if (directChild.isValid() && directChild.row() >= start && directChild.row() <= end) {
3569 QWidget *editor = i.key();
3570 QEditorInfo info = d->indexEditorHash.take(index);
3571 i = d->editorIndexHash.erase(i);
3572 if (info.widget)
3573 d->releaseEditor(editor, index);
3574 } else {
3575 ++i;
3576 }
3577 }
3578}
3579
3580/*!
3581 \internal
3582
3583 This slot is called when rows have been removed. The deleted
3584 rows are those under the given \a parent from \a start to \a end
3585 inclusive.
3586*/
3587void QAbstractItemViewPrivate::rowsRemoved(const QModelIndex &index, int start, int end)
3588{
3589 Q_UNUSED(index);
3590 Q_UNUSED(start);
3591 Q_UNUSED(end);
3592
3593 Q_Q(QAbstractItemView);
3594 if (q->isVisible())
3595 q->updateEditorGeometries();
3596 q->setState(QAbstractItemView::NoState);
3597#if QT_CONFIG(accessibility)
3598 if (QAccessible::isActive()) {
3599 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsRemoved);
3600 accessibleEvent.setFirstRow(start);
3601 accessibleEvent.setLastRow(end);
3602 QAccessible::updateAccessibility(&accessibleEvent);
3603 }
3604#endif
3605 updateGeometry();
3606}
3607
3608/*!
3609 \internal
3610
3611 This slot is called when columns are about to be removed. The deleted
3612 columns are those under the given \a parent from \a start to \a end
3613 inclusive.
3614*/
3615void QAbstractItemViewPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
3616{
3617 Q_Q(QAbstractItemView);
3618
3619 q->setState(QAbstractItemView::CollapsingState);
3620
3621 // Ensure one selected item in single selection mode.
3622 QModelIndex current = q->currentIndex();
3623 if (current.isValid()
3624 && selectionMode == QAbstractItemView::SingleSelection
3625 && current.column() >= start
3626 && current.column() <= end) {
3627 int totalToRemove = end - start + 1;
3628 if (model->columnCount(parent) < totalToRemove) { // no more columns
3629 QModelIndex index = parent;
3630 while (index.isValid() && !isIndexEnabled(index))
3631 index = index.parent();
3632 if (index.isValid())
3633 q->setCurrentIndex(index);
3634 } else {
3635 int column = end;
3636 QModelIndex next;
3637 const int columnCount = model->columnCount(current.parent());
3638 // find the next visible and enabled item
3639 while (column < columnCount) {
3640 next = model->index(current.row(), column++, current.parent());
3641#ifdef QT_DEBUG
3642 if (!next.isValid()) {
3643 qWarning("Model unexpectedly returned an invalid index");
3644 break;
3645 }
3646#endif
3647 if (!q->isIndexHidden(next) && isIndexEnabled(next))
3648 break;
3649 }
3650 q->setCurrentIndex(next);
3651 }
3652 }
3653
3654 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
3655 QEditorIndexHash::iterator it = editorIndexHash.begin();
3656 while (it != editorIndexHash.end()) {
3657 QModelIndex index = it.value();
3658 if (index.column() <= start && index.column() >= end && model->parent(index) == parent) {
3659 QWidget *editor = it.key();
3660 QEditorInfo info = indexEditorHash.take(it.value());
3661 it = editorIndexHash.erase(it);
3662 if (info.widget)
3663 releaseEditor(editor, index);
3664 } else {
3665 ++it;
3666 }
3667 }
3668
3669}
3670
3671/*!
3672 \internal
3673
3674 This slot is called when columns have been removed. The deleted
3675 rows are those under the given \a parent from \a start to \a end
3676 inclusive.
3677*/
3678void QAbstractItemViewPrivate::columnsRemoved(const QModelIndex &index, int start, int end)
3679{
3680 Q_UNUSED(index);
3681 Q_UNUSED(start);
3682 Q_UNUSED(end);
3683
3684 Q_Q(QAbstractItemView);
3685 if (q->isVisible())
3686 q->updateEditorGeometries();
3687 q->setState(QAbstractItemView::NoState);
3688#if QT_CONFIG(accessibility)
3689 if (QAccessible::isActive()) {
3690 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsRemoved);
3691 accessibleEvent.setFirstColumn(start);
3692 accessibleEvent.setLastColumn(end);
3693 QAccessible::updateAccessibility(&accessibleEvent);
3694 }
3695#endif
3696 updateGeometry();
3697}
3698
3699
3700/*!
3701 \internal
3702
3703 This slot is called when rows have been inserted.
3704*/
3705void QAbstractItemViewPrivate::rowsInserted(const QModelIndex &index, int start, int end)
3706{
3707 Q_UNUSED(index);
3708 Q_UNUSED(start);
3709 Q_UNUSED(end);
3710
3711#if QT_CONFIG(accessibility)
3712 Q_Q(QAbstractItemView);
3713 if (QAccessible::isActive()) {
3714 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsInserted);
3715 accessibleEvent.setFirstRow(start);
3716 accessibleEvent.setLastRow(end);
3717 QAccessible::updateAccessibility(&accessibleEvent);
3718 }
3719#endif
3720 updateGeometry();
3721}
3722
3723/*!
3724 \internal
3725
3726 This slot is called when columns have been inserted.
3727*/
3728void QAbstractItemViewPrivate::columnsInserted(const QModelIndex &index, int start, int end)
3729{
3730 Q_UNUSED(index);
3731 Q_UNUSED(start);
3732 Q_UNUSED(end);
3733
3734 Q_Q(QAbstractItemView);
3735 if (q->isVisible())
3736 q->updateEditorGeometries();
3737#if QT_CONFIG(accessibility)
3738 if (QAccessible::isActive()) {
3739 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsInserted);
3740 accessibleEvent.setFirstColumn(start);
3741 accessibleEvent.setLastColumn(end);
3742 QAccessible::updateAccessibility(&accessibleEvent);
3743 }
3744#endif
3745 updateGeometry();
3746}
3747
3748/*!
3749 \internal
3750*/
3751void QAbstractItemViewPrivate::modelDestroyed()
3752{
3753 model = QAbstractItemModelPrivate::staticEmptyModel();
3754 doDelayedReset();
3755}
3756
3757/*!
3758 \internal
3759
3760 This slot is called when the layout is changed.
3761*/
3762void QAbstractItemViewPrivate::layoutChanged()
3763{
3764 doDelayedItemsLayout();
3765#if QT_CONFIG(accessibility)
3766 Q_Q(QAbstractItemView);
3767 if (QAccessible::isActive()) {
3768 QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ModelReset);
3769 QAccessible::updateAccessibility(&accessibleEvent);
3770 }
3771#endif
3772}
3773
3774void QAbstractItemViewPrivate::rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
3775{
3776 layoutChanged();
3777}
3778
3779void QAbstractItemViewPrivate::columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
3780{
3781 layoutChanged();
3782}
3783
3784QRect QAbstractItemViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const
3785{
3786 Q_Q(const QAbstractItemView);
3787
3788 const auto parentIdx = topLeft.parent();
3789 QRect updateRect;
3790 for (int r = topLeft.row(); r <= bottomRight.row(); ++r) {
3791 for (int c = topLeft.column(); c <= bottomRight.column(); ++c)
3792 updateRect |= q->visualRect(model->index(r, c, parentIdx));
3793 }
3794 return rect.intersected(updateRect);
3795}
3796
3797/*!
3798 This slot is called when the selection is changed. The previous
3799 selection (which may be empty), is specified by \a deselected, and the
3800 new selection by \a selected.
3801
3802 \sa setSelection()
3803*/
3804void QAbstractItemView::selectionChanged(const QItemSelection &selected,
3805 const QItemSelection &deselected)
3806{
3807 Q_D(QAbstractItemView);
3808 if (isVisible() && updatesEnabled()) {
3809 d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected));
3810 }
3811}
3812
3813/*!
3814 This slot is called when a new item becomes the current item.
3815 The previous current item is specified by the \a previous index, and the new
3816 item by the \a current index.
3817
3818 If you want to know about changes to items see the
3819 dataChanged() signal.
3820*/
3821void QAbstractItemView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3822{
3823 Q_D(QAbstractItemView);
3824 Q_ASSERT(d->model);
3825
3826 QPersistentModelIndex persistentCurrent(current); // in case commitData() moves things around (QTBUG-127852)
3827
3828 if (previous.isValid()) {
3829 QModelIndex buddy = d->model->buddy(previous);
3830 QWidget *editor = d->editorForIndex(buddy).widget.data();
3831 if (isVisible()) {
3832 update(previous);
3833 }
3834 if (editor && !d->persistent.contains(editor)) {
3835 const bool rowChanged = current.row() != previous.row();
3836 commitData(editor); // might invalidate previous, don't use after this line (QTBUG-127852)
3837 if (rowChanged)
3838 closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
3839 else
3840 closeEditor(editor, QAbstractItemDelegate::NoHint);
3841 }
3842 }
3843
3844 const QModelIndex newCurrent = persistentCurrent;
3845
3846 QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, nullptr);
3847 if ((command & QItemSelectionModel::Current) == 0)
3848 d->currentSelectionStartIndex = newCurrent;
3849
3850 if (newCurrent.isValid() && !d->autoScrollTimer.isActive()) {
3851 if (isVisible()) {
3852 if (d->autoScroll)
3853 scrollTo(newCurrent);
3854 update(newCurrent);
3855 edit(newCurrent, CurrentChanged, nullptr);
3856 if (newCurrent.row() == (d->model->rowCount(d->root) - 1))
3857 d->fetchMore();
3858 } else {
3859 d->shouldScrollToCurrentOnShow = d->autoScroll;
3860 }
3861 }
3862 setAttribute(Qt::WA_InputMethodEnabled, (newCurrent.isValid() && (newCurrent.flags() & Qt::ItemIsEditable)));
3863}
3864
3865#if QT_CONFIG(draganddrop)
3866/*!
3867 Starts a drag by calling drag->exec() using the given \a supportedActions.
3868*/
3869void QAbstractItemView::startDrag(Qt::DropActions supportedActions)
3870{
3871 Q_D(QAbstractItemView);
3872 QModelIndexList indexes = d->selectedDraggableIndexes();
3873 if (indexes.size() > 0) {
3874 QMimeData *data = d->model->mimeData(indexes);
3875 if (!data)
3876 return;
3877 QRect rect;
3878 QPixmap pixmap = d->renderToPixmap(indexes, &rect);
3879 rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
3880 QDrag *drag = new QDrag(this);
3881 drag->setPixmap(pixmap);
3882 drag->setMimeData(data);
3883 drag->setHotSpot(d->pressedPosition - rect.topLeft());
3884 Qt::DropAction defaultDropAction = Qt::IgnoreAction;
3885 if (dragDropMode() == InternalMove)
3886 supportedActions &= ~Qt::CopyAction;
3887 if (d->defaultDropAction != Qt::IgnoreAction && (supportedActions & d->defaultDropAction))
3888 defaultDropAction = d->defaultDropAction;
3889 else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove)
3890 defaultDropAction = Qt::CopyAction;
3891 d->dropEventMoved = false;
3892 if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction && !d->dropEventMoved) {
3893 if (dragDropMode() != InternalMove || drag->target() == viewport())
3894 d->clearOrRemove();
3895 }
3896 d->dropEventMoved = false;
3897 // Reset the drop indicator
3898 d->dropIndicatorRect = QRect();
3899 d->dropIndicatorPosition = OnItem;
3900 }
3901}
3902#endif // QT_CONFIG(draganddrop)
3903
3904/*!
3905 \since 6.0
3906
3907 Initialize the \a option structure with the view's palette, font, state,
3908 alignments etc.
3909
3910 \note Implementations of this methods should check the \l{QStyleOption::}{version}
3911 of the structure received, populate all members the implementation is familiar with,
3912 and set the version member to the one supported by the implementation before returning.
3913*/
3914void QAbstractItemView::initViewItemOption(QStyleOptionViewItem *option) const
3915{
3916 Q_D(const QAbstractItemView);
3917 option->initFrom(this);
3918 option->state &= ~QStyle::State_MouseOver;
3919 option->font = font();
3920
3921 // On mac the focus appearance follows window activation
3922 // not widget activation
3923 if (!hasFocus())
3924 option->state &= ~QStyle::State_Active;
3925
3926 option->state &= ~QStyle::State_HasFocus;
3927 if (d->iconSize.isValid()) {
3928 option->decorationSize = d->iconSize;
3929 } else {
3930 int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
3931 option->decorationSize = QSize(pm, pm);
3932 }
3933 option->decorationPosition = QStyleOptionViewItem::Left;
3934 option->decorationAlignment = Qt::AlignCenter;
3935 option->displayAlignment = Qt::AlignLeft|Qt::AlignVCenter;
3936 option->textElideMode = d->textElideMode;
3937 option->rect = QRect();
3938 option->showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, nullptr, this);
3939 if (d->wrapItemText)
3940 option->features = QStyleOptionViewItem::WrapText;
3941 option->locale = locale();
3942 option->locale.setNumberOptions(QLocale::OmitGroupSeparator);
3943 option->widget = this;
3944}
3945
3946/*!
3947 Returns the item view's state.
3948
3949 \sa setState()
3950*/
3951QAbstractItemView::State QAbstractItemView::state() const
3952{
3953 Q_D(const QAbstractItemView);
3954 return d->state;
3955}
3956
3957/*!
3958 Sets the item view's state to the given \a state.
3959
3960 \sa state()
3961*/
3962void QAbstractItemView::setState(State state)
3963{
3964 Q_D(QAbstractItemView);
3965 d->state = state;
3966}
3967
3968/*!
3969 Schedules a layout of the items in the view to be executed when the
3970 event processing starts.
3971
3972 Even if scheduleDelayedItemsLayout() is called multiple times before
3973 events are processed, the view will only do the layout once.
3974
3975 \sa executeDelayedItemsLayout()
3976*/
3977void QAbstractItemView::scheduleDelayedItemsLayout()
3978{
3979 Q_D(QAbstractItemView);
3980 d->doDelayedItemsLayout();
3981}
3982
3983/*!
3984 Executes the scheduled layouts without waiting for the event processing
3985 to begin.
3986
3987 \sa scheduleDelayedItemsLayout()
3988*/
3989void QAbstractItemView::executeDelayedItemsLayout()
3990{
3991 Q_D(QAbstractItemView);
3992 d->executePostedLayout();
3993}
3994
3995/*!
3996 Marks the given \a region as dirty and schedules it to be updated.
3997 You only need to call this function if you are implementing
3998 your own view subclass.
3999
4000 \sa scrollDirtyRegion(), dirtyRegionOffset()
4001*/
4002
4003void QAbstractItemView::setDirtyRegion(const QRegion &region)
4004{
4005 Q_D(QAbstractItemView);
4006 d->setDirtyRegion(region);
4007}
4008
4009/*!
4010 Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the
4011 opposite direction. You only need to call this function if you are implementing a scrolling
4012 viewport in your view subclass.
4013
4014 If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function
4015 before you call QWidget::scroll() on the viewport. Alternatively, just call update().
4016
4017 \sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion()
4018*/
4019void QAbstractItemView::scrollDirtyRegion(int dx, int dy)
4020{
4021 Q_D(QAbstractItemView);
4022 d->scrollDirtyRegion(dx, dy);
4023}
4024
4025/*!
4026 Returns the offset of the dirty regions in the view.
4027
4028 If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of
4029 QAbstractItemView, you should translate the area given by the paint event with
4030 the offset returned from this function.
4031
4032 \sa scrollDirtyRegion(), setDirtyRegion()
4033*/
4034QPoint QAbstractItemView::dirtyRegionOffset() const
4035{
4036 Q_D(const QAbstractItemView);
4037 return d->scrollDelayOffset;
4038}
4039
4040/*!
4041 \internal
4042*/
4043void QAbstractItemView::startAutoScroll()
4044{
4045 d_func()->startAutoScroll();
4046}
4047
4048/*!
4049 \internal
4050*/
4051void QAbstractItemView::stopAutoScroll()
4052{
4053 d_func()->stopAutoScroll();
4054}
4055
4056/*!
4057 \internal
4058*/
4059void QAbstractItemView::doAutoScroll()
4060{
4061 // find how much we should scroll with
4062 Q_D(QAbstractItemView);
4063 QScrollBar *verticalScroll = verticalScrollBar();
4064 QScrollBar *horizontalScroll = horizontalScrollBar();
4065
4066 // QHeaderView does not (normally) have scrollbars
4067 // It needs to use its parents scroll instead
4068 QHeaderView *hv = qobject_cast<QHeaderView*>(this);
4069 if (hv) {
4070 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget());
4071 if (parent) {
4072 if (hv->orientation() == Qt::Horizontal) {
4073 if (!hv->horizontalScrollBar() || !hv->horizontalScrollBar()->isVisible())
4074 horizontalScroll = parent->horizontalScrollBar();
4075 } else {
4076 if (!hv->verticalScrollBar() || !hv->verticalScrollBar()->isVisible())
4077 verticalScroll = parent->verticalScrollBar();
4078 }
4079 }
4080 }
4081
4082 const int verticalStep = verticalScroll->pageStep();
4083 const int horizontalStep = horizontalScroll->pageStep();
4084 if (d->autoScrollCount < qMax(verticalStep, horizontalStep))
4085 ++d->autoScrollCount;
4086
4087 const int margin = d->autoScrollMargin;
4088 const int verticalValue = verticalScroll->value();
4089 const int horizontalValue = horizontalScroll->value();
4090
4091 const QPoint pos = d->draggedPosition - d->offset();
4092 const QRect area = QWidgetPrivate::get(d->viewport)->clipRect();
4093
4094 // do the scrolling if we are in the scroll margins
4095 if (pos.y() - area.top() < margin)
4096 verticalScroll->setValue(verticalValue - d->autoScrollCount);
4097 else if (area.bottom() - pos.y() < margin)
4098 verticalScroll->setValue(verticalValue + d->autoScrollCount);
4099 if (pos.x() - area.left() < margin)
4100 horizontalScroll->setValue(horizontalValue - d->autoScrollCount);
4101 else if (area.right() - pos.x() < margin)
4102 horizontalScroll->setValue(horizontalValue + d->autoScrollCount);
4103 // if nothing changed, stop scrolling
4104 const bool verticalUnchanged = (verticalValue == verticalScroll->value());
4105 const bool horizontalUnchanged = (horizontalValue == horizontalScroll->value());
4106 if (verticalUnchanged && horizontalUnchanged) {
4107 stopAutoScroll();
4108 } else {
4109#if QT_CONFIG(draganddrop)
4110 d->dropIndicatorRect = QRect();
4111 d->dropIndicatorPosition = QAbstractItemView::OnViewport;
4112#endif
4113 switch (state()) {
4114 case QAbstractItemView::DragSelectingState: {
4115 // mouseMoveEvent updates the drag-selection rectangle, so fake an event. This also
4116 // updates draggedPosition taking the now scrolled viewport into account.
4117 const QPoint globalPos = d->viewport->mapToGlobal(pos);
4118 const QPoint windowPos = window()->mapFromGlobal(globalPos);
4119 QMouseEvent mm(QEvent::MouseMove, pos, windowPos, globalPos,
4120 Qt::NoButton, Qt::LeftButton, d->pressedModifiers,
4121 Qt::MouseEventSynthesizedByQt);
4122 QApplication::sendEvent(viewport(), &mm);
4123 break;
4124 }
4125 case QAbstractItemView::DraggingState: {
4126 // we can't simulate mouse (it would throw off the drag'n'drop state logic) or drag
4127 // (we don't have the mime data or the actions) move events during drag'n'drop, so
4128 // update our dragged position manually after the scroll. "pos" is the old
4129 // draggedPosition - d->offset(), and d->offset() is now updated after scrolling, so
4130 // pos + d->offset() gives us the new position.
4131 d->draggedPosition = pos + d->offset();
4132 break;
4133 }
4134 default:
4135 break;
4136 }
4137 d->viewport->update();
4138 }
4139}
4140
4141/*!
4142 Returns the SelectionFlags to be used when updating a selection model
4143 for the specified \a index. The result depends on the current
4144 selectionMode(), and on the user input event \a event, which can be
4145 \nullptr.
4146
4147 Reimplement this function to define your own selection behavior.
4148
4149 \sa setSelection()
4150*/
4151QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index,
4152 const QEvent *event) const
4153{
4154 Q_D(const QAbstractItemView);
4155 Qt::KeyboardModifiers keyModifiers = event && event->isInputEvent()
4156 ? static_cast<const QInputEvent*>(event)->modifiers()
4157 : Qt::NoModifier;
4158 switch (d->selectionMode) {
4159 case NoSelection: // Never update selection model
4160 return QItemSelectionModel::NoUpdate;
4161 case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate
4162 if (event) {
4163 switch (event->type()) {
4164 case QEvent::MouseButtonPress:
4165 // press with any modifiers on a selected item does nothing
4166 if (d->pressedAlreadySelected)
4167 return QItemSelectionModel::NoUpdate;
4168 break;
4169 case QEvent::MouseButtonRelease:
4170 // clicking into area with no items does nothing
4171 if (!index.isValid())
4172 return QItemSelectionModel::NoUpdate;
4173 Q_FALLTHROUGH();
4174 case QEvent::KeyPress:
4175 // ctrl-release on selected item deselects
4176 if ((keyModifiers & Qt::ControlModifier) && d->selectionModel->isSelected(index))
4177 return QItemSelectionModel::Deselect | d->selectionBehaviorFlags();
4178 break;
4179 default:
4180 break;
4181 }
4182 }
4183 return QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
4184 case MultiSelection:
4185 return d->multiSelectionCommand(index, event);
4186 case ExtendedSelection:
4187 return d->extendedSelectionCommand(index, event);
4188 case ContiguousSelection:
4189 return d->contiguousSelectionCommand(index, event);
4190 }
4191 return QItemSelectionModel::NoUpdate;
4192}
4193
4194QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand(
4195 const QModelIndex &index, const QEvent *event) const
4196{
4197 Q_UNUSED(index);
4198
4199 if (event) {
4200 switch (event->type()) {
4201 case QEvent::KeyPress:
4202 if (static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Space
4203 || static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Select)
4204 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4205 break;
4206 case QEvent::MouseButtonPress:
4207 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) {
4208 // since the press might start a drag, deselect only on release
4209 if (!pressedAlreadySelected
4210#if QT_CONFIG(draganddrop)
4211 || !dragEnabled || !isIndexDragEnabled(index)
4212#endif
4213 )
4214 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); // toggle
4215 }
4216 break;
4217 case QEvent::MouseButtonRelease:
4218 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) {
4219 if (pressedAlreadySelected
4220#if QT_CONFIG(draganddrop)
4221 && dragEnabled && isIndexDragEnabled(index)
4222#endif
4223 && index == pressedIndex)
4224 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4225 return QItemSelectionModel::NoUpdate|selectionBehaviorFlags(); // finalize
4226 }
4227 break;
4228 case QEvent::MouseMove:
4229 if (static_cast<const QMouseEvent*>(event)->buttons() & Qt::LeftButton)
4230 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); // toggle drag select
4231 break;
4232 default:
4233 break;
4234 }
4235 return QItemSelectionModel::NoUpdate;
4236 }
4237
4238 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4239}
4240
4241QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand(
4242 const QModelIndex &index, const QEvent *event) const
4243{
4244 Qt::KeyboardModifiers modifiers = event && event->isInputEvent()
4245 ? static_cast<const QInputEvent*>(event)->modifiers()
4246 : QGuiApplication::keyboardModifiers();
4247 if (event) {
4248 switch (event->type()) {
4249 case QEvent::MouseMove: {
4250 // Toggle on MouseMove
4251 if (modifiers & Qt::ControlModifier)
4252 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags();
4253 break;
4254 }
4255 case QEvent::MouseButtonPress: {
4256 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
4257 const bool rightButtonPressed = button & Qt::RightButton;
4258 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
4259 const bool controlKeyPressed = modifiers & Qt::ControlModifier;
4260 const bool indexIsSelected = selectionModel->isSelected(index);
4261 if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed)
4262 return QItemSelectionModel::NoUpdate;
4263 if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected)
4264 return QItemSelectionModel::NoUpdate;
4265 if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed)
4266 return QItemSelectionModel::Clear;
4267 if (!index.isValid())
4268 return QItemSelectionModel::NoUpdate;
4269 // since the press might start a drag, deselect only on release
4270 if (controlKeyPressed && !rightButtonPressed && pressedAlreadySelected
4271#if QT_CONFIG(draganddrop)
4272 && dragEnabled && isIndexDragEnabled(index)
4273#endif
4274 ) {
4275 return QItemSelectionModel::NoUpdate;
4276 }
4277 break;
4278 }
4279 case QEvent::MouseButtonRelease: {
4280 // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area
4281 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
4282 const bool rightButtonPressed = button & Qt::RightButton;
4283 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
4284 const bool controlKeyPressed = modifiers & Qt::ControlModifier;
4285 if (((index == pressedIndex && selectionModel->isSelected(index))
4286 || !index.isValid()) && state != QAbstractItemView::DragSelectingState
4287 && !shiftKeyPressed && !controlKeyPressed && (!rightButtonPressed || !index.isValid()))
4288 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4289 if (index == pressedIndex && controlKeyPressed && !rightButtonPressed
4290#if QT_CONFIG(draganddrop)
4291 && dragEnabled && isIndexDragEnabled(index)
4292#endif
4293 ) {
4294 break;
4295 }
4296 return QItemSelectionModel::NoUpdate;
4297 }
4298 case QEvent::KeyPress: {
4299 // NoUpdate on Key movement and Ctrl
4300 switch (static_cast<const QKeyEvent*>(event)->key()) {
4301 case Qt::Key_Backtab:
4302 modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab
4303 Q_FALLTHROUGH();
4304 case Qt::Key_Down:
4305 case Qt::Key_Up:
4306 case Qt::Key_Left:
4307 case Qt::Key_Right:
4308 case Qt::Key_Home:
4309 case Qt::Key_End:
4310 case Qt::Key_PageUp:
4311 case Qt::Key_PageDown:
4312 case Qt::Key_Tab:
4313 if (modifiers & Qt::ControlModifier)
4314 return QItemSelectionModel::NoUpdate;
4315 break;
4316 case Qt::Key_Select:
4317 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4318 case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space
4319 if (modifiers & Qt::ControlModifier)
4320 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4321 return QItemSelectionModel::Select|selectionBehaviorFlags();
4322 default:
4323 break;
4324 }
4325 break;
4326 }
4327 default:
4328 break;
4329 }
4330 }
4331
4332 if (modifiers & Qt::ShiftModifier)
4333 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4334 if (modifiers & Qt::ControlModifier)
4335 return QItemSelectionModel::Toggle|selectionBehaviorFlags();
4336 if (state == QAbstractItemView::DragSelectingState) {
4337 //when drag-selecting we need to clear any previous selection and select the current one
4338 return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4339 }
4340
4341 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4342}
4343
4344QItemSelectionModel::SelectionFlags
4345QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index,
4346 const QEvent *event) const
4347{
4348 QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event);
4349 const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select
4350 | QItemSelectionModel::Deselect | QItemSelectionModel::Toggle
4351 | QItemSelectionModel::Current;
4352
4353 switch (flags & Mask) {
4354 case QItemSelectionModel::Clear:
4355 case QItemSelectionModel::ClearAndSelect:
4356 case QItemSelectionModel::SelectCurrent:
4357 return flags;
4358 case QItemSelectionModel::NoUpdate:
4359 if (event &&
4360 (event->type() == QEvent::MouseButtonPress
4361 || event->type() == QEvent::MouseButtonRelease))
4362 return flags;
4363 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
4364 default:
4365 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
4366 }
4367}
4368
4369void QAbstractItemViewPrivate::fetchMore()
4370{
4371 fetchMoreTimer.stop();
4372 if (!model->canFetchMore(root))
4373 return;
4374 int last = model->rowCount(root) - 1;
4375 if (last < 0) {
4376 model->fetchMore(root);
4377 return;
4378 }
4379
4380 QModelIndex index = model->index(last, 0, root);
4381 QRect rect = q_func()->visualRect(index);
4382 if (viewport->rect().intersects(rect))
4383 model->fetchMore(root);
4384}
4385
4386bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger,
4387 const QModelIndex &index) const
4388{
4389 if (!index.isValid())
4390 return false;
4391 Qt::ItemFlags flags = model->flags(index);
4392 if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0))
4393 return false;
4394 if (state == QAbstractItemView::EditingState)
4395 return false;
4396 if (hasEditor(index))
4397 return false;
4398 if (trigger == QAbstractItemView::AllEditTriggers) // force editing
4399 return true;
4400 if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked
4401 && !selectionModel->isSelected(index))
4402 return false;
4403 return (trigger & editTriggers);
4404}
4405
4406bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger,
4407 const QEvent *event) const
4408{
4409 if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed)
4410 return false;
4411
4412 switch (event->type()) {
4413 case QEvent::KeyPress:
4414 case QEvent::MouseButtonDblClick:
4415 case QEvent::MouseButtonPress:
4416 case QEvent::MouseButtonRelease:
4417 case QEvent::MouseMove:
4418 return true;
4419 default:
4420 break;
4421 };
4422
4423 return false;
4424}
4425
4426bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const
4427{
4428 if (!autoScroll)
4429 return false;
4430 const QRect area = QWidgetPrivate::get(viewport)->clipRect();
4431 return (pos.y() - area.top() < autoScrollMargin)
4432 || (area.bottom() - pos.y() < autoScrollMargin)
4433 || (pos.x() - area.left() < autoScrollMargin)
4434 || (area.right() - pos.x() < autoScrollMargin);
4435}
4436
4437void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay)
4438{
4439 if (!delayedPendingLayout) {
4440 delayedPendingLayout = true;
4441 delayedLayout.start(delay, q_func());
4442 }
4443}
4444
4445void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const
4446{
4447 delayedLayout.stop();
4448 delayedPendingLayout = false;
4449}
4450
4451void QAbstractItemViewPrivate::updateGeometry()
4452{
4453 Q_Q(QAbstractItemView);
4454 if (sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored)
4455 return;
4456 if (sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents || !shownOnce)
4457 q->updateGeometry();
4458}
4459
4460/*
4461 Handles selection of content for some editors containing QLineEdit.
4462
4463 ### Qt 7 This should be done by a virtual method in QAbstractItemDelegate.
4464*/
4465void QAbstractItemViewPrivate::selectAllInEditor(QWidget *editor)
4466{
4467 while (QWidget *fp = editor->focusProxy())
4468 editor = fp;
4469
4470#if QT_CONFIG(lineedit)
4471 if (QLineEdit *le = qobject_cast<QLineEdit*>(editor))
4472 le->selectAll();
4473#endif
4474#if QT_CONFIG(spinbox)
4475 if (QSpinBox *sb = qobject_cast<QSpinBox*>(editor))
4476 sb->selectAll();
4477 else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(editor))
4478 dsb->selectAll();
4479#endif
4480}
4481
4482QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index,
4483 const QStyleOptionViewItem &options)
4484{
4485 Q_Q(QAbstractItemView);
4486 QWidget *w = editorForIndex(index).widget.data();
4487 if (!w) {
4488 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4489 if (!delegate)
4490 return nullptr;
4491 w = delegate->createEditor(viewport, options, index);
4492 if (w) {
4493 w->installEventFilter(delegate);
4494 QObject::connect(w, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
4495 delegate->updateEditorGeometry(w, options, index);
4496 delegate->setEditorData(w, index);
4497 addEditor(index, w, false);
4498 if (w->parent() == viewport)
4499 QWidget::setTabOrder(q, w);
4500
4501 selectAllInEditor(w);
4502 }
4503 }
4504
4505 return w;
4506}
4507
4508void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br)
4509{
4510 Q_Q(QAbstractItemView);
4511 // we are counting on having relatively few editors
4512 const bool checkIndexes = tl.isValid() && br.isValid();
4513 const QModelIndex parent = tl.parent();
4514 // QTBUG-25370: We need to copy the indexEditorHash, because while we're
4515 // iterating over it, we are calling methods which can allow user code to
4516 // call a method on *this which can modify the member indexEditorHash.
4517 const QIndexEditorHash indexEditorHashCopy = indexEditorHash;
4518 QIndexEditorHash::const_iterator it = indexEditorHashCopy.constBegin();
4519 for (; it != indexEditorHashCopy.constEnd(); ++it) {
4520 QWidget *editor = it.value().widget.data();
4521 const QModelIndex index = it.key();
4522 if (it.value().isStatic || !editor || !index.isValid() ||
4523 (checkIndexes
4524 && (index.row() < tl.row() || index.row() > br.row()
4525 || index.column() < tl.column() || index.column() > br.column()
4526 || index.parent() != parent)))
4527 continue;
4528
4529 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4530 if (delegate) {
4531 delegate->setEditorData(editor, index);
4532 }
4533 }
4534}
4535
4536/*!
4537 \internal
4538
4539 In DND if something has been moved then this is called.
4540 Typically this means you should "remove" the selected item or row,
4541 but the behavior is view-dependent (table just clears the selected indexes for example).
4542
4543 Either remove the selected rows or clear them
4544*/
4545void QAbstractItemViewPrivate::clearOrRemove()
4546{
4547#if QT_CONFIG(draganddrop)
4548 const QItemSelection selection = selectionModel->selection();
4549 QList<QItemSelectionRange>::const_iterator it = selection.constBegin();
4550
4551 if (!overwrite) {
4552 for (; it != selection.constEnd(); ++it) {
4553 QModelIndex parent = (*it).parent();
4554 if ((*it).left() != 0)
4555 continue;
4556 if ((*it).right() != (model->columnCount(parent) - 1))
4557 continue;
4558 int count = (*it).bottom() - (*it).top() + 1;
4559 model->removeRows((*it).top(), count, parent);
4560 }
4561 } else {
4562 // we can't remove the rows so reset the items (i.e. the view is like a table)
4563 QModelIndexList list = selection.indexes();
4564 for (int i=0; i < list.size(); ++i) {
4565 QModelIndex index = list.at(i);
4566 QMap<int, QVariant> roles = model->itemData(index);
4567 for (QMap<int, QVariant>::Iterator it = roles.begin(); it != roles.end(); ++it)
4568 it.value() = QVariant();
4569 model->setItemData(index, roles);
4570 }
4571 }
4572#endif
4573}
4574
4575/*!
4576 \internal
4577
4578 When persistent aeditor gets/loses focus, we need to check
4579 and setcorrectly the current index.
4580*/
4581void QAbstractItemViewPrivate::checkPersistentEditorFocus()
4582{
4583 Q_Q(QAbstractItemView);
4584 if (QWidget *widget = QApplication::focusWidget()) {
4585 if (persistent.contains(widget)) {
4586 //a persistent editor has gained the focus
4587 QModelIndex index = indexForEditor(widget);
4588 if (selectionModel->currentIndex() != index)
4589 q->setCurrentIndex(index);
4590 }
4591 }
4592}
4593
4594
4595const QEditorInfo & QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const
4596{
4597 static QEditorInfo nullInfo;
4598
4599 // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
4600 if (indexEditorHash.isEmpty())
4601 return nullInfo;
4602
4603 QIndexEditorHash::const_iterator it = indexEditorHash.find(index);
4604 if (it == indexEditorHash.end())
4605 return nullInfo;
4606
4607 return it.value();
4608}
4609
4610bool QAbstractItemViewPrivate::hasEditor(const QModelIndex &index) const
4611{
4612 // Search's implicit cast (QModelIndex to QPersistentModelIndex) is slow; use cheap pre-test to avoid when we can.
4613 return !indexEditorHash.isEmpty() && indexEditorHash.contains(index);
4614}
4615
4616QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const
4617{
4618 // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
4619 if (indexEditorHash.isEmpty())
4620 return QModelIndex();
4621
4622 QEditorIndexHash::const_iterator it = editorIndexHash.find(editor);
4623 if (it == editorIndexHash.end())
4624 return QModelIndex();
4625
4626 return it.value();
4627}
4628
4629void QAbstractItemViewPrivate::removeEditor(QWidget *editor)
4630{
4631 Q_Q(QAbstractItemView);
4632 if (editor)
4633 QObject::disconnect(editor, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
4634 const auto it = editorIndexHash.constFind(editor);
4635 if (it != editorIndexHash.cend()) {
4636 indexEditorHash.remove(it.value());
4637 editorIndexHash.erase(it);
4638 }
4639}
4640
4641void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic)
4642{
4643 editorIndexHash.insert(editor, index);
4644 indexEditorHash.insert(index, QEditorInfo(editor, isStatic));
4645}
4646
4647bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const
4648{
4649 Q_Q(const QAbstractItemView);
4650 QModelIndex buddy = model->buddy(index);
4651 QStyleOptionViewItem options;
4652 q->initViewItemOption(&options);
4653 options.rect = q->visualRect(buddy);
4654 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
4655 QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index);
4656 return (event && delegate && delegate->editorEvent(event, model, options, buddy));
4657}
4658
4659bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event)
4660{
4661 Q_Q(QAbstractItemView);
4662
4663 QModelIndex buddy = model->buddy(index);
4664 QStyleOptionViewItem options;
4665 q->initViewItemOption(&options);
4666 options.rect = q->visualRect(buddy);
4667 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
4668
4669 QWidget *w = editor(buddy, options);
4670 if (!w)
4671 return false;
4672
4673 q->setState(QAbstractItemView::EditingState);
4674 w->show();
4675 if (!waitForIMCommit)
4676 w->setFocus();
4677 else
4678 q->updateMicroFocus();
4679
4680 if (event)
4681 QCoreApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event);
4682
4683 return true;
4684}
4685
4686/*
4687 \internal
4688
4689 returns the pair QRect/QModelIndex that should be painted on the viewports's rect
4690*/
4691
4692QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
4693{
4694 Q_ASSERT(r);
4695 Q_Q(const QAbstractItemView);
4696 QRect &rect = *r;
4697 const QRect viewportRect = viewport->rect();
4698 QItemViewPaintPairs ret;
4699 for (const auto &index : indexes) {
4700 const QRect current = q->visualRect(index);
4701 if (current.intersects(viewportRect)) {
4702 ret.append({current, index});
4703 rect |= current;
4704 }
4705 }
4706 QRect clipped = rect & viewportRect;
4707 rect.setLeft(clipped.left());
4708 rect.setRight(clipped.right());
4709 return ret;
4710}
4711
4712QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const
4713{
4714 Q_Q(const QAbstractItemView);
4715 Q_ASSERT(r);
4716 QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r);
4717 if (paintPairs.isEmpty())
4718 return QPixmap();
4719
4720 QWindow *window = windowHandle(WindowHandleMode::Closest);
4721 const qreal scale = window ? window->devicePixelRatio() : qreal(1);
4722
4723 QPixmap pixmap(r->size() * scale);
4724 pixmap.setDevicePixelRatio(scale);
4725
4726 pixmap.fill(Qt::transparent);
4727 QPainter painter(&pixmap);
4728 painter.setLayoutDirection(q->layoutDirection());
4729 QStyleOptionViewItem option;
4730 q->initViewItemOption(&option);
4731 option.state |= QStyle::State_Selected;
4732 for (int j = 0; j < paintPairs.size(); ++j) {
4733 option.rect = paintPairs.at(j).rect.translated(-r->topLeft());
4734 const QModelIndex &current = paintPairs.at(j).index;
4735 adjustViewOptionsForIndex(&option, current);
4736 q->itemDelegateForIndex(current)->paint(&painter, option, current);
4737 }
4738 return pixmap;
4739}
4740
4741void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
4742{
4743 if (!selectionModel)
4744 return;
4745 if (!model->hasChildren(root))
4746 return;
4747
4748 QItemSelection selection;
4749 QModelIndex tl = model->index(0, 0, root);
4750 QModelIndex br = model->index(model->rowCount(root) - 1,
4751 model->columnCount(root) - 1,
4752 root);
4753 selection.append(QItemSelectionRange(tl, br));
4754 selectionModel->select(selection, command);
4755}
4756
4757#if QT_CONFIG(draganddrop)
4758QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const
4759{
4760 Q_Q(const QAbstractItemView);
4761 QModelIndexList indexes = q->selectedIndexes();
4762 auto isNotDragEnabled = [this](const QModelIndex &index) {
4763 return !isIndexDragEnabled(index);
4764 };
4765 indexes.removeIf(isNotDragEnabled);
4766 return indexes;
4767}
4768
4769void QAbstractItemViewPrivate::maybeStartDrag(QPoint eventPosition)
4770{
4771 Q_Q(QAbstractItemView);
4772
4773 const QPoint topLeft = pressedPosition - offset();
4774 if ((topLeft - eventPosition).manhattanLength() > QApplication::startDragDistance()) {
4775 pressedIndex = QModelIndex();
4776 q->startDrag(model->supportedDragActions());
4777 q->setState(QAbstractItemView::NoState); // the startDrag will return when the dnd operation
4778 // is done
4779 q->stopAutoScroll();
4780 }
4781}
4782#endif
4783
4784/*!
4785 \reimp
4786*/
4787
4788bool QAbstractItemView::eventFilter(QObject *object, QEvent *event)
4789{
4790 Q_D(QAbstractItemView);
4791 if (object == this || object == viewport() || event->type() != QEvent::FocusIn)
4792 return QAbstractScrollArea::eventFilter(object, event);
4793 QWidget *widget = qobject_cast<QWidget *>(object);
4794 // If it is not a persistent widget then we did not install
4795 // the event filter on it, so assume a base implementation is
4796 // filtering
4797 if (!widget || !d->persistent.contains(widget))
4798 return QAbstractScrollArea::eventFilter(object, event);
4799 setCurrentIndex(d->indexForEditor(widget));
4800 return false;
4801}
4802
4803QT_END_NAMESPACE
4804
4805#include "moc_qabstractitemview.cpp"