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