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