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