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