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

source code of qtbase/src/widgets/itemviews/qabstractitemview.cpp