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