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