/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qabstractitemview.h" | |
#ifndef QT_NO_ITEMVIEWS | |
#include <qpointer.h> | |
#include <qapplication.h> | |
#include <qclipboard.h> | |
#include <qpainter.h> | |
#include <qstyle.h> | |
#include <qdrag.h> | |
#include <qevent.h> | |
#include <qscrollbar.h> | |
#include <qwhatsthis.h> | |
#include <qtooltip.h> | |
#include <qdatetime.h> | |
#include <qlineedit.h> | |
#include <qspinbox.h> | |
#include <qstyleditemdelegate.h> | |
#include <private/qabstractitemview_p.h> | |
#include <private/qabstractitemmodel_p.h> | |
#ifndef QT_NO_ACCESSIBILITY | |
#include <qaccessible.h> | |
#endif | |
#include <private/qsoftkeymanager_p.h> | |
QT_BEGIN_NAMESPACE | |
QAbstractItemViewPrivate::QAbstractItemViewPrivate() | |
: model(QAbstractItemModelPrivate::staticEmptyModel()), | |
itemDelegate(0), | |
selectionModel(0), | |
ctrlDragSelectionFlag(QItemSelectionModel::NoUpdate), | |
noSelectionOnMousePress(false), | |
selectionMode(QAbstractItemView::ExtendedSelection), | |
selectionBehavior(QAbstractItemView::SelectItems), | |
currentlyCommittingEditor(0), | |
pressedModifiers(Qt::NoModifier), | |
pressedPosition(QPoint(-1, -1)), | |
pressedAlreadySelected(false), | |
viewportEnteredNeeded(false), | |
state(QAbstractItemView::NoState), | |
editTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed), | |
lastTrigger(QAbstractItemView::NoEditTriggers), | |
tabKeyNavigation(false), | |
#ifndef QT_NO_DRAGANDDROP | |
showDropIndicator(true), | |
dragEnabled(false), | |
dragDropMode(QAbstractItemView::NoDragDrop), | |
overwrite(false), | |
dropIndicatorPosition(QAbstractItemView::OnItem), | |
defaultDropAction(Qt::IgnoreAction), | |
#endif | |
#ifdef QT_SOFTKEYS_ENABLED | |
doneSoftKey(0), | |
#endif | |
autoScroll(true), | |
autoScrollMargin(16), | |
autoScrollCount(0), | |
shouldScrollToCurrentOnShow(false), | |
shouldClearStatusTip(false), | |
alternatingColors(false), | |
textElideMode(Qt::ElideRight), | |
verticalScrollMode(QAbstractItemView::ScrollPerItem), | |
horizontalScrollMode(QAbstractItemView::ScrollPerItem), | |
currentIndexSet(false), | |
wrapItemText(false), | |
delayedPendingLayout(true), | |
moveCursorUpdatedView(false) | |
{ | |
keyboardInputTime.invalidate(); | |
} | |
QAbstractItemViewPrivate::~QAbstractItemViewPrivate() | |
{ | |
} | |
void QAbstractItemViewPrivate::init() | |
{ | |
Q_Q(QAbstractItemView); | |
q->setItemDelegate(new QStyledItemDelegate(q)); | |
vbar->setRange(0, 0); | |
hbar->setRange(0, 0); | |
QObject::connect(vbar, SIGNAL(actionTriggered(int)), | |
q, SLOT(verticalScrollbarAction(int))); | |
QObject::connect(hbar, SIGNAL(actionTriggered(int)), | |
q, SLOT(horizontalScrollbarAction(int))); | |
QObject::connect(vbar, SIGNAL(valueChanged(int)), | |
q, SLOT(verticalScrollbarValueChanged(int))); | |
QObject::connect(hbar, SIGNAL(valueChanged(int)), | |
q, SLOT(horizontalScrollbarValueChanged(int))); | |
viewport->setBackgroundRole(QPalette::Base); | |
q->setAttribute(Qt::WA_InputMethodEnabled); | |
#ifdef QT_SOFTKEYS_ENABLED | |
doneSoftKey = QSoftKeyManager::createKeyedAction(QSoftKeyManager::DoneSoftKey, Qt::Key_Back, q); | |
#endif | |
} | |
void QAbstractItemViewPrivate::setHoverIndex(const QPersistentModelIndex &index) | |
{ | |
Q_Q(QAbstractItemView); | |
if (hover == index) | |
return; | |
if (selectionBehavior != QAbstractItemView::SelectRows) { | |
q->update(hover); //update the old one | |
q->update(index); //update the new one | |
} else { | |
QRect oldHoverRect = q->visualRect(hover); | |
QRect newHoverRect = q->visualRect(index); | |
viewport->update(QRect(0, newHoverRect.y(), viewport->width(), newHoverRect.height())); | |
viewport->update(QRect(0, oldHoverRect.y(), viewport->width(), oldHoverRect.height())); | |
} | |
hover = index; | |
} | |
void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index) | |
{ | |
//we take a persistent model index because the model might change by emitting signals | |
Q_Q(QAbstractItemView); | |
setHoverIndex(index); | |
if (viewportEnteredNeeded || enteredIndex != index) { | |
viewportEnteredNeeded = false; | |
if (index.isValid()) { | |
emit q->entered(index); | |
#ifndef QT_NO_STATUSTIP | |
QString statustip = model->data(index, Qt::StatusTipRole).toString(); | |
if (parent && (shouldClearStatusTip || !statustip.isEmpty())) { | |
QStatusTipEvent tip(statustip); | |
QApplication::sendEvent(parent, &tip); | |
shouldClearStatusTip = !statustip.isEmpty(); | |
} | |
#endif | |
} else { | |
#ifndef QT_NO_STATUSTIP | |
if (parent && shouldClearStatusTip) { | |
QString emptyString; | |
QStatusTipEvent tip( emptyString ); | |
QApplication::sendEvent(parent, &tip); | |
} | |
#endif | |
emit q->viewportEntered(); | |
} | |
enteredIndex = index; | |
} | |
} | |
/*! | |
\class QAbstractItemView | |
\brief The QAbstractItemView class provides the basic functionality for | |
item view classes. | |
\ingroup model-view | |
QAbstractItemView class is the base class for every standard view | |
that uses a QAbstractItemModel. QAbstractItemView is an abstract | |
class and cannot itself be instantiated. It provides a standard | |
interface for interoperating with models through the signals and | |
slots mechanism, enabling subclasses to be kept up-to-date with | |
changes to their models. This class provides standard support for | |
keyboard and mouse navigation, viewport scrolling, item editing, | |
and selections. The keyboard navigation implements this | |
functionality: | |
\table | |
\header | |
\o Keys | |
\o Functionality | |
\row | |
\o Arrow keys | |
\o Changes the current item and selects it. | |
\row | |
\o Ctrl+Arrow keys | |
\o Changes the current item but does not select it. | |
\row | |
\o Shift+Arrow keys | |
\o Changes the current item and selects it. The previously | |
selected item(s) is not deselected. | |
\row | |
\o Ctr+Space | |
\o Toggles selection of the current item. | |
\row | |
\o Tab/Backtab | |
\o Changes the current item to the next/previous item. | |
\row | |
\o Home/End | |
\o Selects the first/last item in the model. | |
\row | |
\o Page up/Page down | |
\o Scrolls the rows shown up/down by the number of | |
visible rows in the view. | |
\row | |
\o Ctrl+A | |
\o Selects all items in the model. | |
\endtable | |
Note that the above table assumes that the | |
\l{selectionMode}{selection mode} allows the operations. For | |
instance, you cannot select items if the selection mode is | |
QAbstractItemView::NoSelection. | |
The QAbstractItemView class is one of the \l{Model/View Classes} | |
and is part of Qt's \l{Model/View Programming}{model/view framework}. | |
The view classes that inherit QAbstractItemView only need | |
to implement their own view-specific functionality, such as | |
drawing items, returning the geometry of items, finding items, | |
etc. | |
QAbstractItemView provides common slots such as edit() and | |
setCurrentIndex(). Many protected slots are also provided, including | |
dataChanged(), rowsInserted(), rowsAboutToBeRemoved(), selectionChanged(), | |
and currentChanged(). | |
The root item is returned by rootIndex(), and the current item by | |
currentIndex(). To make sure that an item is visible use | |
scrollTo(). | |
Some of QAbstractItemView's functions are concerned with | |
scrolling, for example setHorizontalScrollMode() and | |
setVerticalScrollMode(). To set the range of the scroll bars, you | |
can, for example, reimplement the view's resizeEvent() function: | |
\snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 0 | |
Note that the range is not updated until the widget is shown. | |
Several other functions are concerned with selection control; for | |
example setSelectionMode(), and setSelectionBehavior(). This class | |
provides a default selection model to work with | |
(selectionModel()), but this can be replaced by using | |
setSelectionModel() with an instance of QItemSelectionModel. | |
For complete control over the display and editing of items you can | |
specify a delegate with setItemDelegate(). | |
QAbstractItemView provides a lot of protected functions. Some are | |
concerned with editing, for example, edit(), and commitData(), | |
whilst others are keyboard and mouse event handlers. | |
\note If you inherit QAbstractItemView and intend to update the contents | |
of the viewport, you should use viewport->update() instead of | |
\l{QWidget::update()}{update()} as all painting operations take place on the | |
viewport. | |
\sa {View Classes}, {Model/View Programming}, QAbstractItemModel, {Chart Example} | |
*/ | |
/*! | |
\enum QAbstractItemView::SelectionMode | |
This enum indicates how the view responds to user selections: | |
\value SingleSelection When the user selects an item, any already-selected | |
item becomes unselected, and the user cannot unselect the selected item by | |
clicking on it. | |
\value ContiguousSelection When the user selects an item in the usual way, | |
the selection is cleared and the new item selected. However, if the user | |
presses the Shift key while clicking on an item, all items between the | |
current item and the clicked item are selected or unselected, depending on | |
the state of the clicked item. | |
\value ExtendedSelection When the user selects an item in the usual way, | |
the selection is cleared and the new item selected. However, if the user | |
presses the Ctrl key when clicking on an item, the clicked item gets | |
toggled and all other items are left untouched. If the user presses the | |
Shift key while clicking on an item, all items between the current item | |
and the clicked item are selected or unselected, depending on the state of | |
the clicked item. Multiple items can be selected by dragging the mouse over | |
them. | |
\value MultiSelection When the user selects an item in the usual way, the | |
selection status of that item is toggled and the other items are left | |
alone. Multiple items can be toggled by dragging the mouse over them. | |
\value NoSelection Items cannot be selected. | |
The most commonly used modes are SingleSelection and ExtendedSelection. | |
*/ | |
/*! | |
\enum QAbstractItemView::SelectionBehavior | |
\value SelectItems Selecting single items. | |
\value SelectRows Selecting only rows. | |
\value SelectColumns Selecting only columns. | |
*/ | |
/*! | |
\enum QAbstractItemView::ScrollHint | |
\value EnsureVisible Scroll to ensure that the item is visible. | |
\value PositionAtTop Scroll to position the item at the top of the | |
viewport. | |
\value PositionAtBottom Scroll to position the item at the bottom of the | |
viewport. | |
\value PositionAtCenter Scroll to position the item at the center of the | |
viewport. | |
*/ | |
/*! | |
\enum QAbstractItemView::EditTrigger | |
This enum describes actions which will initiate item editing. | |
\value NoEditTriggers No editing possible. | |
\value CurrentChanged Editing start whenever current item changes. | |
\value DoubleClicked Editing starts when an item is double clicked. | |
\value SelectedClicked Editing starts when clicking on an already selected | |
item. | |
\value EditKeyPressed Editing starts when the platform edit key has been | |
pressed over an item. | |
\value AnyKeyPressed Editing starts when any key is pressed over an item. | |
\value AllEditTriggers Editing starts for all above actions. | |
*/ | |
/*! | |
\enum QAbstractItemView::CursorAction | |
This enum describes the different ways to navigate between items, | |
\sa moveCursor() | |
\value MoveUp Move to the item above the current item. | |
\value MoveDown Move to the item below the current item. | |
\value MoveLeft Move to the item left of the current item. | |
\value MoveRight Move to the item right of the current item. | |
\value MoveHome Move to the top-left corner item. | |
\value MoveEnd Move to the bottom-right corner item. | |
\value MovePageUp Move one page up above the current item. | |
\value MovePageDown Move one page down below the current item. | |
\value MoveNext Move to the item after the current item. | |
\value MovePrevious Move to the item before the current item. | |
*/ | |
/*! | |
\enum QAbstractItemView::State | |
Describes the different states the view can be in. This is usually | |
only interesting when reimplementing your own view. | |
\value NoState The is the default state. | |
\value DraggingState The user is dragging items. | |
\value DragSelectingState The user is selecting items. | |
\value EditingState The user is editing an item in a widget editor. | |
\value ExpandingState The user is opening a branch of items. | |
\value CollapsingState The user is closing a branch of items. | |
\value AnimatingState The item view is performing an animation. | |
*/ | |
/*! | |
\since 4.2 | |
\enum QAbstractItemView::ScrollMode | |
\value ScrollPerItem The view will scroll the contents one item at a time. | |
\value ScrollPerPixel The view will scroll the contents one pixel at a time. | |
*/ | |
/*! | |
\fn QRect QAbstractItemView::visualRect(const QModelIndex &index) const = 0 | |
Returns the rectangle on the viewport occupied by the item at \a index. | |
If your item is displayed in several areas then visualRect should return | |
the primary area that contains index and not the complete area that index | |
might encompasses, touch or cause drawing. | |
In the base class this is a pure virtual function. | |
\sa indexAt(), visualRegionForSelection() | |
*/ | |
/*! | |
\fn void QAbstractItemView::scrollTo(const QModelIndex &index, ScrollHint hint) = 0 | |
Scrolls the view if necessary to ensure that the item at \a index | |
is visible. The view will try to position the item according to the given \a hint. | |
In the base class this is a pure virtual function. | |
*/ | |
/*! | |
\fn QModelIndex QAbstractItemView::indexAt(const QPoint &point) const = 0 | |
Returns the model index of the item at the viewport coordinates \a point. | |
In the base class this is a pure virtual function. | |
\sa visualRect() | |
*/ | |
/*! | |
\fn void QAbstractItemView::activated(const QModelIndex &index) | |
This signal is emitted when the item specified by \a index is | |
activated by the user. How to activate items depends on the | |
platform; e.g., by single- or double-clicking the item, or by | |
pressing the Return or Enter key when the item is current. | |
\sa clicked(), doubleClicked(), entered(), pressed() | |
*/ | |
/*! | |
\fn void QAbstractItemView::entered(const QModelIndex &index) | |
This signal is emitted when the mouse cursor enters the item | |
specified by \a index. | |
Mouse tracking needs to be enabled for this feature to work. | |
\sa viewportEntered(), activated(), clicked(), doubleClicked(), pressed() | |
*/ | |
/*! | |
\fn void QAbstractItemView::viewportEntered() | |
This signal is emitted when the mouse cursor enters the viewport. | |
Mouse tracking needs to be enabled for this feature to work. | |
\sa entered() | |
*/ | |
/*! | |
\fn void QAbstractItemView::pressed(const QModelIndex &index) | |
This signal is emitted when a mouse button is pressed. The item | |
the mouse was pressed on is specified by \a index. The signal is | |
only emitted when the index is valid. | |
Use the QApplication::mouseButtons() function to get the state | |
of the mouse buttons. | |
\sa activated(), clicked(), doubleClicked(), entered() | |
*/ | |
/*! | |
\fn void QAbstractItemView::clicked(const QModelIndex &index) | |
This signal is emitted when a mouse button is clicked. The item | |
the mouse was clicked on is specified by \a index. The signal is | |
only emitted when the index is valid. | |
\sa activated(), doubleClicked(), entered(), pressed() | |
*/ | |
/*! | |
\fn void QAbstractItemView::doubleClicked(const QModelIndex &index) | |
This signal is emitted when a mouse button is double-clicked. The | |
item the mouse was double-clicked on is specified by \a index. | |
The signal is only emitted when the index is valid. | |
\sa clicked(), activated() | |
*/ | |
/*! | |
\fn QModelIndex QAbstractItemView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) = 0 | |
Returns a QModelIndex object pointing to the next object in the view, | |
based on the given \a cursorAction and keyboard modifiers specified | |
by \a modifiers. | |
In the base class this is a pure virtual function. | |
*/ | |
/*! | |
\fn int QAbstractItemView::horizontalOffset() const = 0 | |
Returns the horizontal offset of the view. | |
In the base class this is a pure virtual function. | |
\sa verticalOffset() | |
*/ | |
/*! | |
\fn int QAbstractItemView::verticalOffset() const = 0 | |
Returns the vertical offset of the view. | |
In the base class this is a pure virtual function. | |
\sa horizontalOffset() | |
*/ | |
/*! | |
\fn bool QAbstractItemView::isIndexHidden(const QModelIndex &index) const | |
Returns true if the item referred to by the given \a index is hidden in the view, | |
otherwise returns false. | |
Hiding is a view specific feature. For example in TableView a column can be marked | |
as hidden or a row in the TreeView. | |
In the base class this is a pure virtual function. | |
*/ | |
/*! | |
\fn void QAbstractItemView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) | |
Applies the selection \a flags to the items in or touched by the | |
rectangle, \a rect. | |
When implementing your own itemview setSelection should call | |
selectionModel()->select(selection, flags) where selection | |
is either an empty QModelIndex or a QItemSelection that contains | |
all items that are contained in \a rect. | |
\sa selectionCommand(), selectedIndexes() | |
*/ | |
/*! | |
\fn QRegion QAbstractItemView::visualRegionForSelection(const QItemSelection &selection) const = 0 | |
Returns the region from the viewport of the items in the given | |
\a selection. | |
In the base class this is a pure virtual function. | |
\sa visualRect(), selectedIndexes() | |
*/ | |
/*! | |
\fn void QAbstractItemView::update() | |
\internal | |
*/ | |
/*! | |
Constructs an abstract item view with the given \a parent. | |
*/ | |
QAbstractItemView::QAbstractItemView(QWidget *parent) | |
: QAbstractScrollArea(*(new QAbstractItemViewPrivate), parent) | |
{ | |
d_func()->init(); | |
} | |
/*! | |
\internal | |
*/ | |
QAbstractItemView::QAbstractItemView(QAbstractItemViewPrivate &dd, QWidget *parent) | |
: QAbstractScrollArea(dd, parent) | |
{ | |
d_func()->init(); | |
} | |
/*! | |
Destroys the view. | |
*/ | |
QAbstractItemView::~QAbstractItemView() | |
{ | |
Q_D(QAbstractItemView); | |
// stop these timers here before ~QObject | |
d->delayedReset.stop(); | |
d->updateTimer.stop(); | |
d->delayedEditing.stop(); | |
d->delayedAutoScroll.stop(); | |
d->autoScrollTimer.stop(); | |
d->delayedLayout.stop(); | |
d->fetchMoreTimer.stop(); | |
} | |
/*! | |
Sets the \a model for the view to present. | |
This function will create and set a new selection model, replacing any | |
model that was previously set with setSelectionModel(). However, the old | |
selection model will not be deleted as it may be shared between several | |
views. We recommend that you delete the old selection model if it is no | |
longer required. This is done with the following code: | |
\snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 2 | |
If both the old model and the old selection model do not have parents, or | |
if their parents are long-lived objects, it may be preferable to call their | |
deleteLater() functions to explicitly delete them. | |
The view \e{does not} take ownership of the model unless it is the model's | |
parent object because the model may be shared between many different views. | |
\sa selectionModel(), setSelectionModel() | |
*/ | |
void QAbstractItemView::setModel(QAbstractItemModel *model) | |
{ | |
Q_D(QAbstractItemView); | |
if (model == d->model) | |
return; | |
if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) { | |
disconnect(d->model, SIGNAL(destroyed()), | |
this, SLOT(_q_modelDestroyed())); | |
disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), | |
this, SLOT(dataChanged(QModelIndex,QModelIndex))); | |
disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), | |
this, SLOT(_q_headerDataChanged())); | |
disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), | |
this, SLOT(rowsInserted(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), | |
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_columnsRemoved(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), | |
this, SLOT(_q_columnsInserted(QModelIndex,int,int))); | |
disconnect(d->model, SIGNAL(modelReset()), this, SLOT(reset())); | |
disconnect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); | |
} | |
d->model = (model ? model : QAbstractItemModelPrivate::staticEmptyModel()); | |
// These asserts do basic sanity checking of the model | |
Q_ASSERT_X(d->model->index(0,0) == d->model->index(0,0), | |
"QAbstractItemView::setModel", | |
"A model should return the exact same index " | |
"(including its internal id/pointer) when asked for it twice in a row."); | |
Q_ASSERT_X(d->model->index(0,0).parent() == QModelIndex(), | |
"QAbstractItemView::setModel", | |
"The parent of a top level index should be invalid"); | |
if (d->model != QAbstractItemModelPrivate::staticEmptyModel()) { | |
connect(d->model, SIGNAL(destroyed()), | |
this, SLOT(_q_modelDestroyed())); | |
connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), | |
this, SLOT(dataChanged(QModelIndex,QModelIndex))); | |
connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), | |
this, SLOT(_q_headerDataChanged())); | |
connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), | |
this, SLOT(rowsInserted(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), | |
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), | |
this, SLOT(_q_columnsRemoved(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), | |
this, SLOT(_q_columnsInserted(QModelIndex,int,int))); | |
connect(d->model, SIGNAL(modelReset()), this, SLOT(reset())); | |
connect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); | |
} | |
QItemSelectionModel *selection_model = new QItemSelectionModel(d->model, this); | |
connect(d->model, SIGNAL(destroyed()), selection_model, SLOT(deleteLater())); | |
setSelectionModel(selection_model); | |
reset(); // kill editors, set new root and do layout | |
} | |
/*! | |
Returns the model that this view is presenting. | |
*/ | |
QAbstractItemModel *QAbstractItemView::model() const | |
{ | |
Q_D(const QAbstractItemView); | |
return (d->model == QAbstractItemModelPrivate::staticEmptyModel() ? 0 : d->model); | |
} | |
/*! | |
Sets the current selection model to the given \a selectionModel. | |
Note that, if you call setModel() after this function, the given \a selectionModel | |
will be replaced by one created by the view. | |
\note It is up to the application to delete the old selection model if it is no | |
longer needed; i.e., if it is not being used by other views. This will happen | |
automatically when its parent object is deleted. However, if it does not have a | |
parent, or if the parent is a long-lived object, it may be preferable to call its | |
deleteLater() function to explicitly delete it. | |
\sa selectionModel(), setModel(), clearSelection() | |
*/ | |
void QAbstractItemView::setSelectionModel(QItemSelectionModel *selectionModel) | |
{ | |
// ### if the given model is null, we should use the original selection model | |
Q_ASSERT(selectionModel); | |
Q_D(QAbstractItemView); | |
if (selectionModel->model() != d->model) { | |
qWarning("QAbstractItemView::setSelectionModel() failed: " | |
"Trying to set a selection model, which works on " | |
"a different model than the view."); | |
return; | |
} | |
if (d->selectionModel) { | |
disconnect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), | |
this, SLOT(selectionChanged(QItemSelection,QItemSelection))); | |
disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), | |
this, SLOT(currentChanged(QModelIndex,QModelIndex))); | |
} | |
d->selectionModel = selectionModel; | |
if (d->selectionModel) { | |
connect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), | |
this, SLOT(selectionChanged(QItemSelection,QItemSelection))); | |
connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), | |
this, SLOT(currentChanged(QModelIndex,QModelIndex))); | |
} | |
} | |
/*! | |
Returns the current selection model. | |
\sa setSelectionModel(), selectedIndexes() | |
*/ | |
QItemSelectionModel* QAbstractItemView::selectionModel() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->selectionModel; | |
} | |
/*! | |
Sets the item delegate for this view and its model to \a delegate. | |
This is useful if you want complete control over the editing and | |
display of items. | |
Any existing delegate will be removed, but not deleted. QAbstractItemView | |
does not take ownership of \a delegate. | |
\warning You should not share the same instance of a delegate between views. | |
Doing so can cause incorrect or unintuitive editing behavior since each | |
view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} | |
signal, and attempt to access, modify or close an editor that has already been closed. | |
\sa itemDelegate() | |
*/ | |
void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate) | |
{ | |
Q_D(QAbstractItemView); | |
if (delegate == d->itemDelegate) | |
return; | |
if (d->itemDelegate) { | |
if (d->delegateRefCount(d->itemDelegate) == 1) { | |
disconnect(d->itemDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
disconnect(d->itemDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
disconnect(d->itemDelegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout())); | |
} | |
} | |
if (delegate) { | |
if (d->delegateRefCount(delegate) == 0) { | |
connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
qRegisterMetaType<QModelIndex>("QModelIndex"); | |
connect(delegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout()), Qt::QueuedConnection); | |
} | |
} | |
d->itemDelegate = delegate; | |
viewport()->update(); | |
} | |
/*! | |
Returns the item delegate used by this view and model. This is | |
either one set with setItemDelegate(), or the default one. | |
\sa setItemDelegate() | |
*/ | |
QAbstractItemDelegate *QAbstractItemView::itemDelegate() const | |
{ | |
return d_func()->itemDelegate; | |
} | |
/*! | |
\reimp | |
*/ | |
QVariant QAbstractItemView::inputMethodQuery(Qt::InputMethodQuery query) const | |
{ | |
const QModelIndex current = currentIndex(); | |
if (!current.isValid() || query != Qt::ImMicroFocus) | |
return QAbstractScrollArea::inputMethodQuery(query); | |
return visualRect(current); | |
} | |
/*! | |
\since 4.2 | |
Sets the given item \a delegate used by this view and model for the given | |
\a row. All items on \a row will be drawn and managed by \a delegate | |
instead of using the default delegate (i.e., itemDelegate()). | |
Any existing row delegate for \a row will be removed, but not | |
deleted. QAbstractItemView does not take ownership of \a delegate. | |
\note If a delegate has been assigned to both a row and a column, the row | |
delegate (i.e., this delegate) will take precedence and manage the | |
intersecting cell index. | |
\warning You should not share the same instance of a delegate between views. | |
Doing so can cause incorrect or unintuitive editing behavior since each | |
view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} | |
signal, and attempt to access, modify or close an editor that has already been closed. | |
\sa itemDelegateForRow(), setItemDelegateForColumn(), itemDelegate() | |
*/ | |
void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate) | |
{ | |
Q_D(QAbstractItemView); | |
if (QAbstractItemDelegate *rowDelegate = d->rowDelegates.value(row, 0)) { | |
if (d->delegateRefCount(rowDelegate) == 1) { | |
disconnect(rowDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
disconnect(rowDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
} | |
d->rowDelegates.remove(row); | |
} | |
if (delegate) { | |
if (d->delegateRefCount(delegate) == 0) { | |
connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
} | |
d->rowDelegates.insert(row, delegate); | |
} | |
viewport()->update(); | |
} | |
/*! | |
\since 4.2 | |
Returns the item delegate used by this view and model for the given \a row, | |
or 0 if no delegate has been assigned. You can call itemDelegate() to get a | |
pointer to the current delegate for a given index. | |
\sa setItemDelegateForRow(), itemDelegateForColumn(), setItemDelegate() | |
*/ | |
QAbstractItemDelegate *QAbstractItemView::itemDelegateForRow(int row) const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->rowDelegates.value(row, 0); | |
} | |
/*! | |
\since 4.2 | |
Sets the given item \a delegate used by this view and model for the given | |
\a column. All items on \a column will be drawn and managed by \a delegate | |
instead of using the default delegate (i.e., itemDelegate()). | |
Any existing column delegate for \a column will be removed, but not | |
deleted. QAbstractItemView does not take ownership of \a delegate. | |
\note If a delegate has been assigned to both a row and a column, the row | |
delegate will take precedence and manage the intersecting cell index. | |
\warning You should not share the same instance of a delegate between views. | |
Doing so can cause incorrect or unintuitive editing behavior since each | |
view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} | |
signal, and attempt to access, modify or close an editor that has already been closed. | |
\sa itemDelegateForColumn(), setItemDelegateForRow(), itemDelegate() | |
*/ | |
void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate) | |
{ | |
Q_D(QAbstractItemView); | |
if (QAbstractItemDelegate *columnDelegate = d->columnDelegates.value(column, 0)) { | |
if (d->delegateRefCount(columnDelegate) == 1) { | |
disconnect(columnDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
disconnect(columnDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
} | |
d->columnDelegates.remove(column); | |
} | |
if (delegate) { | |
if (d->delegateRefCount(delegate) == 0) { | |
connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), | |
this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); | |
connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); | |
} | |
d->columnDelegates.insert(column, delegate); | |
} | |
viewport()->update(); | |
} | |
/*! | |
\since 4.2 | |
Returns the item delegate used by this view and model for the given \a | |
column. You can call itemDelegate() to get a pointer to the current delegate | |
for a given index. | |
\sa setItemDelegateForColumn(), itemDelegateForRow(), itemDelegate() | |
*/ | |
QAbstractItemDelegate *QAbstractItemView::itemDelegateForColumn(int column) const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->columnDelegates.value(column, 0); | |
} | |
/*! | |
Returns the item delegate used by this view and model for | |
the given \a index. | |
*/ | |
QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->delegateForIndex(index); | |
} | |
/*! | |
\property QAbstractItemView::selectionMode | |
\brief which selection mode the view operates in | |
This property controls whether the user can select one or many items | |
and, in many-item selections, whether the selection must be a | |
continuous range of items. | |
\sa SelectionMode SelectionBehavior | |
*/ | |
void QAbstractItemView::setSelectionMode(SelectionMode mode) | |
{ | |
Q_D(QAbstractItemView); | |
d->selectionMode = mode; | |
} | |
QAbstractItemView::SelectionMode QAbstractItemView::selectionMode() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->selectionMode; | |
} | |
/*! | |
\property QAbstractItemView::selectionBehavior | |
\brief which selection behavior the view uses | |
This property holds whether selections are done | |
in terms of single items, rows or columns. | |
\sa SelectionMode SelectionBehavior | |
*/ | |
void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior) | |
{ | |
Q_D(QAbstractItemView); | |
d->selectionBehavior = behavior; | |
} | |
QAbstractItemView::SelectionBehavior QAbstractItemView::selectionBehavior() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->selectionBehavior; | |
} | |
/*! | |
Sets the current item to be the item at \a index. | |
Unless the current selection mode is | |
\l{QAbstractItemView::}{NoSelection}, the item is also be selected. | |
Note that this function also updates the starting position for any | |
new selections the user performs. | |
To set an item as the current item without selecting it, call | |
\c{selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);} | |
\sa currentIndex(), currentChanged(), selectionMode | |
*/ | |
void QAbstractItemView::setCurrentIndex(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
if (d->selectionModel && (!index.isValid() || d->isIndexEnabled(index))) { | |
QItemSelectionModel::SelectionFlags command = selectionCommand(index, 0); | |
d->selectionModel->setCurrentIndex(index, command); | |
d->currentIndexSet = true; | |
if ((command & QItemSelectionModel::Current) == 0) | |
d->pressedPosition = visualRect(currentIndex()).center() + d->offset(); | |
} | |
} | |
/*! | |
Returns the model index of the current item. | |
\sa setCurrentIndex() | |
*/ | |
QModelIndex QAbstractItemView::currentIndex() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->selectionModel ? d->selectionModel->currentIndex() : QModelIndex(); | |
} | |
/*! | |
Reset the internal state of the view. | |
\warning This function will reset open editors, scroll bar positions, | |
selections, etc. Existing changes will not be committed. If you would like | |
to save your changes when resetting the view, you can reimplement this | |
function, commit your changes, and then call the superclass' | |
implementation. | |
*/ | |
void QAbstractItemView::reset() | |
{ | |
Q_D(QAbstractItemView); | |
d->delayedReset.stop(); //make sure we stop the timer | |
QList<QEditorInfo>::const_iterator it = d->editors.constBegin(); | |
for (; it != d->editors.constEnd(); ++it) | |
d->releaseEditor(it->editor); | |
d->editors.clear(); | |
d->persistent.clear(); | |
d->currentIndexSet = false; | |
setState(NoState); | |
setRootIndex(QModelIndex()); | |
if (d->selectionModel) | |
d->selectionModel->reset(); | |
} | |
/*! | |
Sets the root item to the item at the given \a index. | |
\sa rootIndex() | |
*/ | |
void QAbstractItemView::setRootIndex(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
if (index.isValid() && index.model() != d->model) { | |
qWarning("QAbstractItemView::setRootIndex failed : index must be from the currently set model"); | |
return; | |
} | |
d->root = index; | |
d->doDelayedItemsLayout(); | |
} | |
/*! | |
Returns the model index of the model's root item. The root item is | |
the parent item to the view's toplevel items. The root can be invalid. | |
\sa setRootIndex() | |
*/ | |
QModelIndex QAbstractItemView::rootIndex() const | |
{ | |
return QModelIndex(d_func()->root); | |
} | |
/*! | |
Selects all items in the view. | |
This function will use the selection behavior | |
set on the view when selecting. | |
\sa setSelection(), selectedIndexes(), clearSelection() | |
*/ | |
void QAbstractItemView::selectAll() | |
{ | |
Q_D(QAbstractItemView); | |
SelectionMode mode = d->selectionMode; | |
if (mode == MultiSelection || mode == ExtendedSelection) | |
d->selectAll(QItemSelectionModel::ClearAndSelect | |
|d->selectionBehaviorFlags()); | |
else if (mode != SingleSelection) | |
d->selectAll(selectionCommand(d->model->index(0, 0, d->root))); | |
} | |
/*! | |
Starts editing the item corresponding to the given \a index if it is | |
editable. | |
Note that this function does not change the current index. Since the current | |
index defines the next and previous items to edit, users may find that | |
keyboard navigation does not work as expected. To provide consistent navigation | |
behavior, call setCurrentIndex() before this function with the same model | |
index. | |
\sa QModelIndex::flags() | |
*/ | |
void QAbstractItemView::edit(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
if (!d->isIndexValid(index)) | |
qWarning("edit: index was invalid"); | |
if (!edit(index, AllEditTriggers, 0)) | |
qWarning("edit: editing failed"); | |
} | |
/*! | |
Deselects all selected items. The current index will not be changed. | |
\sa setSelection(), selectAll() | |
*/ | |
void QAbstractItemView::clearSelection() | |
{ | |
Q_D(QAbstractItemView); | |
if (d->selectionModel) | |
d->selectionModel->clearSelection(); | |
} | |
/*! | |
\internal | |
This function is intended to lay out the items in the view. | |
The default implementation just calls updateGeometries() and updates the viewport. | |
*/ | |
void QAbstractItemView::doItemsLayout() | |
{ | |
Q_D(QAbstractItemView); | |
d->interruptDelayedItemsLayout(); | |
updateGeometries(); | |
d->viewport->update(); | |
} | |
/*! | |
\property QAbstractItemView::editTriggers | |
\brief which actions will initiate item editing | |
This property is a selection of flags defined by | |
\l{EditTrigger}, combined using the OR | |
operator. The view will only initiate the editing of an item if the | |
action performed is set in this property. | |
*/ | |
void QAbstractItemView::setEditTriggers(EditTriggers actions) | |
{ | |
Q_D(QAbstractItemView); | |
d->editTriggers = actions; | |
} | |
QAbstractItemView::EditTriggers QAbstractItemView::editTriggers() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->editTriggers; | |
} | |
/*! | |
\since 4.2 | |
\property QAbstractItemView::verticalScrollMode | |
\brief how the view scrolls its contents in the vertical direction | |
This property controls how the view scroll its contents vertically. | |
Scrolling can be done either per pixel or per item. | |
*/ | |
void QAbstractItemView::setVerticalScrollMode(ScrollMode mode) | |
{ | |
Q_D(QAbstractItemView); | |
if (mode == d->verticalScrollMode) | |
return; | |
QModelIndex topLeft = indexAt(QPoint(0, 0)); | |
d->verticalScrollMode = mode; | |
updateGeometries(); // update the scroll bars | |
scrollTo(topLeft, QAbstractItemView::PositionAtTop); | |
} | |
QAbstractItemView::ScrollMode QAbstractItemView::verticalScrollMode() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->verticalScrollMode; | |
} | |
/*! | |
\since 4.2 | |
\property QAbstractItemView::horizontalScrollMode | |
\brief how the view scrolls its contents in the horizontal direction | |
This property controls how the view scroll its contents horizontally. | |
Scrolling can be done either per pixel or per item. | |
*/ | |
void QAbstractItemView::setHorizontalScrollMode(ScrollMode mode) | |
{ | |
Q_D(QAbstractItemView); | |
d->horizontalScrollMode = mode; | |
updateGeometries(); // update the scroll bars | |
} | |
QAbstractItemView::ScrollMode QAbstractItemView::horizontalScrollMode() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->horizontalScrollMode; | |
} | |
#ifndef QT_NO_DRAGANDDROP | |
/*! | |
\since 4.2 | |
\property QAbstractItemView::dragDropOverwriteMode | |
\brief the view's drag and drop behavior | |
If its value is \c true, the selected data will overwrite the | |
existing item data when dropped, while moving the data will clear | |
the item. If its value is \c false, the selected data will be | |
inserted as a new item when the data is dropped. When the data is | |
moved, the item is removed as well. | |
The default value is \c false, as in the QListView and QTreeView | |
subclasses. In the QTableView subclass, on the other hand, the | |
property has been set to \c true. | |
Note: This is not intended to prevent overwriting of items. | |
The model's implementation of flags() should do that by not | |
returning Qt::ItemIsDropEnabled. | |
\sa dragDropMode | |
*/ | |
void QAbstractItemView::setDragDropOverwriteMode(bool overwrite) | |
{ | |
Q_D(QAbstractItemView); | |
d->overwrite = overwrite; | |
} | |
bool QAbstractItemView::dragDropOverwriteMode() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->overwrite; | |
} | |
#endif | |
/*! | |
\property QAbstractItemView::autoScroll | |
\brief whether autoscrolling in drag move events is enabled | |
If this property is set to true (the default), the | |
QAbstractItemView automatically scrolls the contents of the view | |
if the user drags within 16 pixels of the viewport edge. If the current | |
item changes, then the view will scroll automatically to ensure that the | |
current item is fully visible. | |
This property only works if the viewport accepts drops. Autoscroll is | |
switched off by setting this property to false. | |
*/ | |
void QAbstractItemView::setAutoScroll(bool enable) | |
{ | |
Q_D(QAbstractItemView); | |
d->autoScroll = enable; | |
} | |
bool QAbstractItemView::hasAutoScroll() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->autoScroll; | |
} | |
/*! | |
\since 4.4 | |
\property QAbstractItemView::autoScrollMargin | |
\brief the size of the area when auto scrolling is triggered | |
This property controls the size of the area at the edge of the viewport that | |
triggers autoscrolling. The default value is 16 pixels. | |
*/ | |
void QAbstractItemView::setAutoScrollMargin(int margin) | |
{ | |
Q_D(QAbstractItemView); | |
d->autoScrollMargin = margin; | |
} | |
int QAbstractItemView::autoScrollMargin() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->autoScrollMargin; | |
} | |
/*! | |
\property QAbstractItemView::tabKeyNavigation | |
\brief whether item navigation with tab and backtab is enabled. | |
*/ | |
void QAbstractItemView::setTabKeyNavigation(bool enable) | |
{ | |
Q_D(QAbstractItemView); | |
d->tabKeyNavigation = enable; | |
} | |
bool QAbstractItemView::tabKeyNavigation() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->tabKeyNavigation; | |
} | |
#ifndef QT_NO_DRAGANDDROP | |
/*! | |
\property QAbstractItemView::showDropIndicator | |
\brief whether the drop indicator is shown when dragging items and dropping. | |
\sa dragEnabled DragDropMode dragDropOverwriteMode acceptDrops | |
*/ | |
void QAbstractItemView::setDropIndicatorShown(bool enable) | |
{ | |
Q_D(QAbstractItemView); | |
d->showDropIndicator = enable; | |
} | |
bool QAbstractItemView::showDropIndicator() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->showDropIndicator; | |
} | |
/*! | |
\property QAbstractItemView::dragEnabled | |
\brief whether the view supports dragging of its own items | |
\sa showDropIndicator DragDropMode dragDropOverwriteMode acceptDrops | |
*/ | |
void QAbstractItemView::setDragEnabled(bool enable) | |
{ | |
Q_D(QAbstractItemView); | |
d->dragEnabled = enable; | |
} | |
bool QAbstractItemView::dragEnabled() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->dragEnabled; | |
} | |
/*! | |
\since 4.2 | |
\enum QAbstractItemView::DragDropMode | |
Describes the various drag and drop events the view can act upon. | |
By default the view does not support dragging or dropping (\c | |
NoDragDrop). | |
\value NoDragDrop Does not support dragging or dropping. | |
\value DragOnly The view supports dragging of its own items | |
\value DropOnly The view accepts drops | |
\value DragDrop The view supports both dragging and dropping | |
\value InternalMove The view accepts move (\bold{not copy}) operations only | |
from itself. | |
Note that the model used needs to provide support for drag and drop operations. | |
\sa setDragDropMode() {Using drag and drop with item views} | |
*/ | |
/*! | |
\property QAbstractItemView::dragDropMode | |
\brief the drag and drop event the view will act upon | |
\since 4.2 | |
\sa showDropIndicator dragDropOverwriteMode | |
*/ | |
void QAbstractItemView::setDragDropMode(DragDropMode behavior) | |
{ | |
Q_D(QAbstractItemView); | |
d->dragDropMode = behavior; | |
setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove); | |
setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove); | |
} | |
QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const | |
{ | |
Q_D(const QAbstractItemView); | |
DragDropMode setBehavior = d->dragDropMode; | |
if (!dragEnabled() && !acceptDrops()) | |
return NoDragDrop; | |
if (dragEnabled() && !acceptDrops()) | |
return DragOnly; | |
if (!dragEnabled() && acceptDrops()) | |
return DropOnly; | |
if (dragEnabled() && acceptDrops()) { | |
if (setBehavior == InternalMove) | |
return setBehavior; | |
else | |
return DragDrop; | |
} | |
return NoDragDrop; | |
} | |
/*! | |
\property QAbstractItemView::defaultDropAction | |
\brief the drop action that will be used by default in QAbstractItemView::drag() | |
If the property is not set, the drop action is CopyAction when the supported | |
actions support CopyAction. | |
\since 4.6 | |
\sa showDropIndicator dragDropOverwriteMode | |
*/ | |
void QAbstractItemView::setDefaultDropAction(Qt::DropAction dropAction) | |
{ | |
Q_D(QAbstractItemView); | |
d->defaultDropAction = dropAction; | |
} | |
Qt::DropAction QAbstractItemView::defaultDropAction() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->defaultDropAction; | |
} | |
#endif // QT_NO_DRAGANDDROP | |
/*! | |
\property QAbstractItemView::alternatingRowColors | |
\brief whether to draw the background using alternating colors | |
If this property is true, the item background will be drawn using | |
QPalette::Base and QPalette::AlternateBase; otherwise the background | |
will be drawn using the QPalette::Base color. | |
By default, this property is false. | |
*/ | |
void QAbstractItemView::setAlternatingRowColors(bool enable) | |
{ | |
Q_D(QAbstractItemView); | |
d->alternatingColors = enable; | |
if (isVisible()) | |
d->viewport->update(); | |
} | |
bool QAbstractItemView::alternatingRowColors() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->alternatingColors; | |
} | |
/*! | |
\property QAbstractItemView::iconSize | |
\brief the size of items' icons | |
Setting this property when the view is visible will cause the | |
items to be laid out again. | |
*/ | |
void QAbstractItemView::setIconSize(const QSize &size) | |
{ | |
Q_D(QAbstractItemView); | |
if (size == d->iconSize) | |
return; | |
d->iconSize = size; | |
d->doDelayedItemsLayout(); | |
} | |
QSize QAbstractItemView::iconSize() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->iconSize; | |
} | |
/*! | |
\property QAbstractItemView::textElideMode | |
\brief the position of the "..." in elided text. | |
The default value for all item views is Qt::ElideRight. | |
*/ | |
void QAbstractItemView::setTextElideMode(Qt::TextElideMode mode) | |
{ | |
Q_D(QAbstractItemView); | |
d->textElideMode = mode; | |
} | |
Qt::TextElideMode QAbstractItemView::textElideMode() const | |
{ | |
return d_func()->textElideMode; | |
} | |
/*! | |
\reimp | |
*/ | |
bool QAbstractItemView::focusNextPrevChild(bool next) | |
{ | |
Q_D(QAbstractItemView); | |
if (d->tabKeyNavigation && isEnabled() && d->viewport->isEnabled()) { | |
QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier); | |
keyPressEvent(&event); | |
if (event.isAccepted()) | |
return true; | |
} | |
return QAbstractScrollArea::focusNextPrevChild(next); | |
} | |
/*! | |
\reimp | |
*/ | |
bool QAbstractItemView::event(QEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
switch (event->type()) { | |
case QEvent::Paint: | |
//we call this here because the scrollbars' visibility might be altered | |
//so this can't be done in the paintEvent method | |
d->executePostedLayout(); //make sure we set the layout properly | |
break; | |
case QEvent::Show: | |
d->executePostedLayout(); //make sure we set the layout properly | |
if (d->shouldScrollToCurrentOnShow) { | |
d->shouldScrollToCurrentOnShow = false; | |
const QModelIndex current = currentIndex(); | |
if (current.isValid() && (d->state == QAbstractItemView::EditingState || d->autoScroll)) | |
scrollTo(current); | |
} | |
break; | |
case QEvent::LocaleChange: | |
viewport()->update(); | |
break; | |
case QEvent::LayoutDirectionChange: | |
case QEvent::ApplicationLayoutDirectionChange: | |
updateGeometries(); | |
break; | |
case QEvent::StyleChange: | |
doItemsLayout(); | |
break; | |
case QEvent::FocusOut: | |
d->checkPersistentEditorFocus(); | |
break; | |
case QEvent::FontChange: | |
d->doDelayedItemsLayout(); // the size of the items will change | |
break; | |
#ifdef QT_SOFTKEYS_ENABLED | |
case QEvent::LanguageChange: | |
d->doneSoftKey->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::DoneSoftKey)); | |
break; | |
#endif | |
default: | |
break; | |
} | |
return QAbstractScrollArea::event(event); | |
} | |
/*! | |
\fn bool QAbstractItemView::viewportEvent(QEvent *event) | |
This function is used to handle tool tips, and What's | |
This? mode, if the given \a event is a QEvent::ToolTip,or a | |
QEvent::WhatsThis. It passes all other | |
events on to its base class viewportEvent() handler. | |
*/ | |
bool QAbstractItemView::viewportEvent(QEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
switch (event->type()) { | |
case QEvent::HoverMove: | |
case QEvent::HoverEnter: | |
d->setHoverIndex(indexAt(static_cast<QHoverEvent*>(event)->pos())); | |
break; | |
case QEvent::HoverLeave: | |
d->setHoverIndex(QModelIndex()); | |
break; | |
case QEvent::Enter: | |
d->viewportEnteredNeeded = true; | |
break; | |
case QEvent::Leave: | |
#ifndef QT_NO_STATUSTIP | |
if (d->shouldClearStatusTip && d->parent) { | |
QString empty; | |
QStatusTipEvent tip(empty); | |
QApplication::sendEvent(d->parent, &tip); | |
d->shouldClearStatusTip = false; | |
} | |
#endif | |
d->enteredIndex = QModelIndex(); | |
break; | |
case QEvent::ToolTip: | |
case QEvent::QueryWhatsThis: | |
case QEvent::WhatsThis: { | |
QHelpEvent *he = static_cast<QHelpEvent*>(event); | |
const QModelIndex index = indexAt(he->pos()); | |
QStyleOptionViewItemV4 option = d->viewOptionsV4(); | |
option.rect = visualRect(index); | |
option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); | |
bool retval = false; | |
// ### Qt 5: make this a normal function call to a virtual function | |
QMetaObject::invokeMethod(d->delegateForIndex(index), "helpEvent", | |
Q_RETURN_ARG(bool, retval), | |
Q_ARG(QHelpEvent *, he), | |
Q_ARG(QAbstractItemView *, this), | |
Q_ARG(QStyleOptionViewItem, option), | |
Q_ARG(QModelIndex, index)); | |
return retval; | |
} | |
case QEvent::FontChange: | |
d->doDelayedItemsLayout(); // the size of the items will change | |
break; | |
case QEvent::WindowActivate: | |
case QEvent::WindowDeactivate: | |
d->viewport->update(); | |
break; | |
default: | |
break; | |
} | |
return QAbstractScrollArea::viewportEvent(event); | |
} | |
/*! | |
This function is called with the given \a event when a mouse button is pressed | |
while the cursor is inside the widget. If a valid item is pressed on it is made | |
into the current item. This function emits the pressed() signal. | |
*/ | |
void QAbstractItemView::mousePressEvent(QMouseEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling | |
QPoint pos = event->pos(); | |
QPersistentModelIndex index = indexAt(pos); | |
if (!d->selectionModel | |
|| (d->state == EditingState && d->hasEditor(index))) | |
return; | |
d->pressedAlreadySelected = d->selectionModel->isSelected(index); | |
d->pressedIndex = index; | |
d->pressedModifiers = event->modifiers(); | |
QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); | |
d->noSelectionOnMousePress = command == QItemSelectionModel::NoUpdate || !index.isValid(); | |
QPoint offset = d->offset(); | |
if ((command & QItemSelectionModel::Current) == 0) | |
d->pressedPosition = pos + offset; | |
else if (!indexAt(d->pressedPosition - offset).isValid()) | |
d->pressedPosition = visualRect(currentIndex()).center() + offset; | |
if (edit(index, NoEditTriggers, event)) | |
return; | |
if (index.isValid() && d->isIndexEnabled(index)) { | |
// we disable scrollTo for mouse press so the item doesn't change position | |
// when the user is interacting with it (ie. clicking on it) | |
bool autoScroll = d->autoScroll; | |
d->autoScroll = false; | |
d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | |
d->autoScroll = autoScroll; | |
QRect rect(d->pressedPosition - offset, pos); | |
if (command.testFlag(QItemSelectionModel::Toggle)) { | |
command &= ~QItemSelectionModel::Toggle; | |
d->ctrlDragSelectionFlag = d->selectionModel->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; | |
command |= d->ctrlDragSelectionFlag; | |
} | |
setSelection(rect, command); | |
// signal handlers may change the model | |
emit pressed(index); | |
if (d->autoScroll) { | |
//we delay the autoscrolling to filter out double click event | |
//100 is to be sure that there won't be a double-click misinterpreted as a 2 single clicks | |
d->delayedAutoScroll.start(QApplication::doubleClickInterval()+100, this); | |
} | |
} else { | |
// Forces a finalize() even if mouse is pressed, but not on a item | |
d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select); | |
} | |
} | |
/*! | |
This function is called with the given \a event when a mouse move event is | |
sent to the widget. If a selection is in progress and new items are moved | |
over the selection is extended; if a drag is in progress it is continued. | |
*/ | |
void QAbstractItemView::mouseMoveEvent(QMouseEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
QPoint topLeft; | |
QPoint bottomRight = event->pos(); | |
if (state() == ExpandingState || state() == CollapsingState) | |
return; | |
#ifndef QT_NO_DRAGANDDROP | |
if (state() == DraggingState) { | |
topLeft = d->pressedPosition - d->offset(); | |
if ((topLeft - bottomRight).manhattanLength() > QApplication::startDragDistance()) { | |
d->pressedIndex = QModelIndex(); | |
startDrag(d->model->supportedDragActions()); | |
setState(NoState); // the startDrag will return when the dnd operation is done | |
stopAutoScroll(); | |
} | |
return; | |
} | |
#endif // QT_NO_DRAGANDDROP | |
QPersistentModelIndex index = indexAt(bottomRight); | |
QModelIndex buddy = d->model->buddy(d->pressedIndex); | |
if ((state() == EditingState && d->hasEditor(buddy)) | |
|| edit(index, NoEditTriggers, event)) | |
return; | |
if (d->selectionMode != SingleSelection) | |
topLeft = d->pressedPosition - d->offset(); | |
else | |
topLeft = bottomRight; | |
d->checkMouseMove(index); | |
#ifndef QT_NO_DRAGANDDROP | |
if (d->pressedIndex.isValid() | |
&& d->dragEnabled | |
&& (state() != DragSelectingState) | |
&& (event->buttons() != Qt::NoButton) | |
&& !d->selectedDraggableIndexes().isEmpty()) { | |
setState(DraggingState); | |
return; | |
} | |
#endif | |
if ((event->buttons() & Qt::LeftButton) && d->selectionAllowed(index) && d->selectionModel) { | |
setState(DragSelectingState); | |
QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); | |
if (d->ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) { | |
command &= ~QItemSelectionModel::Toggle; | |
command |= d->ctrlDragSelectionFlag; | |
} | |
// Do the normalize ourselves, since QRect::normalized() is flawed | |
QRect selectionRect = QRect(topLeft, bottomRight); | |
setSelection(selectionRect, command); | |
// set at the end because it might scroll the view | |
if (index.isValid() | |
&& (index != d->selectionModel->currentIndex()) | |
&& d->isIndexEnabled(index)) | |
d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | |
} | |
} | |
/*! | |
This function is called with the given \a event when a mouse button is released, | |
after a mouse press event on the widget. If a user presses the mouse inside your | |
widget and then drags the mouse to another location before releasing the mouse button, | |
your widget receives the release event. The function will emit the clicked() signal if an | |
item was being pressed. | |
*/ | |
void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
QPoint pos = event->pos(); | |
QPersistentModelIndex index = indexAt(pos); | |
if (state() == EditingState) { | |
if (d->isIndexValid(index) | |
&& d->isIndexEnabled(index) | |
&& d->sendDelegateEvent(index, event)) | |
update(index); | |
return; | |
} | |
bool click = (index == d->pressedIndex && index.isValid()); | |
bool selectedClicked = click && (event->button() & Qt::LeftButton) && d->pressedAlreadySelected; | |
EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers); | |
bool edited = edit(index, trigger, event); | |
d->ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; | |
if (d->selectionModel && d->noSelectionOnMousePress) { | |
d->noSelectionOnMousePress = false; | |
d->selectionModel->select(index, selectionCommand(index, event)); | |
} | |
setState(NoState); | |
if (click) { | |
emit clicked(index); | |
if (edited) | |
return; | |
QStyleOptionViewItemV4 option = d->viewOptionsV4(); | |
if (d->pressedAlreadySelected) | |
option.state |= QStyle::State_Selected; | |
if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) | |
emit activated(index); | |
} | |
} | |
/*! | |
This function is called with the given \a event when a mouse button is | |
double clicked inside the widget. If the double-click is on a valid item it | |
emits the doubleClicked() signal and calls edit() on the item. | |
*/ | |
void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
QModelIndex index = indexAt(event->pos()); | |
if (!index.isValid() | |
|| !d->isIndexEnabled(index) | |
|| (d->pressedIndex != index)) { | |
QMouseEvent me(QEvent::MouseButtonPress, | |
event->pos(), event->button(), | |
event->buttons(), event->modifiers()); | |
mousePressEvent(&me); | |
return; | |
} | |
// signal handlers may change the model | |
QPersistentModelIndex persistent = index; | |
emit doubleClicked(persistent); | |
if ((event->button() & Qt::LeftButton) && !edit(persistent, DoubleClicked, event) | |
&& !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) | |
emit activated(persistent); | |
} | |
#ifndef QT_NO_DRAGANDDROP | |
/*! | |
This function is called with the given \a event when a drag and drop operation enters | |
the widget. If the drag is over a valid dropping place (e.g. over an item that | |
accepts drops), the event is accepted; otherwise it is ignored. | |
\sa dropEvent() startDrag() | |
*/ | |
void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event) | |
{ | |
if (dragDropMode() == InternalMove | |
&& (event->source() != this|| !(event->possibleActions() & Qt::MoveAction))) | |
return; | |
if (d_func()->canDecode(event)) { | |
event->accept(); | |
setState(DraggingState); | |
} else { | |
event->ignore(); | |
} | |
} | |
/*! | |
This function is called continuously with the given \a event during a drag and | |
drop operation over the widget. It can cause the view to scroll if, for example, | |
the user drags a selection to view's right or bottom edge. In this case, the | |
event will be accepted; otherwise it will be ignored. | |
\sa dropEvent() startDrag() | |
*/ | |
void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
if (dragDropMode() == InternalMove | |
&& (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) | |
return; | |
// ignore by default | |
event->ignore(); | |
QModelIndex index = indexAt(event->pos()); | |
d->hover = index; | |
if (!d->droppingOnItself(event, index) | |
&& d->canDecode(event)) { | |
if (index.isValid() && d->showDropIndicator) { | |
QRect rect = visualRect(index); | |
d->dropIndicatorPosition = d->position(event->pos(), rect, index); | |
switch (d->dropIndicatorPosition) { | |
case AboveItem: | |
if (d->isIndexDropEnabled(index.parent())) { | |
d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0); | |
event->accept(); | |
} else { | |
d->dropIndicatorRect = QRect(); | |
} | |
break; | |
case BelowItem: | |
if (d->isIndexDropEnabled(index.parent())) { | |
d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0); | |
event->accept(); | |
} else { | |
d->dropIndicatorRect = QRect(); | |
} | |
break; | |
case OnItem: | |
if (d->isIndexDropEnabled(index)) { | |
d->dropIndicatorRect = rect; | |
event->accept(); | |
} else { | |
d->dropIndicatorRect = QRect(); | |
} | |
break; | |
case OnViewport: | |
d->dropIndicatorRect = QRect(); | |
if (d->isIndexDropEnabled(rootIndex())) { | |
event->accept(); // allow dropping in empty areas | |
} | |
break; | |
} | |
} else { | |
d->dropIndicatorRect = QRect(); | |
d->dropIndicatorPosition = OnViewport; | |
if (d->isIndexDropEnabled(rootIndex())) { | |
event->accept(); // allow dropping in empty areas | |
} | |
} | |
d->viewport->update(); | |
} // can decode | |
if (d->shouldAutoScroll(event->pos())) | |
startAutoScroll(); | |
} | |
/*! | |
\internal | |
Return true if this is a move from ourself and \a index is a child of the selection that | |
is being moved. | |
*/ | |
bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index) | |
{ | |
Q_Q(QAbstractItemView); | |
Qt::DropAction dropAction = event->dropAction(); | |
if (q->dragDropMode() == QAbstractItemView::InternalMove) | |
dropAction = Qt::MoveAction; | |
if (event->source() == q | |
&& event->possibleActions() & Qt::MoveAction | |
&& dropAction == Qt::MoveAction) { | |
QModelIndexList selectedIndexes = q->selectedIndexes(); | |
QModelIndex child = index; | |
while (child.isValid() && child != root) { | |
if (selectedIndexes.contains(child)) | |
return true; | |
child = child.parent(); | |
} | |
} | |
return false; | |
} | |
/*! | |
\fn void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *event) | |
This function is called when the item being dragged leaves the view. | |
The \a event describes the state of the drag and drop operation. | |
*/ | |
void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *) | |
{ | |
Q_D(QAbstractItemView); | |
stopAutoScroll(); | |
setState(NoState); | |
d->hover = QModelIndex(); | |
d->viewport->update(); | |
} | |
/*! | |
This function is called with the given \a event when a drop event occurs over | |
the widget. If the model accepts the even position the drop event is accepted; | |
otherwise it is ignored. | |
\sa startDrag() | |
*/ | |
void QAbstractItemView::dropEvent(QDropEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
if (dragDropMode() == InternalMove) { | |
if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) | |
return; | |
} | |
QModelIndex index; | |
int col = -1; | |
int row = -1; | |
if (d->dropOn(event, &row, &col, &index)) { | |
if (d->model->dropMimeData(event->mimeData(), | |
dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction(), row, col, index)) { | |
if (dragDropMode() == InternalMove) | |
event->setDropAction(Qt::MoveAction); | |
event->accept(); | |
} | |
} | |
stopAutoScroll(); | |
setState(NoState); | |
d->viewport->update(); | |
} | |
/*! | |
If the event hasn't already been accepted, determines the index to drop on. | |
if (row == -1 && col == -1) | |
// append to this drop index | |
else | |
// place at row, col in drop index | |
If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop. | |
\internal | |
*/ | |
bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) | |
{ | |
Q_Q(QAbstractItemView); | |
if (event->isAccepted()) | |
return false; | |
QModelIndex index; | |
// rootIndex() (i.e. the viewport) might be a valid index | |
if (viewport->rect().contains(event->pos())) { | |
index = q->indexAt(event->pos()); | |
if (!index.isValid() || !q->visualRect(index).contains(event->pos())) | |
index = root; | |
} | |
// If we are allowed to do the drop | |
if (model->supportedDropActions() & event->dropAction()) { | |
int row = -1; | |
int col = -1; | |
if (index != root) { | |
dropIndicatorPosition = position(event->pos(), q->visualRect(index), index); | |
switch (dropIndicatorPosition) { | |
case QAbstractItemView::AboveItem: | |
row = index.row(); | |
col = index.column(); | |
index = index.parent(); | |
break; | |
case QAbstractItemView::BelowItem: | |
row = index.row() + 1; | |
col = index.column(); | |
index = index.parent(); | |
break; | |
case QAbstractItemView::OnItem: | |
case QAbstractItemView::OnViewport: | |
break; | |
} | |
} else { | |
dropIndicatorPosition = QAbstractItemView::OnViewport; | |
} | |
*dropIndex = index; | |
*dropRow = row; | |
*dropCol = col; | |
if (!droppingOnItself(event, index)) | |
return true; | |
} | |
return false; | |
} | |
QAbstractItemView::DropIndicatorPosition | |
QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const | |
{ | |
QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport; | |
if (!overwrite) { | |
const int margin = 2; | |
if (pos.y() - rect.top() < margin) { | |
r = QAbstractItemView::AboveItem; | |
} else if (rect.bottom() - pos.y() < margin) { | |
r = QAbstractItemView::BelowItem; | |
} else if (rect.contains(pos, true)) { | |
r = QAbstractItemView::OnItem; | |
} | |
} else { | |
QRect touchingRect = rect; | |
touchingRect.adjust(-1, -1, 1, 1); | |
if (touchingRect.contains(pos, false)) { | |
r = QAbstractItemView::OnItem; | |
} | |
} | |
if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled))) | |
r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem; | |
return r; | |
} | |
#endif // QT_NO_DRAGANDDROP | |
/*! | |
This function is called with the given \a event when the widget obtains the focus. | |
By default, the event is ignored. | |
\sa setFocus(), focusOutEvent() | |
*/ | |
void QAbstractItemView::focusInEvent(QFocusEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
QAbstractScrollArea::focusInEvent(event); | |
const QItemSelectionModel* model = selectionModel(); | |
const bool currentIndexValid = currentIndex().isValid(); | |
if (model | |
&& !d->currentIndexSet | |
&& !currentIndexValid) { | |
bool autoScroll = d->autoScroll; | |
d->autoScroll = false; | |
QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index | |
if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) | |
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | |
d->autoScroll = autoScroll; | |
} | |
if (model && currentIndexValid) { | |
if (currentIndex().flags() != Qt::ItemIsEditable) | |
setAttribute(Qt::WA_InputMethodEnabled, false); | |
else | |
setAttribute(Qt::WA_InputMethodEnabled); | |
} | |
if (!currentIndexValid) | |
setAttribute(Qt::WA_InputMethodEnabled, false); | |
d->viewport->update(); | |
} | |
/*! | |
This function is called with the given \a event when the widget | |
looses the focus. By default, the event is ignored. | |
\sa clearFocus(), focusInEvent() | |
*/ | |
void QAbstractItemView::focusOutEvent(QFocusEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
QAbstractScrollArea::focusOutEvent(event); | |
d->viewport->update(); | |
#ifdef QT_SOFTKEYS_ENABLED | |
if(!hasEditFocus()) | |
removeAction(d->doneSoftKey); | |
#endif | |
} | |
/*! | |
This function is called with the given \a event when a key event is sent to | |
the widget. The default implementation handles basic cursor movement, e.g. Up, | |
Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is | |
emitted if the current index is valid and the activation key is pressed | |
(e.g. Enter or Return, depending on the platform). | |
This function is where editing is initiated by key press, e.g. if F2 is | |
pressed. | |
\sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation | |
*/ | |
void QAbstractItemView::keyPressEvent(QKeyEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling | |
#ifdef QT_KEYPAD_NAVIGATION | |
switch (event->key()) { | |
case Qt::Key_Select: | |
if (QApplication::keypadNavigationEnabled()) { | |
if (!hasEditFocus()) { | |
setEditFocus(true); | |
#ifdef QT_SOFTKEYS_ENABLED | |
// If we can't keypad navigate to any direction, there is no sense to add | |
// "Done" softkey, since it basically does nothing when there is | |
// only one widget in screen | |
if(QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) | |
|| QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) | |
addAction(d->doneSoftKey); | |
#endif | |
return; | |
} | |
} | |
break; | |
case Qt::Key_Back: | |
if (QApplication::keypadNavigationEnabled() && hasEditFocus()) { | |
#ifdef QT_SOFTKEYS_ENABLED | |
removeAction(d->doneSoftKey); | |
#endif | |
setEditFocus(false); | |
} else { | |
event->ignore(); | |
} | |
return; | |
case Qt::Key_Down: | |
case Qt::Key_Up: | |
// Let's ignore vertical navigation events, only if there is no other widget | |
// what can take the focus in vertical direction. This means widget can handle navigation events | |
// even the widget don't have edit focus, and there is no other widget in requested direction. | |
if(QApplication::keypadNavigationEnabled() && !hasEditFocus() | |
&& QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) { | |
event->ignore(); | |
return; | |
} | |
break; | |
case Qt::Key_Left: | |
case Qt::Key_Right: | |
// Similar logic as in up and down events | |
if(QApplication::keypadNavigationEnabled() && !hasEditFocus() | |
&& (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this))) { | |
event->ignore(); | |
return; | |
} | |
break; | |
default: | |
if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { | |
event->ignore(); | |
return; | |
} | |
} | |
#endif | |
#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT) | |
if (event == QKeySequence::Copy) { | |
QVariant variant; | |
if (d->model) | |
variant = d->model->data(currentIndex(), Qt::DisplayRole); | |
if (variant.type() == QVariant::String) | |
QApplication::clipboard()->setText(variant.toString()); | |
event->accept(); | |
} | |
#endif | |
QPersistentModelIndex newCurrent; | |
d->moveCursorUpdatedView = false; | |
switch (event->key()) { | |
case Qt::Key_Down: | |
newCurrent = moveCursor(MoveDown, event->modifiers()); | |
break; | |
case Qt::Key_Up: | |
newCurrent = moveCursor(MoveUp, event->modifiers()); | |
break; | |
case Qt::Key_Left: | |
newCurrent = moveCursor(MoveLeft, event->modifiers()); | |
break; | |
case Qt::Key_Right: | |
newCurrent = moveCursor(MoveRight, event->modifiers()); | |
break; | |
case Qt::Key_Home: | |
newCurrent = moveCursor(MoveHome, event->modifiers()); | |
break; | |
case Qt::Key_End: | |
newCurrent = moveCursor(MoveEnd, event->modifiers()); | |
break; | |
case Qt::Key_PageUp: | |
newCurrent = moveCursor(MovePageUp, event->modifiers()); | |
break; | |
case Qt::Key_PageDown: | |
newCurrent = moveCursor(MovePageDown, event->modifiers()); | |
break; | |
case Qt::Key_Tab: | |
if (d->tabKeyNavigation) | |
newCurrent = moveCursor(MoveNext, event->modifiers()); | |
break; | |
case Qt::Key_Backtab: | |
if (d->tabKeyNavigation) | |
newCurrent = moveCursor(MovePrevious, event->modifiers()); | |
break; | |
} | |
QPersistentModelIndex oldCurrent = currentIndex(); | |
if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) { | |
if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent)) | |
setFocus(); | |
QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event); | |
if (command != QItemSelectionModel::NoUpdate | |
|| style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, 0, this)) { | |
// note that we don't check if the new current index is enabled because moveCursor() makes sure it is | |
if (command & QItemSelectionModel::Current) { | |
d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); | |
if (!indexAt(d->pressedPosition - d->offset()).isValid()) | |
d->pressedPosition = visualRect(oldCurrent).center() + d->offset(); | |
QRect rect(d->pressedPosition - d->offset(), visualRect(newCurrent).center()); | |
setSelection(rect, command); | |
} else { | |
d->selectionModel->setCurrentIndex(newCurrent, command); | |
d->pressedPosition = visualRect(newCurrent).center() + d->offset(); | |
if (newCurrent.isValid()) { | |
// We copy the same behaviour as for mousePressEvent(). | |
QRect rect(d->pressedPosition - d->offset(), QSize(1, 1)); | |
setSelection(rect, command); | |
} | |
} | |
event->accept(); | |
return; | |
} | |
} | |
switch (event->key()) { | |
// ignored keys | |
case Qt::Key_Down: | |
case Qt::Key_Up: | |
#ifdef QT_KEYPAD_NAVIGATION | |
if (QApplication::keypadNavigationEnabled() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) { | |
event->accept(); // don't change focus | |
break; | |
} | |
#endif | |
case Qt::Key_Left: | |
case Qt::Key_Right: | |
#ifdef QT_KEYPAD_NAVIGATION | |
if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional | |
&& (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) | |
|| (QWidgetPrivate::inTabWidget(this) && d->model->columnCount(d->root) > 1))) { | |
event->accept(); // don't change focus | |
break; | |
} | |
#endif // QT_KEYPAD_NAVIGATION | |
case Qt::Key_Home: | |
case Qt::Key_End: | |
case Qt::Key_PageUp: | |
case Qt::Key_PageDown: | |
case Qt::Key_Escape: | |
case Qt::Key_Shift: | |
case Qt::Key_Control: | |
case Qt::Key_Delete: | |
case Qt::Key_Backspace: | |
event->ignore(); | |
break; | |
case Qt::Key_Space: | |
case Qt::Key_Select: | |
if (!edit(currentIndex(), AnyKeyPressed, event) && d->selectionModel) | |
d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event)); | |
#ifdef QT_KEYPAD_NAVIGATION | |
if ( event->key()==Qt::Key_Select ) { | |
// Also do Key_Enter action. | |
if (currentIndex().isValid()) { | |
if (state() != EditingState) | |
emit activated(currentIndex()); | |
} else { | |
event->ignore(); | |
} | |
} | |
#endif | |
break; | |
#ifdef Q_WS_MAC | |
case Qt::Key_Enter: | |
case Qt::Key_Return: | |
// Propagate the enter if you couldn't edit the item and there are no | |
// current editors (if there are editors, the event was most likely propagated from it). | |
if (!edit(currentIndex(), EditKeyPressed, event) && d->editors.isEmpty()) | |
event->ignore(); | |
break; | |
#else | |
case Qt::Key_F2: | |
if (!edit(currentIndex(), EditKeyPressed, event)) | |
event->ignore(); | |
break; | |
case Qt::Key_Enter: | |
case Qt::Key_Return: | |
// ### we can't open the editor on enter, becuse | |
// some widgets will forward the enter event back | |
// to the viewport, starting an endless loop | |
if (state() != EditingState || hasFocus()) { | |
if (currentIndex().isValid()) | |
emit activated(currentIndex()); | |
event->ignore(); | |
} | |
break; | |
#endif | |
case Qt::Key_A: | |
if (event->modifiers() & Qt::ControlModifier) { | |
selectAll(); | |
break; | |
} | |
default: { | |
#ifdef Q_WS_MAC | |
if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) { | |
emit activated(currentIndex()); | |
break; | |
} | |
#endif | |
bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)); | |
if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) { | |
keyboardSearch(event->text()); | |
event->accept(); | |
} else { | |
event->ignore(); | |
} | |
break; } | |
} | |
if (d->moveCursorUpdatedView) | |
event->accept(); | |
} | |
/*! | |
This function is called with the given \a event when a resize event is sent to | |
the widget. | |
\sa QWidget::resizeEvent() | |
*/ | |
void QAbstractItemView::resizeEvent(QResizeEvent *event) | |
{ | |
QAbstractScrollArea::resizeEvent(event); | |
updateGeometries(); | |
} | |
/*! | |
This function is called with the given \a event when a timer event is sent | |
to the widget. | |
\sa QObject::timerEvent() | |
*/ | |
void QAbstractItemView::timerEvent(QTimerEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
if (event->timerId() == d->fetchMoreTimer.timerId()) | |
d->fetchMore(); | |
else if (event->timerId() == d->delayedReset.timerId()) | |
reset(); | |
else if (event->timerId() == d->autoScrollTimer.timerId()) | |
doAutoScroll(); | |
else if (event->timerId() == d->updateTimer.timerId()) | |
d->updateDirtyRegion(); | |
else if (event->timerId() == d->delayedEditing.timerId()) { | |
d->delayedEditing.stop(); | |
edit(currentIndex()); | |
} else if (event->timerId() == d->delayedLayout.timerId()) { | |
d->delayedLayout.stop(); | |
if (isVisible()) { | |
d->interruptDelayedItemsLayout(); | |
doItemsLayout(); | |
const QModelIndex current = currentIndex(); | |
if (current.isValid() && d->state == QAbstractItemView::EditingState) | |
scrollTo(current); | |
} | |
} else if (event->timerId() == d->delayedAutoScroll.timerId()) { | |
d->delayedAutoScroll.stop(); | |
//end of the timer: if the current item is still the same as the one when the mouse press occurred | |
//we only get here if there was no double click | |
if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex()) | |
scrollTo(d->pressedIndex); | |
} | |
} | |
/*! | |
\reimp | |
*/ | |
void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event) | |
{ | |
if (event->commitString().isEmpty() && event->preeditString().isEmpty()) { | |
event->ignore(); | |
return; | |
} | |
if (!edit(currentIndex(), AnyKeyPressed, event)) { | |
if (!event->commitString().isEmpty()) | |
keyboardSearch(event->commitString()); | |
event->ignore(); | |
} | |
} | |
#ifndef QT_NO_DRAGANDDROP | |
/*! | |
\enum QAbstractItemView::DropIndicatorPosition | |
This enum indicates the position of the drop indicator in | |
relation to the index at the current mouse position: | |
\value OnItem The item will be dropped on the index. | |
\value AboveItem The item will be dropped above the index. | |
\value BelowItem The item will be dropped below the index. | |
\value OnViewport The item will be dropped onto a region of the viewport with | |
no items. The way each view handles items dropped onto the viewport depends on | |
the behavior of the underlying model in use. | |
*/ | |
/*! | |
\since 4.1 | |
Returns the position of the drop indicator in relation to the closest item. | |
*/ | |
QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->dropIndicatorPosition; | |
} | |
#endif | |
/*! | |
This convenience function returns a list of all selected and | |
non-hidden item indexes in the view. The list contains no | |
duplicates, and is not sorted. | |
\sa QItemSelectionModel::selectedIndexes() | |
*/ | |
QModelIndexList QAbstractItemView::selectedIndexes() const | |
{ | |
Q_D(const QAbstractItemView); | |
QModelIndexList indexes; | |
if (d->selectionModel) { | |
indexes = d->selectionModel->selectedIndexes(); | |
QList<QModelIndex>::iterator it = indexes.begin(); | |
while (it != indexes.end()) | |
if (isIndexHidden(*it)) | |
it = indexes.erase(it); | |
else | |
++it; | |
} | |
return indexes; | |
} | |
/*! | |
Starts editing the item at \a index, creating an editor if | |
necessary, and returns true if the view's \l{State} is now | |
EditingState; otherwise returns false. | |
The action that caused the editing process is described by | |
\a trigger, and the associated event is specified by \a event. | |
Editing can be forced by specifying the \a trigger to be | |
QAbstractItemView::AllEditTriggers. | |
\sa closeEditor() | |
*/ | |
bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) | |
{ | |
Q_D(QAbstractItemView); | |
if (!d->isIndexValid(index)) | |
return false; | |
if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).editor.data())) { | |
if (w->focusPolicy() == Qt::NoFocus) | |
return false; | |
w->setFocus(); | |
return true; | |
} | |
if (trigger == DoubleClicked) { | |
d->delayedEditing.stop(); | |
d->delayedAutoScroll.stop(); | |
} else if (trigger == CurrentChanged) { | |
d->delayedEditing.stop(); | |
} | |
if (d->sendDelegateEvent(index, event)) { | |
update(index); | |
return true; | |
} | |
// save the previous trigger before updating | |
EditTriggers lastTrigger = d->lastTrigger; | |
d->lastTrigger = trigger; | |
if (!d->shouldEdit(trigger, d->model->buddy(index))) | |
return false; | |
if (d->delayedEditing.isActive()) | |
return false; | |
// we will receive a mouseButtonReleaseEvent after a | |
// mouseDoubleClickEvent, so we need to check the previous trigger | |
if (lastTrigger == DoubleClicked && trigger == SelectedClicked) | |
return false; | |
// we may get a double click event later | |
if (trigger == SelectedClicked) | |
d->delayedEditing.start(QApplication::doubleClickInterval(), this); | |
else | |
d->openEditor(index, d->shouldForwardEvent(trigger, event) ? event : 0); | |
return true; | |
} | |
/*! | |
\internal | |
Updates the data shown in the open editor widgets in the view. | |
*/ | |
void QAbstractItemView::updateEditorData() | |
{ | |
Q_D(QAbstractItemView); | |
d->updateEditorData(QModelIndex(), QModelIndex()); | |
} | |
/*! | |
\internal | |
Updates the geometry of the open editor widgets in the view. | |
*/ | |
void QAbstractItemView::updateEditorGeometries() | |
{ | |
Q_D(QAbstractItemView); | |
if(d->editors.isEmpty()) | |
return; | |
QStyleOptionViewItemV4 option = d->viewOptionsV4(); | |
QList<QEditorInfo>::iterator it = d->editors.begin(); | |
QWidgetList editorsToRelease; | |
QWidgetList editorsToHide; | |
while (it != d->editors.end()) { | |
QModelIndex index = it->index; | |
QWidget *editor = it->editor; | |
if (index.isValid() && editor) { | |
option.rect = visualRect(index); | |
if (option.rect.isValid()) { | |
editor->show(); | |
QAbstractItemDelegate *delegate = d->delegateForIndex(index); | |
if (delegate) | |
delegate->updateEditorGeometry(editor, option, index); | |
} else { | |
editorsToHide << editor; | |
} | |
++it; | |
} else { | |
it = d->editors.erase(it); | |
editorsToRelease << editor; | |
} | |
} | |
//we hide and release the editor outside of the loop because it might change the focus and try | |
//to change the d->editors list. | |
for (int i = 0; i < editorsToHide.count(); ++i) { | |
editorsToHide.at(i)->hide(); | |
} | |
for (int i = 0; i < editorsToRelease.count(); ++i) { | |
d->releaseEditor(editorsToRelease.at(i)); | |
} | |
} | |
/*! | |
\since 4.4 | |
Updates the geometry of the child widgets of the view. | |
*/ | |
void QAbstractItemView::updateGeometries() | |
{ | |
updateEditorGeometries(); | |
d_func()->fetchMoreTimer.start(0, this); //fetch more later | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::verticalScrollbarValueChanged(int value) | |
{ | |
Q_D(QAbstractItemView); | |
if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) | |
d->model->fetchMore(d->root); | |
QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos()); | |
if (viewport()->rect().contains(posInVp)) | |
d->checkMouseMove(posInVp); | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::horizontalScrollbarValueChanged(int value) | |
{ | |
Q_D(QAbstractItemView); | |
if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) | |
d->model->fetchMore(d->root); | |
QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos()); | |
if (viewport()->rect().contains(posInVp)) | |
d->checkMouseMove(posInVp); | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::verticalScrollbarAction(int) | |
{ | |
//do nothing | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::horizontalScrollbarAction(int) | |
{ | |
//do nothing | |
} | |
/*! | |
Closes the given \a editor, and releases it. The \a hint is | |
used to specify how the view should respond to the end of the editing | |
operation. For example, the hint may indicate that the next item in | |
the view should be opened for editing. | |
\sa edit(), commitData() | |
*/ | |
void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint) | |
{ | |
Q_D(QAbstractItemView); | |
// Close the editor | |
if (editor) { | |
bool isPersistent = d->persistent.contains(editor); | |
bool hadFocus = editor->hasFocus(); | |
QModelIndex index = d->indexForEditor(editor); | |
if (!index.isValid()) | |
return; // the editor was not registered | |
if (!isPersistent) { | |
setState(NoState); | |
QModelIndex index = d->indexForEditor(editor); | |
editor->removeEventFilter(d->delegateForIndex(index)); | |
d->removeEditor(editor); | |
} | |
if (hadFocus) | |
setFocus(); // this will send a focusLost event to the editor | |
else | |
d->checkPersistentEditorFocus(); | |
QPointer<QWidget> ed = editor; | |
QApplication::sendPostedEvents(editor, 0); | |
editor = ed; | |
if (!isPersistent && editor) | |
d->releaseEditor(editor); | |
} | |
// The EndEditHint part | |
QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect | |
| d->selectionBehaviorFlags(); | |
switch (hint) { | |
case QAbstractItemDelegate::EditNextItem: { | |
QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); | |
if (index.isValid()) { | |
QPersistentModelIndex persistent(index); | |
d->selectionModel->setCurrentIndex(persistent, flags); | |
// currentChanged signal would have already started editing | |
if (index.flags() & Qt::ItemIsEditable | |
&& (!(editTriggers() & QAbstractItemView::CurrentChanged))) | |
edit(persistent); | |
} break; } | |
case QAbstractItemDelegate::EditPreviousItem: { | |
QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier); | |
if (index.isValid()) { | |
QPersistentModelIndex persistent(index); | |
d->selectionModel->setCurrentIndex(persistent, flags); | |
// currentChanged signal would have already started editing | |
if (index.flags() & Qt::ItemIsEditable | |
&& (!(editTriggers() & QAbstractItemView::CurrentChanged))) | |
edit(persistent); | |
} break; } | |
case QAbstractItemDelegate::SubmitModelCache: | |
d->model->submit(); | |
break; | |
case QAbstractItemDelegate::RevertModelCache: | |
d->model->revert(); | |
break; | |
default: | |
break; | |
} | |
} | |
/*! | |
Commit the data in the \a editor to the model. | |
\sa closeEditor() | |
*/ | |
void QAbstractItemView::commitData(QWidget *editor) | |
{ | |
Q_D(QAbstractItemView); | |
if (!editor || !d->itemDelegate || d->currentlyCommittingEditor) | |
return; | |
QModelIndex index = d->indexForEditor(editor); | |
if (!index.isValid()) | |
return; | |
d->currentlyCommittingEditor = editor; | |
QAbstractItemDelegate *delegate = d->delegateForIndex(index); | |
editor->removeEventFilter(delegate); | |
delegate->setModelData(editor, d->model, index); | |
editor->installEventFilter(delegate); | |
d->currentlyCommittingEditor = 0; | |
} | |
/*! | |
This function is called when the given \a editor has been destroyed. | |
\sa closeEditor() | |
*/ | |
void QAbstractItemView::editorDestroyed(QObject *editor) | |
{ | |
Q_D(QAbstractItemView); | |
QWidget *w = qobject_cast<QWidget*>(editor); | |
d->removeEditor(w); | |
d->persistent.remove(w); | |
if (state() == EditingState) | |
setState(NoState); | |
} | |
/*! | |
\obsolete | |
Sets the horizontal scroll bar's steps per item to \a steps. | |
This is the number of steps used by the horizontal scroll bar to | |
represent the width of an item. | |
Note that if the view has a horizontal header, the item steps | |
will be ignored and the header section size will be used instead. | |
\sa horizontalStepsPerItem() setVerticalStepsPerItem() | |
*/ | |
void QAbstractItemView::setHorizontalStepsPerItem(int steps) | |
{ | |
Q_UNUSED(steps); | |
// do nothing | |
} | |
/*! | |
\obsolete | |
Returns the horizontal scroll bar's steps per item. | |
\sa setHorizontalStepsPerItem() verticalStepsPerItem() | |
*/ | |
int QAbstractItemView::horizontalStepsPerItem() const | |
{ | |
return 1; | |
} | |
/*! | |
\obsolete | |
Sets the vertical scroll bar's steps per item to \a steps. | |
This is the number of steps used by the vertical scroll bar to | |
represent the height of an item. | |
Note that if the view has a vertical header, the item steps | |
will be ignored and the header section size will be used instead. | |
\sa verticalStepsPerItem() setHorizontalStepsPerItem() | |
*/ | |
void QAbstractItemView::setVerticalStepsPerItem(int steps) | |
{ | |
Q_UNUSED(steps); | |
// do nothing | |
} | |
/*! | |
\obsolete | |
Returns the vertical scroll bar's steps per item. | |
\sa setVerticalStepsPerItem() horizontalStepsPerItem() | |
*/ | |
int QAbstractItemView::verticalStepsPerItem() const | |
{ | |
return 1; | |
} | |
/*! | |
Moves to and selects the item best matching the string \a search. | |
If no item is found nothing happens. | |
In the default implementation, the search is reset if \a search is empty, or | |
the time interval since the last search has exceeded | |
QApplication::keyboardInputInterval(). | |
*/ | |
void QAbstractItemView::keyboardSearch(const QString &search) | |
{ | |
Q_D(QAbstractItemView); | |
if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root)) | |
return; | |
QModelIndex start = currentIndex().isValid() ? currentIndex() | |
: d->model->index(0, 0, d->root); | |
bool skipRow = false; | |
bool keyboardTimeWasValid = d->keyboardInputTime.isValid(); | |
qint64 keyboardInputTimeElapsed = d->keyboardInputTime.restart(); | |
if (search.isEmpty() || !keyboardTimeWasValid | |
|| keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) { | |
d->keyboardInput = search; | |
skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0) | |
} else { | |
d->keyboardInput += search; | |
} | |
// special case for searches with same key like 'aaaaa' | |
bool sameKey = false; | |
if (d->keyboardInput.length() > 1) { | |
int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1)); | |
sameKey = (c == d->keyboardInput.length()); | |
if (sameKey) | |
skipRow = true; | |
} | |
// skip if we are searching for the same key or a new search started | |
if (skipRow) { | |
QModelIndex parent = start.parent(); | |
int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0; | |
start = d->model->index(newRow, start.column(), parent); | |
} | |
// search from start with wraparound | |
const QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput; | |
QModelIndex current = start; | |
QModelIndexList match; | |
QModelIndex firstMatch; | |
QModelIndex startMatch; | |
QModelIndexList previous; | |
do { | |
match = d->model->match(current, Qt::DisplayRole, searchString); | |
if (match == previous) | |
break; | |
firstMatch = match.value(0); | |
previous = match; | |
if (firstMatch.isValid()) { | |
if (d->isIndexEnabled(firstMatch)) { | |
setCurrentIndex(firstMatch); | |
break; | |
} | |
int row = firstMatch.row() + 1; | |
if (row >= d->model->rowCount(firstMatch.parent())) | |
row = 0; | |
current = firstMatch.sibling(row, firstMatch.column()); | |
//avoid infinite loop if all the matching items are disabled. | |
if (!startMatch.isValid()) | |
startMatch = firstMatch; | |
else if (startMatch == firstMatch) | |
break; | |
} | |
} while (current != start && firstMatch.isValid()); | |
} | |
/*! | |
Returns the size hint for the item with the specified \a index or | |
an invalid size for invalid indexes. | |
\sa sizeHintForRow(), sizeHintForColumn() | |
*/ | |
QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const | |
{ | |
Q_D(const QAbstractItemView); | |
if (!d->isIndexValid(index) || !d->itemDelegate) | |
return QSize(); | |
return d->delegateForIndex(index)->sizeHint(d->viewOptionsV4(), index); | |
} | |
/*! | |
Returns the height size hint for the specified \a row or -1 if | |
there is no model. | |
The returned height is calculated using the size hints of the | |
given \a row's items, i.e. the returned value is the maximum | |
height among the items. Note that to control the height of a row, | |
you must reimplement the QAbstractItemDelegate::sizeHint() | |
function. | |
This function is used in views with a vertical header to find the | |
size hint for a header section based on the contents of the given | |
\a row. | |
\sa sizeHintForColumn() | |
*/ | |
int QAbstractItemView::sizeHintForRow(int row) const | |
{ | |
Q_D(const QAbstractItemView); | |
if (row < 0 || row >= d->model->rowCount() || !model()) | |
return -1; | |
ensurePolished(); | |
QStyleOptionViewItemV4 option = d->viewOptionsV4(); | |
int height = 0; | |
int colCount = d->model->columnCount(d->root); | |
QModelIndex index; | |
for (int c = 0; c < colCount; ++c) { | |
index = d->model->index(row, c, d->root); | |
if (QWidget *editor = d->editorForIndex(index).editor) | |
height = qMax(height, editor->size().height()); | |
int hint = d->delegateForIndex(index)->sizeHint(option, index).height(); | |
height = qMax(height, hint); | |
} | |
return height; | |
} | |
/*! | |
Returns the width size hint for the specified \a column or -1 if there is no model. | |
This function is used in views with a horizontal header to find the size hint for | |
a header section based on the contents of the given \a column. | |
\sa sizeHintForRow() | |
*/ | |
int QAbstractItemView::sizeHintForColumn(int column) const | |
{ | |
Q_D(const QAbstractItemView); | |
if (column < 0 || column >= d->model->columnCount() || !model()) | |
return -1; | |
ensurePolished(); | |
QStyleOptionViewItemV4 option = d->viewOptionsV4(); | |
int width = 0; | |
int rows = d->model->rowCount(d->root); | |
QModelIndex index; | |
for (int r = 0; r < rows; ++r) { | |
index = d->model->index(r, column, d->root); | |
if (QWidget *editor = d->editorForIndex(index).editor) | |
width = qMax(width, editor->sizeHint().width()); | |
int hint = d->delegateForIndex(index)->sizeHint(option, index).width(); | |
width = qMax(width, hint); | |
} | |
return width; | |
} | |
/*! | |
Opens a persistent editor on the item at the given \a index. | |
If no editor exists, the delegate will create a new editor. | |
\sa closePersistentEditor() | |
*/ | |
void QAbstractItemView::openPersistentEditor(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
QStyleOptionViewItemV4 options = d->viewOptionsV4(); | |
options.rect = visualRect(index); | |
options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); | |
QWidget *editor = d->editor(index, options); | |
if (editor) { | |
editor->show(); | |
d->persistent.insert(editor); | |
} | |
} | |
/*! | |
Closes the persistent editor for the item at the given \a index. | |
\sa openPersistentEditor() | |
*/ | |
void QAbstractItemView::closePersistentEditor(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
QWidget *editor = d->editorForIndex(index).editor; | |
if (editor) { | |
if (index == selectionModel()->currentIndex()) | |
closeEditor(editor, QAbstractItemDelegate::RevertModelCache); | |
d->persistent.remove(editor); | |
d->removeEditor(editor); | |
d->releaseEditor(editor); | |
} | |
} | |
/*! | |
\since 4.1 | |
Sets the given \a widget on the item at the given \a index, passing the | |
ownership of the widget to the viewport. | |
If \a index is invalid (e.g., if you pass the root index), this function | |
will do nothing. | |
The given \a widget's \l{QWidget}{autoFillBackground} property must be set | |
to true, otherwise the widget's background will be transparent, showing | |
both the model data and the item at the given \a index. | |
If index widget A is replaced with index widget B, index widget A will be | |
deleted. For example, in the code snippet below, the QLineEdit object will | |
be deleted. | |
\snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 1 | |
This function should only be used to display static content within the | |
visible area corresponding to an item of data. If you want to display | |
custom dynamic content or implement a custom editor widget, subclass | |
QItemDelegate instead. | |
\sa {Delegate Classes} | |
*/ | |
void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget) | |
{ | |
Q_D(QAbstractItemView); | |
if (!d->isIndexValid(index)) | |
return; | |
if (QWidget *oldWidget = indexWidget(index)) { | |
d->persistent.remove(oldWidget); | |
d->removeEditor(oldWidget); | |
oldWidget->deleteLater(); | |
} | |
if (widget) { | |
widget->setParent(viewport()); | |
d->persistent.insert(widget); | |
d->addEditor(index, widget, true); | |
widget->show(); | |
dataChanged(index, index); // update the geometry | |
if (!d->delayedPendingLayout) | |
widget->setGeometry(visualRect(index)); | |
} | |
} | |
/*! | |
\since 4.1 | |
Returns the widget for the item at the given \a index. | |
*/ | |
QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const | |
{ | |
Q_D(const QAbstractItemView); | |
if (!d->isIndexValid(index)) | |
return 0; | |
return d->editorForIndex(index).editor; | |
} | |
/*! | |
\since 4.1 | |
Scrolls the view to the top. | |
\sa scrollTo(), scrollToBottom() | |
*/ | |
void QAbstractItemView::scrollToTop() | |
{ | |
verticalScrollBar()->setValue(verticalScrollBar()->minimum()); | |
} | |
/*! | |
\since 4.1 | |
Scrolls the view to the bottom. | |
\sa scrollTo(), scrollToTop() | |
*/ | |
void QAbstractItemView::scrollToBottom() | |
{ | |
Q_D(QAbstractItemView); | |
if (d->delayedPendingLayout) { | |
d->executePostedLayout(); | |
updateGeometries(); | |
} | |
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); | |
} | |
/*! | |
\since 4.3 | |
Updates the area occupied by the given \a index. | |
*/ | |
void QAbstractItemView::update(const QModelIndex &index) | |
{ | |
Q_D(QAbstractItemView); | |
if (index.isValid()) { | |
const QRect rect = visualRect(index); | |
//this test is important for peformance reason | |
//For example in dataChanged we simply update all the cells without checking | |
//it can be a major bottleneck to update rects that aren't even part of the viewport | |
if (d->viewport->rect().intersects(rect)) | |
d->viewport->update(rect); | |
} | |
} | |
/*! | |
This slot is called when items are changed in the model. The | |
changed items are those from \a topLeft to \a bottomRight | |
inclusive. If just one item is changed \a topLeft == \a | |
bottomRight. | |
*/ | |
void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) | |
{ | |
// Single item changed | |
Q_D(QAbstractItemView); | |
if (topLeft == bottomRight && topLeft.isValid()) { | |
const QEditorInfo editorInfo = d->editorForIndex(topLeft); | |
//we don't update the edit data if it is static | |
if (!editorInfo.isStatic && editorInfo.editor) { | |
QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); | |
if (delegate) { | |
delegate->setEditorData(editorInfo.editor, topLeft); | |
} | |
} | |
if (isVisible() && !d->delayedPendingLayout) { | |
// otherwise the items will be update later anyway | |
update(topLeft); | |
} | |
return; | |
} | |
d->updateEditorData(topLeft, bottomRight); | |
if (!isVisible() || d->delayedPendingLayout) | |
return; // no need to update | |
d->viewport->update(); | |
} | |
/*! | |
This slot is called when rows are inserted. The new rows are those | |
under the given \a parent from \a start to \a end inclusive. The | |
base class implementation calls fetchMore() on the model to check | |
for more data. | |
\sa rowsAboutToBeRemoved() | |
*/ | |
void QAbstractItemView::rowsInserted(const QModelIndex &, int, int) | |
{ | |
if (!isVisible()) | |
d_func()->fetchMoreTimer.start(0, this); //fetch more later | |
else | |
updateEditorGeometries(); | |
} | |
/*! | |
This slot is called when rows are about to be removed. The deleted rows are | |
those under the given \a parent from \a start to \a end inclusive. | |
\sa rowsInserted() | |
*/ | |
void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) | |
{ | |
Q_D(QAbstractItemView); | |
setState(CollapsingState); | |
// Ensure one selected item in single selection mode. | |
QModelIndex current = currentIndex(); | |
if (d->selectionMode == SingleSelection | |
&& current.isValid() | |
&& current.row() >= start | |
&& current.row() <= end | |
&& current.parent() == parent) { | |
int totalToRemove = end - start + 1; | |
if (d->model->rowCount(parent) <= totalToRemove) { // no more children | |
QModelIndex index = parent; | |
while (index != d->root && !d->isIndexEnabled(index)) | |
index = index.parent(); | |
if (index != d->root) | |
setCurrentIndex(index); | |
} else { | |
int row = end + 1; | |
QModelIndex next; | |
do { // find the next visible and enabled item | |
next = d->model->index(row++, current.column(), current.parent()); | |
} while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); | |
if (row > d->model->rowCount(parent)) { | |
row = start - 1; | |
do { // find the previous visible and enabled item | |
next = d->model->index(row--, current.column(), current.parent()); | |
} while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); | |
} | |
setCurrentIndex(next); | |
} | |
} | |
// Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes | |
for (int i = d->editors.size() - 1; i >= 0; --i) { | |
const QModelIndex index = d->editors.at(i).index; | |
QWidget *editor = d->editors.at(i).editor; | |
if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { | |
d->editors.removeAt(i); | |
d->releaseEditor(editor); | |
} | |
} | |
} | |
/*! | |
\internal | |
This slot is called when rows have been removed. The deleted | |
rows are those under the given \a parent from \a start to \a end | |
inclusive. | |
*/ | |
void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &, int, int) | |
{ | |
Q_Q(QAbstractItemView); | |
if (q->isVisible()) | |
q->updateEditorGeometries(); | |
q->setState(QAbstractItemView::NoState); | |
} | |
/*! | |
\internal | |
This slot is called when columns are about to be removed. The deleted | |
columns are those under the given \a parent from \a start to \a end | |
inclusive. | |
*/ | |
void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) | |
{ | |
Q_Q(QAbstractItemView); | |
q->setState(QAbstractItemView::CollapsingState); | |
// Ensure one selected item in single selection mode. | |
QModelIndex current = q->currentIndex(); | |
if (current.isValid() | |
&& selectionMode == QAbstractItemView::SingleSelection | |
&& current.column() >= start | |
&& current.column() <= end) { | |
int totalToRemove = end - start + 1; | |
if (model->columnCount(parent) < totalToRemove) { // no more columns | |
QModelIndex index = parent; | |
while (index.isValid() && !isIndexEnabled(index)) | |
index = index.parent(); | |
if (index.isValid()) | |
q->setCurrentIndex(index); | |
} else { | |
int column = end; | |
QModelIndex next; | |
do { // find the next visible and enabled item | |
next = model->index(current.row(), column++, current.parent()); | |
} while (next.isValid() && (q->isIndexHidden(next) || !isIndexEnabled(next))); | |
q->setCurrentIndex(next); | |
} | |
} | |
// Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes | |
QList<QEditorInfo>::iterator it = editors.begin(); | |
while (it != editors.end()) { | |
QModelIndex index = it->index; | |
if (index.column() <= start && index.column() >= end && model->parent(index) == parent) { | |
QWidget *editor = it->editor; | |
it = editors.erase(it); | |
releaseEditor(editor); | |
} else { | |
++it; | |
} | |
} | |
} | |
/*! | |
\internal | |
This slot is called when columns have been removed. The deleted | |
rows are those under the given \a parent from \a start to \a end | |
inclusive. | |
*/ | |
void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &, int, int) | |
{ | |
Q_Q(QAbstractItemView); | |
if (q->isVisible()) | |
q->updateEditorGeometries(); | |
q->setState(QAbstractItemView::NoState); | |
} | |
/*! | |
\internal | |
This slot is called when rows have been inserted. | |
*/ | |
void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &, int, int) | |
{ | |
Q_Q(QAbstractItemView); | |
if (q->isVisible()) | |
q->updateEditorGeometries(); | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemViewPrivate::_q_modelDestroyed() | |
{ | |
model = QAbstractItemModelPrivate::staticEmptyModel(); | |
doDelayedReset(); | |
} | |
/*! | |
\internal | |
This slot is called when the layout is changed. | |
*/ | |
void QAbstractItemViewPrivate::_q_layoutChanged() | |
{ | |
doDelayedItemsLayout(); | |
} | |
/*! | |
This slot is called when the selection is changed. The previous | |
selection (which may be empty), is specified by \a deselected, and the | |
new selection by \a selected. | |
\sa setSelection() | |
*/ | |
void QAbstractItemView::selectionChanged(const QItemSelection &selected, | |
const QItemSelection &deselected) | |
{ | |
Q_D(QAbstractItemView); | |
if (isVisible() && updatesEnabled()) { | |
d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected)); | |
} | |
} | |
/*! | |
This slot is called when a new item becomes the current item. | |
The previous current item is specified by the \a previous index, and the new | |
item by the \a current index. | |
If you want to know about changes to items see the | |
dataChanged() signal. | |
*/ | |
void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) | |
{ | |
Q_D(QAbstractItemView); | |
Q_ASSERT(d->model); | |
if (previous.isValid()) { | |
QModelIndex buddy = d->model->buddy(previous); | |
QWidget *editor = d->editorForIndex(buddy).editor; | |
if (editor && !d->persistent.contains(editor)) { | |
commitData(editor); | |
if (current.row() != previous.row()) | |
closeEditor(editor, QAbstractItemDelegate::SubmitModelCache); | |
else | |
closeEditor(editor, QAbstractItemDelegate::NoHint); | |
} | |
if (isVisible()) { | |
update(previous); | |
} | |
} | |
if (current.isValid() && !d->autoScrollTimer.isActive()) { | |
if (isVisible()) { | |
if (d->autoScroll) | |
scrollTo(current); | |
update(current); | |
edit(current, CurrentChanged, 0); | |
if (current.row() == (d->model->rowCount(d->root) - 1)) | |
d->fetchMore(); | |
} else { | |
d->shouldScrollToCurrentOnShow = d->autoScroll; | |
} | |
} | |
} | |
#ifndef QT_NO_DRAGANDDROP | |
/*! | |
Starts a drag by calling drag->exec() using the given \a supportedActions. | |
*/ | |
void QAbstractItemView::startDrag(Qt::DropActions supportedActions) | |
{ | |
Q_D(QAbstractItemView); | |
QModelIndexList indexes = d->selectedDraggableIndexes(); | |
if (indexes.count() > 0) { | |
QMimeData *data = d->model->mimeData(indexes); | |
if (!data) | |
return; | |
QRect rect; | |
QPixmap pixmap = d->renderToPixmap(indexes, &rect); | |
rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); | |
QDrag *drag = new QDrag(this); | |
drag->setPixmap(pixmap); | |
drag->setMimeData(data); | |
drag->setHotSpot(d->pressedPosition - rect.topLeft()); | |
Qt::DropAction defaultDropAction = Qt::IgnoreAction; | |
if (d->defaultDropAction != Qt::IgnoreAction && (supportedActions & d->defaultDropAction)) | |
defaultDropAction = d->defaultDropAction; | |
else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) | |
defaultDropAction = Qt::CopyAction; | |
if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) | |
d->clearOrRemove(); | |
} | |
} | |
#endif // QT_NO_DRAGANDDROP | |
/*! | |
Returns a QStyleOptionViewItem structure populated with the view's | |
palette, font, state, alignments etc. | |
*/ | |
QStyleOptionViewItem QAbstractItemView::viewOptions() const | |
{ | |
Q_D(const QAbstractItemView); | |
QStyleOptionViewItem option; | |
option.init(this); | |
option.state &= ~QStyle::State_MouseOver; | |
option.font = font(); | |
#ifndef Q_WS_MAC | |
// On mac the focus appearance follows window activation | |
// not widget activation | |
if (!hasFocus()) | |
option.state &= ~QStyle::State_Active; | |
#endif | |
option.state &= ~QStyle::State_HasFocus; | |
if (d->iconSize.isValid()) { | |
option.decorationSize = d->iconSize; | |
} else { | |
int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this); | |
option.decorationSize = QSize(pm, pm); | |
} | |
option.decorationPosition = QStyleOptionViewItem::Left; | |
option.decorationAlignment = Qt::AlignCenter; | |
option.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; | |
option.textElideMode = d->textElideMode; | |
option.rect = QRect(); | |
option.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, this); | |
return option; | |
} | |
QStyleOptionViewItemV4 QAbstractItemViewPrivate::viewOptionsV4() const | |
{ | |
Q_Q(const QAbstractItemView); | |
QStyleOptionViewItemV4 option = q->viewOptions(); | |
if (wrapItemText) | |
option.features = QStyleOptionViewItemV2::WrapText; | |
option.locale = q->locale(); | |
option.locale.setNumberOptions(QLocale::OmitGroupSeparator); | |
option.widget = q; | |
return option; | |
} | |
/*! | |
Returns the item view's state. | |
\sa setState() | |
*/ | |
QAbstractItemView::State QAbstractItemView::state() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->state; | |
} | |
/*! | |
Sets the item view's state to the given \a state. | |
\sa state() | |
*/ | |
void QAbstractItemView::setState(State state) | |
{ | |
Q_D(QAbstractItemView); | |
d->state = state; | |
} | |
/*! | |
Schedules a layout of the items in the view to be executed when the | |
event processing starts. | |
Even if scheduleDelayedItemsLayout() is called multiple times before | |
events are processed, the view will only do the layout once. | |
\sa executeDelayedItemsLayout() | |
*/ | |
void QAbstractItemView::scheduleDelayedItemsLayout() | |
{ | |
Q_D(QAbstractItemView); | |
d->doDelayedItemsLayout(); | |
} | |
/*! | |
Executes the scheduled layouts without waiting for the event processing | |
to begin. | |
\sa scheduleDelayedItemsLayout() | |
*/ | |
void QAbstractItemView::executeDelayedItemsLayout() | |
{ | |
Q_D(QAbstractItemView); | |
d->executePostedLayout(); | |
} | |
/*! | |
\since 4.1 | |
Marks the given \a region as dirty and schedules it to be updated. | |
You only need to call this function if you are implementing | |
your own view subclass. | |
\sa scrollDirtyRegion(), dirtyRegionOffset() | |
*/ | |
void QAbstractItemView::setDirtyRegion(const QRegion ®ion) | |
{ | |
Q_D(QAbstractItemView); | |
d->setDirtyRegion(region); | |
} | |
/*! | |
Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the | |
opposite direction. You only need to call this function if you are implementing a scrolling | |
viewport in your view subclass. | |
If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function | |
before you call QWidget::scroll() on the viewport. Alternatively, just call update(). | |
\sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion() | |
*/ | |
void QAbstractItemView::scrollDirtyRegion(int dx, int dy) | |
{ | |
Q_D(QAbstractItemView); | |
d->scrollDirtyRegion(dx, dy); | |
} | |
/*! | |
Returns the offset of the dirty regions in the view. | |
If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of | |
QAbstractItemView, you should translate the area given by the paint event with | |
the offset returned from this function. | |
\sa scrollDirtyRegion(), setDirtyRegion() | |
*/ | |
QPoint QAbstractItemView::dirtyRegionOffset() const | |
{ | |
Q_D(const QAbstractItemView); | |
return d->scrollDelayOffset; | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::startAutoScroll() | |
{ | |
d_func()->startAutoScroll(); | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::stopAutoScroll() | |
{ | |
d_func()->stopAutoScroll(); | |
} | |
/*! | |
\internal | |
*/ | |
void QAbstractItemView::doAutoScroll() | |
{ | |
// find how much we should scroll with | |
Q_D(QAbstractItemView); | |
int verticalStep = verticalScrollBar()->pageStep(); | |
int horizontalStep = horizontalScrollBar()->pageStep(); | |
if (d->autoScrollCount < qMax(verticalStep, horizontalStep)) | |
++d->autoScrollCount; | |
int margin = d->autoScrollMargin; | |
int verticalValue = verticalScrollBar()->value(); | |
int horizontalValue = horizontalScrollBar()->value(); | |
QPoint pos = d->viewport->mapFromGlobal(QCursor::pos()); | |
QRect area = static_cast<QAbstractItemView*>(d->viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules | |
// do the scrolling if we are in the scroll margins | |
if (pos.y() - area.top() < margin) | |
verticalScrollBar()->setValue(verticalValue - d->autoScrollCount); | |
else if (area.bottom() - pos.y() < margin) | |
verticalScrollBar()->setValue(verticalValue + d->autoScrollCount); | |
if (pos.x() - area.left() < margin) | |
horizontalScrollBar()->setValue(horizontalValue - d->autoScrollCount); | |
else if (area.right() - pos.x() < margin) | |
horizontalScrollBar()->setValue(horizontalValue + d->autoScrollCount); | |
// if nothing changed, stop scrolling | |
bool verticalUnchanged = (verticalValue == verticalScrollBar()->value()); | |
bool horizontalUnchanged = (horizontalValue == horizontalScrollBar()->value()); | |
if (verticalUnchanged && horizontalUnchanged) { | |
stopAutoScroll(); | |
} else { | |
#ifndef QT_NO_DRAGANDDROP | |
d->dropIndicatorRect = QRect(); | |
d->dropIndicatorPosition = QAbstractItemView::OnViewport; | |
#endif | |
d->viewport->update(); | |
} | |
} | |
/*! | |
Returns the SelectionFlags to be used when updating a selection with | |
to include the \a index specified. The \a event is a user input event, | |
such as a mouse or keyboard event. | |
Reimplement this function to define your own selection behavior. | |
\sa setSelection() | |
*/ | |
QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index, | |
const QEvent *event) const | |
{ | |
Q_D(const QAbstractItemView); | |
switch (d->selectionMode) { | |
case NoSelection: // Never update selection model | |
return QItemSelectionModel::NoUpdate; | |
case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate | |
if (event && event->type() == QEvent::MouseButtonRelease) | |
return QItemSelectionModel::NoUpdate; | |
return QItemSelectionModel::ClearAndSelect|d->selectionBehaviorFlags(); | |
case MultiSelection: | |
return d->multiSelectionCommand(index, event); | |
case ExtendedSelection: | |
return d->extendedSelectionCommand(index, event); | |
case ContiguousSelection: | |
return d->contiguousSelectionCommand(index, event); | |
} | |
return QItemSelectionModel::NoUpdate; | |
} | |
QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand( | |
const QModelIndex &index, const QEvent *event) const | |
{ | |
Q_UNUSED(index); | |
if (event) { | |
switch (event->type()) { | |
case QEvent::KeyPress: | |
if (static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Space | |
|| static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Select) | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); | |
break; | |
case QEvent::MouseButtonPress: | |
if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); // toggle | |
break; | |
case QEvent::MouseButtonRelease: | |
if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) | |
return QItemSelectionModel::NoUpdate|selectionBehaviorFlags(); // finalize | |
break; | |
case QEvent::MouseMove: | |
if (static_cast<const QMouseEvent*>(event)->buttons() & Qt::LeftButton) | |
return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); // toggle drag select | |
default: | |
break; | |
} | |
return QItemSelectionModel::NoUpdate; | |
} | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); | |
} | |
QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand( | |
const QModelIndex &index, const QEvent *event) const | |
{ | |
Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); | |
if (event) { | |
switch (event->type()) { | |
case QEvent::MouseMove: { | |
// Toggle on MouseMove | |
modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); | |
if (modifiers & Qt::ControlModifier) | |
return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); | |
break; | |
} | |
case QEvent::MouseButtonPress: { | |
modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); | |
const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button(); | |
const bool rightButtonPressed = button & Qt::RightButton; | |
const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; | |
const bool controlKeyPressed = modifiers & Qt::ControlModifier; | |
const bool indexIsSelected = selectionModel->isSelected(index); | |
if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed) | |
return QItemSelectionModel::NoUpdate; | |
if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected) | |
return QItemSelectionModel::NoUpdate; | |
if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) | |
return QItemSelectionModel::Clear; | |
if (!index.isValid()) | |
return QItemSelectionModel::NoUpdate; | |
break; | |
} | |
case QEvent::MouseButtonRelease: { | |
// ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area | |
modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); | |
const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button(); | |
const bool rightButtonPressed = button & Qt::RightButton; | |
const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; | |
const bool controlKeyPressed = modifiers & Qt::ControlModifier; | |
if (((index == pressedIndex && selectionModel->isSelected(index)) | |
|| !index.isValid()) && state != QAbstractItemView::DragSelectingState | |
&& !shiftKeyPressed && !controlKeyPressed && (!rightButtonPressed || !index.isValid())) | |
return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); | |
return QItemSelectionModel::NoUpdate; | |
} | |
case QEvent::KeyPress: { | |
// NoUpdate on Key movement and Ctrl | |
modifiers = static_cast<const QKeyEvent*>(event)->modifiers(); | |
switch (static_cast<const QKeyEvent*>(event)->key()) { | |
case Qt::Key_Backtab: | |
modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab | |
case Qt::Key_Down: | |
case Qt::Key_Up: | |
case Qt::Key_Left: | |
case Qt::Key_Right: | |
case Qt::Key_Home: | |
case Qt::Key_End: | |
case Qt::Key_PageUp: | |
case Qt::Key_PageDown: | |
case Qt::Key_Tab: | |
if (modifiers & Qt::ControlModifier | |
#ifdef QT_KEYPAD_NAVIGATION | |
// Preserve historical tab order navigation behavior | |
|| QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder | |
#endif | |
) | |
return QItemSelectionModel::NoUpdate; | |
break; | |
case Qt::Key_Select: | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); | |
case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space | |
if (modifiers & Qt::ControlModifier) | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); | |
return QItemSelectionModel::Select|selectionBehaviorFlags(); | |
default: | |
break; | |
} | |
} | |
default: | |
break; | |
} | |
} | |
if (modifiers & Qt::ShiftModifier) | |
return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); | |
if (modifiers & Qt::ControlModifier) | |
return QItemSelectionModel::Toggle|selectionBehaviorFlags(); | |
if (state == QAbstractItemView::DragSelectingState) { | |
//when drag-selecting we need to clear any previous selection and select the current one | |
return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); | |
} | |
return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); | |
} | |
QItemSelectionModel::SelectionFlags | |
QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index, | |
const QEvent *event) const | |
{ | |
QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event); | |
const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select | |
| QItemSelectionModel::Deselect | QItemSelectionModel::Toggle | |
| QItemSelectionModel::Current; | |
switch (flags & Mask) { | |
case QItemSelectionModel::Clear: | |
case QItemSelectionModel::ClearAndSelect: | |
case QItemSelectionModel::SelectCurrent: | |
return flags; | |
case QItemSelectionModel::NoUpdate: | |
if (event && | |
(event->type() == QEvent::MouseButtonPress | |
|| event->type() == QEvent::MouseButtonRelease)) | |
return flags; | |
return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); | |
default: | |
return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); | |
} | |
} | |
void QAbstractItemViewPrivate::fetchMore() | |
{ | |
fetchMoreTimer.stop(); | |
if (!model->canFetchMore(root)) | |
return; | |
int last = model->rowCount(root) - 1; | |
if (last < 0) { | |
model->fetchMore(root); | |
return; | |
} | |
QModelIndex index = model->index(last, 0, root); | |
QRect rect = q_func()->visualRect(index); | |
if (viewport->rect().intersects(rect)) | |
model->fetchMore(root); | |
} | |
bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger, | |
const QModelIndex &index) const | |
{ | |
if (!index.isValid()) | |
return false; | |
Qt::ItemFlags flags = model->flags(index); | |
if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0)) | |
return false; | |
if (state == QAbstractItemView::EditingState) | |
return false; | |
if (hasEditor(index)) | |
return false; | |
if (trigger == QAbstractItemView::AllEditTriggers) // force editing | |
return true; | |
if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked | |
&& !selectionModel->isSelected(index)) | |
return false; | |
return (trigger & editTriggers); | |
} | |
bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger, | |
const QEvent *event) const | |
{ | |
if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed) | |
return false; | |
switch (event->type()) { | |
case QEvent::KeyPress: | |
case QEvent::MouseButtonDblClick: | |
case QEvent::MouseButtonPress: | |
case QEvent::MouseButtonRelease: | |
case QEvent::MouseMove: | |
return true; | |
default: | |
break; | |
}; | |
return false; | |
} | |
bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const | |
{ | |
if (!autoScroll) | |
return false; | |
QRect area = static_cast<QAbstractItemView*>(viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules | |
return (pos.y() - area.top() < autoScrollMargin) | |
|| (area.bottom() - pos.y() < autoScrollMargin) | |
|| (pos.x() - area.left() < autoScrollMargin) | |
|| (area.right() - pos.x() < autoScrollMargin); | |
} | |
void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay) | |
{ | |
if (!delayedPendingLayout) { | |
delayedPendingLayout = true; | |
delayedLayout.start(delay, q_func()); | |
} | |
} | |
void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const | |
{ | |
delayedLayout.stop(); | |
delayedPendingLayout = false; | |
} | |
QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, | |
const QStyleOptionViewItem &options) | |
{ | |
Q_Q(QAbstractItemView); | |
QWidget *w = editorForIndex(index).editor; | |
if (!w) { | |
QAbstractItemDelegate *delegate = delegateForIndex(index); | |
if (!delegate) | |
return 0; | |
w = delegate->createEditor(viewport, options, index); | |
if (w) { | |
w->installEventFilter(delegate); | |
QObject::connect(w, SIGNAL(destroyed(QObject*)), q, SLOT(editorDestroyed(QObject*))); | |
delegate->updateEditorGeometry(w, options, index); | |
delegate->setEditorData(w, index); | |
addEditor(index, w, false); | |
if (w->parent() == viewport) | |
QWidget::setTabOrder(q, w); | |
// Special cases for some editors containing QLineEdit | |
QWidget *focusWidget = w; | |
while (QWidget *fp = focusWidget->focusProxy()) | |
focusWidget = fp; | |
#ifndef QT_NO_LINEEDIT | |
if (QLineEdit *le = qobject_cast<QLineEdit*>(focusWidget)) | |
le->selectAll(); | |
#endif | |
#ifndef QT_NO_SPINBOX | |
if (QSpinBox *sb = qobject_cast<QSpinBox*>(focusWidget)) | |
sb->selectAll(); | |
else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(focusWidget)) | |
dsb->selectAll(); | |
#endif | |
} | |
} | |
return w; | |
} | |
void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br) | |
{ | |
// we are counting on having relatively few editors | |
const bool checkIndexes = tl.isValid() && br.isValid(); | |
const QModelIndex parent = tl.parent(); | |
QList<QEditorInfo>::const_iterator it = editors.constBegin(); | |
for (; it != editors.constEnd(); ++it) { | |
QWidget *editor = it->editor; | |
const QModelIndex index = it->index; | |
if (it->isStatic || editor == 0 || !index.isValid() || | |
(checkIndexes | |
&& (index.row() < tl.row() || index.row() > br.row() | |
|| index.column() < tl.column() || index.column() > br.column() | |
|| index.parent() != parent))) | |
continue; | |
QAbstractItemDelegate *delegate = delegateForIndex(index); | |
if (delegate) { | |
delegate->setEditorData(editor, index); | |
} | |
} | |
} | |
/*! | |
\internal | |
In DND if something has been moved then this is called. | |
Typically this means you should "remove" the selected item or row, | |
but the behavior is view dependant (table just clears the selected indexes for example). | |
Either remove the selected rows or clear them | |
*/ | |
void QAbstractItemViewPrivate::clearOrRemove() | |
{ | |
#ifndef QT_NO_DRAGANDDROP | |
const QItemSelection selection = selectionModel->selection(); | |
QList<QItemSelectionRange>::const_iterator it = selection.constBegin(); | |
if (!overwrite) { | |
for (; it != selection.constEnd(); ++it) { | |
QModelIndex parent = (*it).parent(); | |
if ((*it).left() != 0) | |
continue; | |
if ((*it).right() != (model->columnCount(parent) - 1)) | |
continue; | |
int count = (*it).bottom() - (*it).top() + 1; | |
model->removeRows((*it).top(), count, parent); | |
} | |
} else { | |
// we can't remove the rows so reset the items (i.e. the view is like a table) | |
QModelIndexList list = selection.indexes(); | |
for (int i=0; i < list.size(); ++i) { | |
QModelIndex index = list.at(i); | |
QMap<int, QVariant> roles = model->itemData(index); | |
for (QMap<int, QVariant>::Iterator it = roles.begin(); it != roles.end(); ++it) | |
it.value() = QVariant(); | |
model->setItemData(index, roles); | |
} | |
} | |
#endif | |
} | |
/*! | |
\internal | |
When persistent aeditor gets/loses focus, we need to check | |
and setcorrectly the current index. | |
*/ | |
void QAbstractItemViewPrivate::checkPersistentEditorFocus() | |
{ | |
Q_Q(QAbstractItemView); | |
if (QWidget *widget = QApplication::focusWidget()) { | |
if (persistent.contains(widget)) { | |
//a persistent editor has gained the focus | |
QModelIndex index = indexForEditor(widget); | |
if (selectionModel->currentIndex() != index) | |
q->setCurrentIndex(index); | |
} | |
} | |
} | |
QEditorInfo QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const | |
{ | |
QList<QEditorInfo>::const_iterator it = editors.constBegin(); | |
for (; it != editors.constEnd(); ++it) { | |
if (it->index == index) | |
return *it; | |
} | |
return QEditorInfo(); | |
} | |
QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const | |
{ | |
QList<QEditorInfo>::const_iterator it = editors.constBegin(); | |
for (; it != editors.constEnd(); ++it) { | |
if (it->editor == editor) | |
return it->index; | |
} | |
return QModelIndex(); | |
} | |
void QAbstractItemViewPrivate::removeEditor(QWidget *editor) | |
{ | |
QList<QEditorInfo>::iterator it = editors.begin(); | |
for (; it != editors.end(); ) { | |
if (it->editor == editor) | |
it = editors.erase(it); | |
else | |
++it; | |
} | |
} | |
void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic) | |
{ | |
editors.append(QEditorInfo(index, editor, isStatic)); | |
} | |
bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const | |
{ | |
Q_Q(const QAbstractItemView); | |
QModelIndex buddy = model->buddy(index); | |
QStyleOptionViewItemV4 options = viewOptionsV4(); | |
options.rect = q->visualRect(buddy); | |
options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); | |
QAbstractItemDelegate *delegate = delegateForIndex(index); | |
return (event && delegate && delegate->editorEvent(event, model, options, buddy)); | |
} | |
bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event) | |
{ | |
Q_Q(QAbstractItemView); | |
QModelIndex buddy = model->buddy(index); | |
QStyleOptionViewItemV4 options = viewOptionsV4(); | |
options.rect = q->visualRect(buddy); | |
options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); | |
QWidget *w = editor(buddy, options); | |
if (!w) | |
return false; | |
if (event) | |
QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); | |
q->setState(QAbstractItemView::EditingState); | |
w->show(); | |
w->setFocus(); | |
return true; | |
} | |
/* | |
\internal | |
returns the pair QRect/QModelIndex that should be painted on the viewports's rect | |
*/ | |
QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const | |
{ | |
Q_ASSERT(r); | |
Q_Q(const QAbstractItemView); | |
QRect &rect = *r; | |
const QRect viewportRect = viewport->rect(); | |
QItemViewPaintPairs ret; | |
for (int i = 0; i < indexes.count(); ++i) { | |
const QModelIndex &index = indexes.at(i); | |
const QRect current = q->visualRect(index); | |
if (current.intersects(viewportRect)) { | |
ret += qMakePair(current, index); | |
rect |= current; | |
} | |
} | |
rect &= viewportRect; | |
return ret; | |
} | |
QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const | |
{ | |
Q_ASSERT(r); | |
QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); | |
if (paintPairs.isEmpty()) | |
return QPixmap(); | |
QPixmap pixmap(r->size()); | |
pixmap.fill(Qt::transparent); | |
QPainter painter(&pixmap); | |
QStyleOptionViewItemV4 option = viewOptionsV4(); | |
option.state |= QStyle::State_Selected; | |
for (int j = 0; j < paintPairs.count(); ++j) { | |
option.rect = paintPairs.at(j).first.translated(-r->topLeft()); | |
const QModelIndex ¤t = paintPairs.at(j).second; | |
delegateForIndex(current)->paint(&painter, option, current); | |
} | |
return pixmap; | |
} | |
void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) | |
{ | |
if (!selectionModel) | |
return; | |
QItemSelection selection; | |
QModelIndex tl = model->index(0, 0, root); | |
QModelIndex br = model->index(model->rowCount(root) - 1, | |
model->columnCount(root) - 1, | |
root); | |
selection.append(QItemSelectionRange(tl, br)); | |
selectionModel->select(selection, command); | |
} | |
QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const | |
{ | |
Q_Q(const QAbstractItemView); | |
QModelIndexList indexes = q->selectedIndexes(); | |
for(int i = indexes.count() - 1 ; i >= 0; --i) { | |
if (!isIndexDragEnabled(indexes.at(i))) | |
indexes.removeAt(i); | |
} | |
return indexes; | |
} | |
QT_END_NAMESPACE | |
#include "moc_qabstractitemview.cpp" | |
#endif // QT_NO_ITEMVIEWS |