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

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