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

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