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