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