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