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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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