/****************************************************************************+ −
**+ −
** Copyright (C) 2009 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$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, 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.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
#include "qtreeview.h"+ −
+ −
#ifndef QT_NO_TREEVIEW+ −
#include <qheaderview.h>+ −
#include <qitemdelegate.h>+ −
#include <qapplication.h>+ −
#include <qscrollbar.h>+ −
#include <qpainter.h>+ −
#include <qstack.h>+ −
#include <qstyle.h>+ −
#include <qstyleoption.h>+ −
#include <qevent.h>+ −
#include <qpen.h>+ −
#include <qdebug.h>+ −
#ifndef QT_NO_ACCESSIBILITY+ −
#include <qaccessible.h>+ −
#endif+ −
+ −
#include <private/qtreeview_p.h>+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
/*!+ −
\class QTreeView+ −
\brief The QTreeView class provides a default model/view implementation of a tree view.+ −
+ −
\ingroup model-view+ −
\ingroup advanced+ −
+ −
+ −
A QTreeView implements a tree representation of items from a+ −
model. This class is used to provide standard hierarchical lists that+ −
were previously provided by the \c QListView class, but using the more+ −
flexible approach provided by Qt's model/view architecture.+ −
+ −
The QTreeView class is one of the \l{Model/View Classes} and is part of+ −
Qt's \l{Model/View Programming}{model/view framework}.+ −
+ −
QTreeView implements the interfaces defined by the+ −
QAbstractItemView class to allow it to display data provided by+ −
models derived from the QAbstractItemModel class.+ −
+ −
It is simple to construct a tree view displaying data from a+ −
model. In the following example, the contents of a directory are+ −
supplied by a QDirModel and displayed as a tree:+ −
+ −
\snippet doc/src/snippets/shareddirmodel/main.cpp 3+ −
\snippet doc/src/snippets/shareddirmodel/main.cpp 6+ −
+ −
The model/view architecture ensures that the contents of the tree view+ −
are updated as the model changes.+ −
+ −
Items that have children can be in an expanded (children are+ −
visible) or collapsed (children are hidden) state. When this state+ −
changes a collapsed() or expanded() signal is emitted with the+ −
model index of the relevant item.+ −
+ −
The amount of indentation used to indicate levels of hierarchy is+ −
controlled by the \l indentation property.+ −
+ −
Headers in tree views are constructed using the QHeaderView class and can+ −
be hidden using \c{header()->hide()}. Note that each header is configured+ −
with its \l{QHeaderView::}{stretchLastSection} property set to true,+ −
ensuring that the view does not waste any of the space assigned to it for+ −
its header. If this value is set to true, this property will override the+ −
resize mode set on the last section in the header.+ −
+ −
+ −
\section1 Key Bindings+ −
+ −
QTreeView supports a set of key bindings that enable the user to+ −
navigate in the view and interact with the contents of items:+ −
+ −
\table+ −
\header \o Key \o Action+ −
\row \o Up \o Moves the cursor to the item in the same column on+ −
the previous row. If the parent of the current item has no more rows to+ −
navigate to, the cursor moves to the relevant item in the last row+ −
of the sibling that precedes the parent.+ −
\row \o Down \o Moves the cursor to the item in the same column on+ −
the next row. If the parent of the current item has no more rows to+ −
navigate to, the cursor moves to the relevant item in the first row+ −
of the sibling that follows the parent.+ −
\row \o Left \o Hides the children of the current item (if present)+ −
by collapsing a branch.+ −
\row \o Minus \o Same as LeftArrow.+ −
\row \o Right \o Reveals the children of the current item (if present)+ −
by expanding a branch.+ −
\row \o Plus \o Same as RightArrow.+ −
\row \o Asterisk \o Expands all children of the current item (if present).+ −
\row \o PageUp \o Moves the cursor up one page.+ −
\row \o PageDown \o Moves the cursor down one page.+ −
\row \o Home \o Moves the cursor to an item in the same column of the first+ −
row of the first top-level item in the model.+ −
\row \o End \o Moves the cursor to an item in the same column of the last+ −
row of the last top-level item in the model.+ −
\row \o F2 \o In editable models, this opens the current item for editing.+ −
The Escape key can be used to cancel the editing process and revert+ −
any changes to the data displayed.+ −
\endtable+ −
+ −
\omit+ −
Describe the expanding/collapsing concept if not covered elsewhere.+ −
\endomit+ −
+ −
\table 100%+ −
\row \o \inlineimage windowsxp-treeview.png Screenshot of a Windows XP style tree view+ −
\o \inlineimage macintosh-treeview.png Screenshot of a Macintosh style tree view+ −
\o \inlineimage plastique-treeview.png Screenshot of a Plastique style tree view+ −
\row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} tree view.+ −
\o A \l{Macintosh Style Widget Gallery}{Macintosh style} tree view.+ −
\o A \l{Plastique Style Widget Gallery}{Plastique style} tree view.+ −
\endtable+ −
+ −
\section1 Improving Performance+ −
+ −
It is possible to give the view hints about the data it is handling in order+ −
to improve its performance when displaying large numbers of items. One approach+ −
that can be taken for views that are intended to display items with equal heights+ −
is to set the \l uniformRowHeights property to true.+ −
+ −
\sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,+ −
{Dir View Example}+ −
*/+ −
+ −
+ −
/*!+ −
\fn void QTreeView::expanded(const QModelIndex &index)+ −
+ −
This signal is emitted when the item specified by \a index is expanded.+ −
*/+ −
+ −
+ −
/*!+ −
\fn void QTreeView::collapsed(const QModelIndex &index)+ −
+ −
This signal is emitted when the item specified by \a index is collapsed.+ −
*/+ −
+ −
/*!+ −
Constructs a table view with a \a parent to represent a model's+ −
data. Use setModel() to set the model.+ −
+ −
\sa QAbstractItemModel+ −
*/+ −
QTreeView::QTreeView(QWidget *parent)+ −
: QAbstractItemView(*new QTreeViewPrivate, parent)+ −
{+ −
Q_D(QTreeView);+ −
d->initialize();+ −
}+ −
+ −
/*!+ −
\internal+ −
*/+ −
QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)+ −
: QAbstractItemView(dd, parent)+ −
{+ −
Q_D(QTreeView);+ −
d->initialize();+ −
}+ −
+ −
/*!+ −
Destroys the tree view.+ −
*/+ −
QTreeView::~QTreeView()+ −
{+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::setModel(QAbstractItemModel *model)+ −
{+ −
Q_D(QTreeView);+ −
if (model == d->model)+ −
return;+ −
if (d->selectionModel) { // support row editing+ −
disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),+ −
d->model, SLOT(submit()));+ −
disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),+ −
this, SLOT(rowsRemoved(QModelIndex,int,int)));+ −
disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));+ −
}+ −
d->viewItems.clear();+ −
d->expandedIndexes.clear();+ −
d->hiddenIndexes.clear();+ −
d->header->setModel(model);+ −
QAbstractItemView::setModel(model);+ −
+ −
// QAbstractItemView connects to a private slot+ −
disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),+ −
this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));+ −
// do header layout after the tree+ −
disconnect(d->model, SIGNAL(layoutChanged()),+ −
d->header, SLOT(_q_layoutChanged()));+ −
// QTreeView has a public slot for this+ −
connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),+ −
this, SLOT(rowsRemoved(QModelIndex,int,int)));+ −
+ −
connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset()));+ −
+ −
if (d->sortingEnabled)+ −
d->_q_sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder());+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::setRootIndex(const QModelIndex &index)+ −
{+ −
Q_D(QTreeView);+ −
d->header->setRootIndex(index);+ −
QAbstractItemView::setRootIndex(index);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel)+ −
{+ −
Q_D(QTreeView);+ −
Q_ASSERT(selectionModel);+ −
if (d->selectionModel) {+ −
// support row editing+ −
disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),+ −
d->model, SLOT(submit()));+ −
}+ −
+ −
d->header->setSelectionModel(selectionModel);+ −
QAbstractItemView::setSelectionModel(selectionModel);+ −
+ −
if (d->selectionModel) {+ −
// support row editing+ −
connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),+ −
d->model, SLOT(submit()));+ −
}+ −
}+ −
+ −
/*!+ −
Returns the header for the tree view.+ −
+ −
\sa QAbstractItemModel::headerData()+ −
*/+ −
QHeaderView *QTreeView::header() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header;+ −
}+ −
+ −
/*!+ −
Sets the header for the tree view, to the given \a header.+ −
+ −
The view takes ownership over the given \a header and deletes it+ −
when a new header is set.+ −
+ −
\sa QAbstractItemModel::headerData()+ −
*/+ −
void QTreeView::setHeader(QHeaderView *header)+ −
{+ −
Q_D(QTreeView);+ −
if (header == d->header || !header)+ −
return;+ −
if (d->header && d->header->parent() == this)+ −
delete d->header;+ −
d->header = header;+ −
d->header->setParent(this);+ −
+ −
if (!d->header->model()) {+ −
d->header->setModel(d->model);+ −
if (d->selectionModel)+ −
d->header->setSelectionModel(d->selectionModel);+ −
}+ −
+ −
connect(d->header, SIGNAL(sectionResized(int,int,int)),+ −
this, SLOT(columnResized(int,int,int)));+ −
connect(d->header, SIGNAL(sectionMoved(int,int,int)),+ −
this, SLOT(columnMoved()));+ −
connect(d->header, SIGNAL(sectionCountChanged(int,int)),+ −
this, SLOT(columnCountChanged(int,int)));+ −
connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),+ −
this, SLOT(resizeColumnToContents(int)));+ −
connect(d->header, SIGNAL(geometriesChanged()),+ −
this, SLOT(updateGeometries()));+ −
+ −
setSortingEnabled(d->sortingEnabled);+ −
}+ −
+ −
/*!+ −
\property QTreeView::autoExpandDelay+ −
\brief The delay time before items in a tree are opened during a drag and drop operation.+ −
\since 4.3+ −
+ −
This property holds the amount of time in milliseconds that the user must wait over+ −
a node before that node will automatically open or close. If the time is+ −
set to less then 0 then it will not be activated.+ −
+ −
By default, this property has a value of -1, meaning that auto-expansion is disabled.+ −
*/+ −
int QTreeView::autoExpandDelay() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->autoExpandDelay;+ −
}+ −
+ −
void QTreeView::setAutoExpandDelay(int delay)+ −
{+ −
Q_D(QTreeView);+ −
d->autoExpandDelay = delay;+ −
}+ −
+ −
/*!+ −
\property QTreeView::indentation+ −
\brief indentation of the items in the tree view.+ −
+ −
This property holds the indentation measured in pixels of the items for each+ −
level in the tree view. For top-level items, the indentation specifies the+ −
horizontal distance from the viewport edge to the items in the first column;+ −
for child items, it specifies their indentation from their parent items.+ −
+ −
By default, this property has a value of 20.+ −
*/+ −
int QTreeView::indentation() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->indent;+ −
}+ −
+ −
void QTreeView::setIndentation(int i)+ −
{+ −
Q_D(QTreeView);+ −
if (i != d->indent) {+ −
d->indent = i;+ −
d->viewport->update();+ −
}+ −
}+ −
+ −
/*!+ −
\property QTreeView::rootIsDecorated+ −
\brief whether to show controls for expanding and collapsing top-level items+ −
+ −
Items with children are typically shown with controls to expand and collapse+ −
them, allowing their children to be shown or hidden. If this property is+ −
false, these controls are not shown for top-level items. This can be used to+ −
make a single level tree structure appear like a simple list of items.+ −
+ −
By default, this property is true.+ −
*/+ −
bool QTreeView::rootIsDecorated() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->rootDecoration;+ −
}+ −
+ −
void QTreeView::setRootIsDecorated(bool show)+ −
{+ −
Q_D(QTreeView);+ −
if (show != d->rootDecoration) {+ −
d->rootDecoration = show;+ −
d->viewport->update();+ −
}+ −
}+ −
+ −
/*!+ −
\property QTreeView::uniformRowHeights+ −
\brief whether all items in the treeview have the same height+ −
+ −
This property should only be set to true if it is guaranteed that all items+ −
in the view has the same height. This enables the view to do some+ −
optimizations.+ −
+ −
The height is obtained from the first item in the view. It is updated+ −
when the data changes on that item.+ −
+ −
By default, this property is false.+ −
*/+ −
bool QTreeView::uniformRowHeights() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->uniformRowHeights;+ −
}+ −
+ −
void QTreeView::setUniformRowHeights(bool uniform)+ −
{+ −
Q_D(QTreeView);+ −
d->uniformRowHeights = uniform;+ −
}+ −
+ −
/*!+ −
\property QTreeView::itemsExpandable+ −
\brief whether the items are expandable by the user.+ −
+ −
This property holds whether the user can expand and collapse items+ −
interactively.+ −
+ −
By default, this property is true.+ −
+ −
*/+ −
bool QTreeView::itemsExpandable() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->itemsExpandable;+ −
}+ −
+ −
void QTreeView::setItemsExpandable(bool enable)+ −
{+ −
Q_D(QTreeView);+ −
d->itemsExpandable = enable;+ −
}+ −
+ −
/*!+ −
\property QTreeView::expandsOnDoubleClick+ −
\since 4.4+ −
\brief whether the items can be expanded by double-clicking.+ −
+ −
This property holds whether the user can expand and collapse items+ −
by double-clicking. The default value is true.+ −
+ −
\sa itemsExpandable+ −
*/+ −
bool QTreeView::expandsOnDoubleClick() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->expandsOnDoubleClick;+ −
}+ −
+ −
void QTreeView::setExpandsOnDoubleClick(bool enable)+ −
{+ −
Q_D(QTreeView);+ −
d->expandsOnDoubleClick = enable;+ −
}+ −
+ −
/*!+ −
Returns the horizontal position of the \a column in the viewport.+ −
*/+ −
int QTreeView::columnViewportPosition(int column) const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->sectionViewportPosition(column);+ −
}+ −
+ −
/*!+ −
Returns the width of the \a column.+ −
+ −
\sa resizeColumnToContents(), setColumnWidth()+ −
*/+ −
int QTreeView::columnWidth(int column) const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->sectionSize(column);+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
+ −
Sets the width of the given \a column to the \a width specified.+ −
+ −
\sa columnWidth(), resizeColumnToContents()+ −
*/+ −
void QTreeView::setColumnWidth(int column, int width)+ −
{+ −
Q_D(QTreeView);+ −
d->header->resizeSection(column, width);+ −
}+ −
+ −
/*!+ −
Returns the column in the tree view whose header covers the \a x+ −
coordinate given.+ −
*/+ −
int QTreeView::columnAt(int x) const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->logicalIndexAt(x);+ −
}+ −
+ −
/*!+ −
Returns true if the \a column is hidden; otherwise returns false.+ −
+ −
\sa hideColumn(), isRowHidden()+ −
*/+ −
bool QTreeView::isColumnHidden(int column) const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->isSectionHidden(column);+ −
}+ −
+ −
/*!+ −
If \a hide is true the \a column is hidden, otherwise the \a column is shown.+ −
+ −
\sa hideColumn(), setRowHidden()+ −
*/+ −
void QTreeView::setColumnHidden(int column, bool hide)+ −
{+ −
Q_D(QTreeView);+ −
if (column < 0 || column >= d->header->count())+ −
return;+ −
d->header->setSectionHidden(column, hide);+ −
}+ −
+ −
/*!+ −
\property QTreeView::headerHidden+ −
\brief whether the header is shown or not.+ −
\since 4.4+ −
+ −
If this property is true, the header is not shown otherwise it is.+ −
The default value is false.+ −
+ −
\sa header()+ −
*/+ −
bool QTreeView::isHeaderHidden() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->isHidden();+ −
}+ −
+ −
void QTreeView::setHeaderHidden(bool hide)+ −
{+ −
Q_D(QTreeView);+ −
d->header->setHidden(hide);+ −
}+ −
+ −
/*!+ −
Returns true if the item in the given \a row of the \a parent is hidden;+ −
otherwise returns false.+ −
+ −
\sa setRowHidden(), isColumnHidden()+ −
*/+ −
bool QTreeView::isRowHidden(int row, const QModelIndex &parent) const+ −
{+ −
Q_D(const QTreeView);+ −
if (!d->model)+ −
return false;+ −
return d->isRowHidden(d->model->index(row, 0, parent));+ −
}+ −
+ −
/*!+ −
If \a hide is true the \a row with the given \a parent is hidden, otherwise the \a row is shown.+ −
+ −
\sa isRowHidden(), setColumnHidden()+ −
*/+ −
void QTreeView::setRowHidden(int row, const QModelIndex &parent, bool hide)+ −
{+ −
Q_D(QTreeView);+ −
if (!d->model)+ −
return;+ −
QModelIndex index = d->model->index(row, 0, parent);+ −
if (!index.isValid())+ −
return;+ −
+ −
if (hide) {+ −
d->hiddenIndexes.insert(index);+ −
} else if(d->isPersistent(index)) { //if the index is not persistent, it cannot be in the set+ −
d->hiddenIndexes.remove(index);+ −
}+ −
+ −
d->doDelayedItemsLayout();+ −
}+ −
+ −
/*!+ −
\since 4.3+ −
+ −
Returns true if the item in first column in the given \a row+ −
of the \a parent is spanning all the columns; otherwise returns false.+ −
+ −
\sa setFirstColumnSpanned()+ −
*/+ −
bool QTreeView::isFirstColumnSpanned(int row, const QModelIndex &parent) const+ −
{+ −
Q_D(const QTreeView);+ −
if (d->spanningIndexes.isEmpty() || !d->model)+ −
return false;+ −
QModelIndex index = d->model->index(row, 0, parent);+ −
for (int i = 0; i < d->spanningIndexes.count(); ++i)+ −
if (d->spanningIndexes.at(i) == index)+ −
return true;+ −
return false;+ −
}+ −
+ −
/*!+ −
\since 4.3+ −
+ −
If \a span is true the item in the first column in the \a row+ −
with the given \a parent is set to span all columns, otherwise all items+ −
on the \a row are shown.+ −
+ −
\sa isFirstColumnSpanned()+ −
*/+ −
void QTreeView::setFirstColumnSpanned(int row, const QModelIndex &parent, bool span)+ −
{+ −
Q_D(QTreeView);+ −
if (!d->model)+ −
return;+ −
QModelIndex index = d->model->index(row, 0, parent);+ −
if (!index.isValid())+ −
return;+ −
+ −
if (span) {+ −
QPersistentModelIndex persistent(index);+ −
if (!d->spanningIndexes.contains(persistent))+ −
d->spanningIndexes.append(persistent);+ −
} else {+ −
QPersistentModelIndex persistent(index);+ −
int i = d->spanningIndexes.indexOf(persistent);+ −
if (i >= 0)+ −
d->spanningIndexes.remove(i);+ −
}+ −
+ −
d->executePostedLayout();+ −
int i = d->viewIndex(index);+ −
if (i >= 0)+ −
d->viewItems[i].spanning = span;+ −
+ −
d->viewport->update();+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)+ −
{+ −
Q_D(QTreeView);+ −
+ −
// if we are going to do a complete relayout anyway, there is no need to update+ −
if (d->delayedPendingLayout)+ −
return;+ −
+ −
// refresh the height cache here; we don't really lose anything by getting the size hint,+ −
// since QAbstractItemView::dataChanged() will get the visualRect for the items anyway+ −
+ −
int topViewIndex = d->viewIndex(topLeft);+ −
if (topViewIndex == 0)+ −
d->defaultItemHeight = indexRowSizeHint(topLeft);+ −
bool sizeChanged = false;+ −
if (topViewIndex != -1) {+ −
if (topLeft == bottomRight) {+ −
int oldHeight = d->itemHeight(topViewIndex);+ −
d->invalidateHeightCache(topViewIndex);+ −
sizeChanged = (oldHeight != d->itemHeight(topViewIndex));+ −
} else {+ −
int bottomViewIndex = d->viewIndex(bottomRight);+ −
for (int i = topViewIndex; i <= bottomViewIndex; ++i) {+ −
int oldHeight = d->itemHeight(i);+ −
d->invalidateHeightCache(i);+ −
sizeChanged |= (oldHeight != d->itemHeight(i));+ −
}+ −
}+ −
}+ −
+ −
if (sizeChanged) {+ −
d->updateScrollBars();+ −
d->viewport->update();+ −
}+ −
QAbstractItemView::dataChanged(topLeft, bottomRight);+ −
}+ −
+ −
/*!+ −
Hides the \a column given.+ −
+ −
\note This function should only be called after the model has been+ −
initialized, as the view needs to know the number of columns in order to+ −
hide \a column.+ −
+ −
\sa showColumn(), setColumnHidden()+ −
*/+ −
void QTreeView::hideColumn(int column)+ −
{+ −
Q_D(QTreeView);+ −
d->header->hideSection(column);+ −
}+ −
+ −
/*!+ −
Shows the given \a column in the tree view.+ −
+ −
\sa hideColumn(), setColumnHidden()+ −
*/+ −
void QTreeView::showColumn(int column)+ −
{+ −
Q_D(QTreeView);+ −
d->header->showSection(column);+ −
}+ −
+ −
/*!+ −
\fn void QTreeView::expand(const QModelIndex &index)+ −
+ −
Expands the model item specified by the \a index.+ −
+ −
\sa expanded()+ −
*/+ −
void QTreeView::expand(const QModelIndex &index)+ −
{+ −
Q_D(QTreeView);+ −
if (!d->isIndexValid(index))+ −
return;+ −
if (d->delayedPendingLayout) {+ −
//A complete relayout is going to be performed, just store the expanded index, no need to layout.+ −
if (d->storeExpanded(index))+ −
emit expanded(index);+ −
return;+ −
}+ −
+ −
int i = d->viewIndex(index);+ −
if (i != -1) { // is visible+ −
d->expand(i, true);+ −
if (!d->isAnimating()) {+ −
updateGeometries();+ −
d->viewport->update();+ −
}+ −
} else if (d->storeExpanded(index)) {+ −
emit expanded(index);+ −
}+ −
}+ −
+ −
/*!+ −
\fn void QTreeView::collapse(const QModelIndex &index)+ −
+ −
Collapses the model item specified by the \a index.+ −
+ −
\sa collapsed()+ −
*/+ −
void QTreeView::collapse(const QModelIndex &index)+ −
{+ −
Q_D(QTreeView);+ −
if (!d->isIndexValid(index))+ −
return;+ −
//if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll+ −
d->delayedAutoScroll.stop();+ −
+ −
if (d->delayedPendingLayout) {+ −
//A complete relayout is going to be performed, just un-store the expanded index, no need to layout.+ −
if (d->isPersistent(index) && d->expandedIndexes.remove(index))+ −
emit collapsed(index);+ −
return;+ −
}+ −
int i = d->viewIndex(index);+ −
if (i != -1) { // is visible+ −
d->collapse(i, true);+ −
if (!d->isAnimating()) {+ −
updateGeometries();+ −
viewport()->update();+ −
}+ −
} else {+ −
if (d->isPersistent(index) && d->expandedIndexes.remove(index))+ −
emit collapsed(index);+ −
}+ −
}+ −
+ −
/*!+ −
\fn bool QTreeView::isExpanded(const QModelIndex &index) const+ −
+ −
Returns true if the model item \a index is expanded; otherwise returns+ −
false.+ −
+ −
\sa expand(), expanded(), setExpanded()+ −
*/+ −
bool QTreeView::isExpanded(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
return d->isIndexExpanded(index);+ −
}+ −
+ −
/*!+ −
Sets the item referred to by \a index to either collapse or expanded,+ −
depending on the value of \a expanded.+ −
+ −
\sa expanded(), expand(), isExpanded()+ −
*/+ −
void QTreeView::setExpanded(const QModelIndex &index, bool expanded)+ −
{+ −
if (expanded)+ −
this->expand(index);+ −
else+ −
this->collapse(index);+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
\property QTreeView::sortingEnabled+ −
\brief whether sorting is enabled+ −
+ −
If this property is true, sorting is enabled for the tree; if the property+ −
is false, sorting is not enabled. The default value is false.+ −
+ −
\note In order to avoid performance issues, it is recommended that+ −
sorting is enabled \e after inserting the items into the tree.+ −
Alternatively, you could also insert the items into a list before inserting+ −
the items into the tree.+ −
+ −
\sa sortByColumn()+ −
*/+ −
+ −
void QTreeView::setSortingEnabled(bool enable)+ −
{+ −
Q_D(QTreeView);+ −
header()->setSortIndicatorShown(enable);+ −
header()->setClickable(enable);+ −
if (enable) {+ −
//sortByColumn has to be called before we connect or set the sortingEnabled flag+ −
// because otherwise it will not call sort on the model.+ −
sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());+ −
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),+ −
this, SLOT(_q_sortIndicatorChanged(int, Qt::SortOrder)), Qt::UniqueConnection);+ −
} else {+ −
disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),+ −
this, SLOT(_q_sortIndicatorChanged(int, Qt::SortOrder)));+ −
}+ −
d->sortingEnabled = enable;+ −
}+ −
+ −
bool QTreeView::isSortingEnabled() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->sortingEnabled;+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
\property QTreeView::animated+ −
\brief whether animations are enabled+ −
+ −
If this property is true the treeview will animate expandsion+ −
and collasping of branches. If this property is false, the treeview+ −
will expand or collapse branches immediately without showing+ −
the animation.+ −
+ −
By default, this property is false.+ −
*/+ −
+ −
void QTreeView::setAnimated(bool animate)+ −
{+ −
Q_D(QTreeView);+ −
d->animationsEnabled = animate;+ −
}+ −
+ −
bool QTreeView::isAnimated() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->animationsEnabled;+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
\property QTreeView::allColumnsShowFocus+ −
\brief whether items should show keyboard focus using all columns+ −
+ −
If this property is true all columns will show focus, otherwise only+ −
one column will show focus.+ −
+ −
The default is false.+ −
*/+ −
+ −
void QTreeView::setAllColumnsShowFocus(bool enable)+ −
{+ −
Q_D(QTreeView);+ −
if (d->allColumnsShowFocus == enable)+ −
return;+ −
d->allColumnsShowFocus = enable;+ −
d->viewport->update();+ −
}+ −
+ −
bool QTreeView::allColumnsShowFocus() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->allColumnsShowFocus;+ −
}+ −
+ −
/*!+ −
\property QTreeView::wordWrap+ −
\brief the item text word-wrapping policy+ −
\since 4.3+ −
+ −
If this property is true then the item text is wrapped where+ −
necessary at word-breaks; otherwise it is not wrapped at all.+ −
This property is false by default.+ −
+ −
Note that even if wrapping is enabled, the cell will not be+ −
expanded to fit all text. Ellipsis will be inserted according to+ −
the current \l{QAbstractItemView::}{textElideMode}.+ −
*/+ −
void QTreeView::setWordWrap(bool on)+ −
{+ −
Q_D(QTreeView);+ −
if (d->wrapItemText == on)+ −
return;+ −
d->wrapItemText = on;+ −
d->doDelayedItemsLayout();+ −
}+ −
+ −
bool QTreeView::wordWrap() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->wrapItemText;+ −
}+ −
+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::keyboardSearch(const QString &search)+ −
{+ −
Q_D(QTreeView);+ −
if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))+ −
return;+ −
+ −
QModelIndex start;+ −
if (currentIndex().isValid())+ −
start = currentIndex();+ −
else+ −
start = d->model->index(0, 0, d->root);+ −
+ −
QTime now(QTime::currentTime());+ −
bool skipRow = false;+ −
if (search.isEmpty()+ −
|| (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) {+ −
d->keyboardInput = search;+ −
skipRow = true;+ −
} else {+ −
d->keyboardInput += search;+ −
}+ −
d->keyboardInputTime = now;+ −
+ −
// 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) {+ −
if (indexBelow(start).isValid())+ −
start = indexBelow(start);+ −
else+ −
start = d->model->index(0, start.column(), d->root);+ −
}+ −
+ −
d->executePostedLayout();+ −
int startIndex = d->viewIndex(start);+ −
if (startIndex <= -1)+ −
return;+ −
+ −
int previousLevel = -1;+ −
int bestAbove = -1;+ −
int bestBelow = -1;+ −
QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;+ −
for (int i = 0; i < d->viewItems.count(); ++i) {+ −
if ((int)d->viewItems.at(i).level > previousLevel) {+ −
QModelIndex searchFrom = d->viewItems.at(i).index;+ −
if (searchFrom.parent() == start.parent())+ −
searchFrom = start;+ −
QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);+ −
if (match.count()) {+ −
int hitIndex = d->viewIndex(match.at(0));+ −
if (hitIndex >= 0 && hitIndex < startIndex)+ −
bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);+ −
else if (hitIndex >= startIndex)+ −
bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);+ −
}+ −
}+ −
previousLevel = d->viewItems.at(i).level;+ −
}+ −
+ −
QModelIndex index;+ −
if (bestBelow > -1)+ −
index = d->viewItems.at(bestBelow).index;+ −
else if (bestAbove > -1)+ −
index = d->viewItems.at(bestAbove).index;+ −
+ −
if (index.isValid()) {+ −
QItemSelectionModel::SelectionFlags flags = (d->selectionMode == SingleSelection+ −
? QItemSelectionModel::SelectionFlags(+ −
QItemSelectionModel::ClearAndSelect+ −
|d->selectionBehaviorFlags())+ −
: QItemSelectionModel::SelectionFlags(+ −
QItemSelectionModel::NoUpdate));+ −
selectionModel()->setCurrentIndex(index, flags);+ −
}+ −
}+ −
+ −
/*!+ −
Returns the rectangle on the viewport occupied by the item at \a index.+ −
If the index is not visible or explicitly hidden, the returned rectangle is invalid.+ −
*/+ −
QRect QTreeView::visualRect(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
+ −
if (!d->isIndexValid(index) || isIndexHidden(index))+ −
return QRect();+ −
+ −
d->executePostedLayout();+ −
+ −
int vi = d->viewIndex(index);+ −
if (vi < 0)+ −
return QRect();+ −
+ −
bool spanning = d->viewItems.at(vi).spanning;+ −
+ −
// if we have a spanning item, make the selection stretch from left to right+ −
int x = (spanning ? 0 : columnViewportPosition(index.column()));+ −
int w = (spanning ? d->header->length() : columnWidth(index.column()));+ −
// handle indentation+ −
if (index.column() == 0) {+ −
int i = d->indentationForItem(vi);+ −
w -= i;+ −
if (!isRightToLeft())+ −
x += i;+ −
}+ −
+ −
int y = d->coordinateForItem(vi);+ −
int h = d->itemHeight(vi);+ −
+ −
return QRect(x, y, w, h);+ −
}+ −
+ −
/*!+ −
Scroll the contents of the tree view until the given model item+ −
\a index is visible. The \a hint parameter specifies more+ −
precisely where the item should be located after the+ −
operation.+ −
If any of the parents of the model item are collapsed, they will+ −
be expanded to ensure that the model item is visible.+ −
*/+ −
void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint)+ −
{+ −
Q_D(QTreeView);+ −
+ −
if (!d->isIndexValid(index))+ −
return;+ −
+ −
d->executePostedLayout();+ −
d->updateScrollBars();+ −
+ −
// Expand all parents if the parent(s) of the node are not expanded.+ −
QModelIndex parent = index.parent();+ −
while (parent.isValid() && state() == NoState && d->itemsExpandable) {+ −
if (!isExpanded(parent))+ −
expand(parent);+ −
parent = d->model->parent(parent);+ −
}+ −
+ −
int item = d->viewIndex(index);+ −
if (item < 0)+ −
return;+ −
+ −
QRect area = d->viewport->rect();+ −
+ −
// vertical+ −
if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {+ −
int top = verticalScrollBar()->value();+ −
int bottom = top + verticalScrollBar()->pageStep();+ −
if (hint == EnsureVisible && item >= top && item < bottom) {+ −
// nothing to do+ −
} else if (hint == PositionAtTop || (hint == EnsureVisible && item < top)) {+ −
verticalScrollBar()->setValue(item);+ −
} else { // PositionAtBottom or PositionAtCenter+ −
const int currentItemHeight = d->itemHeight(item);+ −
int y = (hint == PositionAtCenter+ −
//we center on the current item with a preference to the top item (ie. -1)+ −
? area.height() / 2 + currentItemHeight - 1+ −
//otherwise we simply take the whole space+ −
: area.height());+ −
if (y > currentItemHeight) {+ −
while (item >= 0) {+ −
y -= d->itemHeight(item);+ −
if (y < 0) { //there is no more space left+ −
item++;+ −
break;+ −
}+ −
item--;+ −
}+ −
}+ −
verticalScrollBar()->setValue(item);+ −
}+ −
} else { // ScrollPerPixel+ −
QRect rect(columnViewportPosition(index.column()),+ −
d->coordinateForItem(item), // ### slow for items outside the view+ −
columnWidth(index.column()),+ −
d->itemHeight(item));+ −
+ −
if (rect.isEmpty()) {+ −
// nothing to do+ −
} else if (hint == EnsureVisible && area.contains(rect)) {+ −
d->viewport->update(rect);+ −
// nothing to do+ −
} else {+ −
bool above = (hint == EnsureVisible+ −
&& (rect.top() < area.top()+ −
|| area.height() < rect.height()));+ −
bool below = (hint == EnsureVisible+ −
&& rect.bottom() > area.bottom()+ −
&& rect.height() < area.height());+ −
+ −
int verticalValue = verticalScrollBar()->value();+ −
if (hint == PositionAtTop || above)+ −
verticalValue += rect.top();+ −
else if (hint == PositionAtBottom || below)+ −
verticalValue += rect.bottom() - area.height();+ −
else if (hint == PositionAtCenter)+ −
verticalValue += rect.top() - ((area.height() - rect.height()) / 2);+ −
verticalScrollBar()->setValue(verticalValue);+ −
}+ −
}+ −
// horizontal+ −
int viewportWidth = d->viewport->width();+ −
int horizontalOffset = d->header->offset();+ −
int horizontalPosition = d->header->sectionPosition(index.column());+ −
int cellWidth = d->header->sectionSize(index.column());+ −
+ −
if (hint == PositionAtCenter) {+ −
horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));+ −
} else {+ −
if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)+ −
horizontalScrollBar()->setValue(horizontalPosition);+ −
else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)+ −
horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);+ −
}+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::timerEvent(QTimerEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
if (event->timerId() == d->columnResizeTimerID) {+ −
updateGeometries();+ −
killTimer(d->columnResizeTimerID);+ −
d->columnResizeTimerID = 0;+ −
QRect rect;+ −
int viewportHeight = d->viewport->height();+ −
int viewportWidth = d->viewport->width();+ −
for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {+ −
int column = d->columnsToUpdate.at(i);+ −
int x = columnViewportPosition(column);+ −
if (isRightToLeft())+ −
rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);+ −
else+ −
rect |= QRect(x, 0, viewportWidth - x, viewportHeight);+ −
}+ −
d->viewport->update(rect.normalized());+ −
d->columnsToUpdate.clear();+ −
} else if (event->timerId() == d->openTimer.timerId()) {+ −
QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());+ −
if (state() == QAbstractItemView::DraggingState+ −
&& d->viewport->rect().contains(pos)) {+ −
QModelIndex index = indexAt(pos);+ −
setExpanded(index, !isExpanded(index));+ −
}+ −
d->openTimer.stop();+ −
}+ −
+ −
QAbstractItemView::timerEvent(event);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
#ifndef QT_NO_DRAGANDDROP+ −
void QTreeView::dragMoveEvent(QDragMoveEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
if (d->autoExpandDelay >= 0)+ −
d->openTimer.start(d->autoExpandDelay, this);+ −
QAbstractItemView::dragMoveEvent(event);+ −
}+ −
#endif+ −
+ −
/*!+ −
\reimp+ −
*/+ −
bool QTreeView::viewportEvent(QEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
switch (event->type()) {+ −
case QEvent::HoverEnter:+ −
case QEvent::HoverLeave:+ −
case QEvent::HoverMove: {+ −
QHoverEvent *he = static_cast<QHoverEvent*>(event);+ −
int oldBranch = d->hoverBranch;+ −
d->hoverBranch = d->itemDecorationAt(he->pos());+ −
if (oldBranch != d->hoverBranch) {+ −
QModelIndex oldIndex = d->modelIndex(oldBranch),+ −
newIndex = d->modelIndex(d->hoverBranch);+ −
if (oldIndex != newIndex) {+ −
QRect oldRect = visualRect(oldIndex);+ −
QRect newRect = visualRect(newIndex);+ −
viewport()->update(oldRect.left() - d->indent, oldRect.top(), d->indent, oldRect.height());+ −
viewport()->update(newRect.left() - d->indent, newRect.top(), d->indent, newRect.height());+ −
}+ −
}+ −
if (selectionBehavior() == QAbstractItemView::SelectRows) {+ −
QModelIndex newHoverIndex = indexAt(he->pos());+ −
if (d->hover != newHoverIndex) {+ −
QRect oldHoverRect = visualRect(d->hover);+ −
QRect newHoverRect = visualRect(newHoverIndex);+ −
viewport()->update(QRect(0, newHoverRect.y(), viewport()->width(), newHoverRect.height()));+ −
viewport()->update(QRect(0, oldHoverRect.y(), viewport()->width(), oldHoverRect.height()));+ −
}+ −
}+ −
break; }+ −
default:+ −
break;+ −
}+ −
return QAbstractItemView::viewportEvent(event);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::paintEvent(QPaintEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
d->executePostedLayout();+ −
QPainter painter(viewport());+ −
#ifndef QT_NO_ANIMATION+ −
if (d->isAnimating()) {+ −
drawTree(&painter, event->region() - d->animatedOperation.rect());+ −
d->drawAnimatedOperation(&painter);+ −
} else+ −
#endif //QT_NO_ANIMATION+ −
{+ −
drawTree(&painter, event->region());+ −
#ifndef QT_NO_DRAGANDDROP+ −
d->paintDropIndicator(&painter);+ −
#endif+ −
}+ −
}+ −
+ −
void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItemV4 *option, int y, int bottom) const+ −
{+ −
Q_Q(const QTreeView);+ −
if (!alternatingColors || !q->style()->styleHint(QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea, option, q))+ −
return;+ −
int rowHeight = defaultItemHeight;+ −
if (rowHeight <= 0) {+ −
rowHeight = itemDelegate->sizeHint(*option, QModelIndex()).height();+ −
if (rowHeight <= 0)+ −
return;+ −
}+ −
while (y <= bottom) {+ −
option->rect.setRect(0, y, viewport->width(), rowHeight);+ −
if (current & 1) {+ −
option->features |= QStyleOptionViewItemV2::Alternate;+ −
} else {+ −
option->features &= ~QStyleOptionViewItemV2::Alternate;+ −
}+ −
++current;+ −
q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, option, painter, q);+ −
y += rowHeight;+ −
}+ −
}+ −
+ −
bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos)+ −
{+ −
Q_Q(QTreeView);+ −
// we want to handle mousePress in EditingState (persistent editors)+ −
if ((state != QAbstractItemView::NoState+ −
&& state != QAbstractItemView::EditingState)+ −
|| !viewport->rect().contains(pos))+ −
return true;+ −
+ −
int i = itemDecorationAt(pos);+ −
if ((i != -1) && itemsExpandable && hasVisibleChildren(viewItems.at(i).index)) {+ −
if (viewItems.at(i).expanded)+ −
collapse(i, true);+ −
else+ −
expand(i, true);+ −
if (!isAnimating()) {+ −
q->updateGeometries();+ −
viewport->update();+ −
}+ −
return true;+ −
}+ −
return false;+ −
}+ −
+ −
void QTreeViewPrivate::_q_modelDestroyed()+ −
{+ −
//we need to clear that list because it contais QModelIndex to + −
//the model currently being destroyed+ −
viewItems.clear();+ −
QAbstractItemViewPrivate::_q_modelDestroyed();+ −
}+ −
+ −
/*!+ −
\reimp+ −
+ −
We have a QTreeView way of knowing what elements are on the viewport+ −
*/+ −
QItemViewPaintPairs QTreeViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const+ −
{+ −
Q_ASSERT(r);+ −
return QAbstractItemViewPrivate::draggablePaintPairs(indexes, r);+ −
Q_Q(const QTreeView);+ −
QRect &rect = *r;+ −
const QRect viewportRect = viewport->rect();+ −
int itemOffset = 0;+ −
int row = firstVisibleItem(&itemOffset);+ −
QPair<int, int> startEnd = startAndEndColumns(viewportRect);+ −
QVector<int> columns;+ −
for (int i = startEnd.first; i <= startEnd.second; ++i) {+ −
int logical = header->logicalIndex(i);+ −
if (!header->isSectionHidden(logical))+ −
columns += logical;+ −
}+ −
QSet<QModelIndex> visibleIndexes;+ −
for (; itemOffset < viewportRect.bottom() && row < viewItems.count(); ++row) {+ −
const QModelIndex &index = viewItems.at(row).index;+ −
for (int colIndex = 0; colIndex < columns.count(); ++colIndex)+ −
visibleIndexes += index.sibling(index.row(), columns.at(colIndex));+ −
itemOffset += itemHeight(row);+ −
}+ −
+ −
//now that we have the visible indexes, we can try to find those which are selected+ −
QItemViewPaintPairs ret;+ −
for (int i = 0; i < indexes.count(); ++i) {+ −
const QModelIndex &index = indexes.at(i);+ −
if (visibleIndexes.contains(index)) {+ −
const QRect current = q->visualRect(index);+ −
ret += qMakePair(current, index);+ −
rect |= current;+ −
}+ −
}+ −
rect &= viewportRect;+ −
return ret;+ −
}+ −
+ −
+ −
/*!+ −
\since 4.2+ −
Draws the part of the tree intersecting the given \a region using the specified+ −
\a painter.+ −
+ −
\sa paintEvent()+ −
*/+ −
void QTreeView::drawTree(QPainter *painter, const QRegion ®ion) const+ −
{+ −
Q_D(const QTreeView);+ −
const QVector<QTreeViewItem> viewItems = d->viewItems;+ −
+ −
QStyleOptionViewItemV4 option = d->viewOptionsV4();+ −
const QStyle::State state = option.state;+ −
d->current = 0;+ −
+ −
if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) {+ −
d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);+ −
return;+ −
}+ −
+ −
int firstVisibleItemOffset = 0;+ −
const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);+ −
if (firstVisibleItem < 0) {+ −
d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);+ −
return;+ −
}+ −
+ −
const int viewportWidth = d->viewport->width();+ −
+ −
QVector<QRect> rects = region.rects();+ −
QVector<int> drawn;+ −
bool multipleRects = (rects.size() > 1);+ −
for (int a = 0; a < rects.size(); ++a) {+ −
const QRect area = (multipleRects+ −
? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height())+ −
: rects.at(a));+ −
d->leftAndRight = d->startAndEndColumns(area);+ −
+ −
int i = firstVisibleItem; // the first item at the top of the viewport+ −
int y = firstVisibleItemOffset; // we may only see part of the first item+ −
+ −
// start at the top of the viewport and iterate down to the update area+ −
for (; i < viewItems.count(); ++i) {+ −
const int itemHeight = d->itemHeight(i);+ −
if (y + itemHeight > area.top())+ −
break;+ −
y += itemHeight;+ −
}+ −
+ −
// paint the visible rows+ −
for (; i < viewItems.count() && y <= area.bottom(); ++i) {+ −
const int itemHeight = d->itemHeight(i);+ −
option.rect.setRect(0, y, viewportWidth, itemHeight);+ −
option.state = state | (viewItems.at(i).expanded+ −
? QStyle::State_Open : QStyle::State_None);+ −
d->current = i;+ −
d->spanning = viewItems.at(i).spanning;+ −
if (!multipleRects || !drawn.contains(i)) {+ −
drawRow(painter, option, viewItems.at(i).index);+ −
if (multipleRects) // even if the rect only intersects the item,+ −
drawn.append(i); // the entire item will be painted+ −
}+ −
y += itemHeight;+ −
}+ −
+ −
if (y <= area.bottom()) {+ −
d->current = i;+ −
d->paintAlternatingRowColors(painter, &option, y, area.bottom());+ −
}+ −
}+ −
}+ −
+ −
/// ### move to QObject :)+ −
static inline bool ancestorOf(QObject *widget, QObject *other)+ −
{+ −
for (QObject *parent = other; parent != 0; parent = parent->parent()) {+ −
if (parent == widget)+ −
return true;+ −
}+ −
return false;+ −
}+ −
+ −
/*!+ −
Draws the row in the tree view that contains the model item \a index,+ −
using the \a painter given. The \a option control how the item is+ −
displayed.+ −
+ −
\sa setAlternatingRowColors()+ −
*/+ −
void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,+ −
const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
QStyleOptionViewItemV4 opt = option;+ −
const QPoint offset = d->scrollDelayOffset;+ −
const int y = option.rect.y() + offset.y();+ −
const QModelIndex parent = index.parent();+ −
const QHeaderView *header = d->header;+ −
const QModelIndex current = currentIndex();+ −
const QModelIndex hover = d->hover;+ −
const bool reverse = isRightToLeft();+ −
const QStyle::State state = opt.state;+ −
const bool spanning = d->spanning;+ −
const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);+ −
const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);+ −
const bool alternate = d->alternatingColors;+ −
const bool enabled = (state & QStyle::State_Enabled) != 0;+ −
const bool allColumnsShowFocus = d->allColumnsShowFocus;+ −
+ −
+ −
// when the row contains an index widget which has focus,+ −
// we want to paint the entire row as active+ −
bool indexWidgetHasFocus = false;+ −
if ((current.row() == index.row()) && !d->editors.isEmpty()) {+ −
const int r = index.row();+ −
QWidget *fw = QApplication::focusWidget();+ −
for (int c = 0; c < header->count(); ++c) {+ −
QModelIndex idx = d->model->index(r, c, parent);+ −
if (QWidget *editor = indexWidget(idx)) {+ −
if (ancestorOf(editor, fw)) {+ −
indexWidgetHasFocus = true;+ −
break;+ −
}+ −
}+ −
}+ −
}+ −
+ −
const bool widgetHasFocus = hasFocus();+ −
bool currentRowHasFocus = false;+ −
if (allColumnsShowFocus && widgetHasFocus && current.isValid()) {+ −
// check if the focus index is before or after the visible columns+ −
const int r = index.row();+ −
for (int c = 0; c < left && !currentRowHasFocus; ++c) {+ −
QModelIndex idx = d->model->index(r, c, parent);+ −
currentRowHasFocus = (idx == current);+ −
}+ −
QModelIndex parent = d->model->parent(index);+ −
for (int c = right; c < header->count() && !currentRowHasFocus; ++c) {+ −
currentRowHasFocus = (d->model->index(r, c, parent) == current);+ −
}+ −
}+ −
+ −
// ### special case: treeviews with multiple columns draw+ −
// the selections differently than with only one column+ −
opt.showDecorationSelected = (d->selectionBehavior & SelectRows)+ −
|| option.showDecorationSelected;+ −
+ −
int width, height = option.rect.height();+ −
int position;+ −
QModelIndex modelIndex;+ −
int columnCount = header->count();+ −
const bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows+ −
&& index.parent() == hover.parent()+ −
&& index.row() == hover.row();+ −
+ −
/* 'left' and 'right' are the left-most and right-most visible visual indices.+ −
Compute the first visible logical indices before and after the left and right.+ −
We will use these values to determine the QStyleOptionViewItemV4::viewItemPosition. */+ −
int logicalIndexBeforeLeft = -1, logicalIndexAfterRight = -1;+ −
for (int visualIndex = left - 1; visualIndex >= 0; --visualIndex) {+ −
int logicalIndex = header->logicalIndex(visualIndex);+ −
if (!header->isSectionHidden(logicalIndex)) {+ −
logicalIndexBeforeLeft = logicalIndex;+ −
break;+ −
}+ −
}+ −
QVector<int> logicalIndices; // vector of currently visibly logical indices+ −
for (int visualIndex = left; visualIndex < columnCount; ++visualIndex) {+ −
int logicalIndex = header->logicalIndex(visualIndex);+ −
if (!header->isSectionHidden(logicalIndex)) {+ −
if (visualIndex > right) {+ −
logicalIndexAfterRight = logicalIndex;+ −
break;+ −
}+ −
logicalIndices.append(logicalIndex);+ −
}+ −
}+ −
+ −
for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection) {+ −
int headerSection = logicalIndices.at(currentLogicalSection);+ −
position = columnViewportPosition(headerSection) + offset.x();+ −
width = header->sectionSize(headerSection);+ −
+ −
if (spanning) {+ −
int lastSection = header->logicalIndex(header->count() - 1);+ −
if (!reverse) {+ −
width = columnViewportPosition(lastSection) + header->sectionSize(lastSection) - position;+ −
} else {+ −
width += position - columnViewportPosition(lastSection);+ −
position = columnViewportPosition(lastSection);+ −
}+ −
}+ −
+ −
modelIndex = d->model->index(index.row(), headerSection, parent);+ −
if (!modelIndex.isValid())+ −
continue;+ −
opt.state = state;+ −
+ −
// determine the viewItemPosition depending on the position of column 0+ −
int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices.count()+ −
? logicalIndexAfterRight+ −
: logicalIndices.at(currentLogicalSection + 1);+ −
int prevLogicalSection = currentLogicalSection - 1 < 0+ −
? logicalIndexBeforeLeft+ −
: logicalIndices.at(currentLogicalSection - 1);+ −
if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1)+ −
|| (headerSection == 0 && nextLogicalSection == -1) || spanning)+ −
opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;+ −
else if (headerSection == 0 || (nextLogicalSection != 0 && prevLogicalSection == -1))+ −
opt.viewItemPosition = QStyleOptionViewItemV4::Beginning;+ −
else if (nextLogicalSection == 0 || nextLogicalSection == -1)+ −
opt.viewItemPosition = QStyleOptionViewItemV4::End;+ −
else+ −
opt.viewItemPosition = QStyleOptionViewItemV4::Middle;+ −
+ −
// fake activeness when row editor has focus+ −
if (indexWidgetHasFocus)+ −
opt.state |= QStyle::State_Active;+ −
+ −
if (d->selectionModel->isSelected(modelIndex))+ −
opt.state |= QStyle::State_Selected;+ −
if (widgetHasFocus && (current == modelIndex)) {+ −
if (allColumnsShowFocus)+ −
currentRowHasFocus = true;+ −
else+ −
opt.state |= QStyle::State_HasFocus;+ −
}+ −
if ((hoverRow || modelIndex == hover)+ −
&& (option.showDecorationSelected || (d->hoverBranch == -1)))+ −
opt.state |= QStyle::State_MouseOver;+ −
else+ −
opt.state &= ~QStyle::State_MouseOver;+ −
+ −
if (enabled) {+ −
QPalette::ColorGroup cg;+ −
if ((d->model->flags(modelIndex) & Qt::ItemIsEnabled) == 0) {+ −
opt.state &= ~QStyle::State_Enabled;+ −
cg = QPalette::Disabled;+ −
} else if (opt.state & QStyle::State_Active) {+ −
cg = QPalette::Active;+ −
} else {+ −
cg = QPalette::Inactive;+ −
}+ −
opt.palette.setCurrentColorGroup(cg);+ −
}+ −
+ −
if (alternate) {+ −
if (d->current & 1) {+ −
opt.features |= QStyleOptionViewItemV2::Alternate;+ −
} else {+ −
opt.features &= ~QStyleOptionViewItemV2::Alternate;+ −
}+ −
}+ −
+ −
/* Prior to Qt 4.3, the background of the branch (in selected state and+ −
alternate row color was provided by the view. For backward compatibility,+ −
this is now delegated to the style using PE_PanelViewItemRow which+ −
does the appropriate fill */+ −
if (headerSection == 0) {+ −
const int i = d->indentationForItem(d->current);+ −
QRect branches(reverse ? position + width - i : position, y, i, height);+ −
const bool setClipRect = branches.width() > width;+ −
if (setClipRect) {+ −
painter->save();+ −
painter->setClipRect(QRect(position, y, width, height));+ −
}+ −
// draw background for the branch (selection + alternate row)+ −
opt.rect = branches;+ −
style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);+ −
+ −
// draw background of the item (only alternate row). rest of the background+ −
// is provided by the delegate+ −
QStyle::State oldState = opt.state;+ −
opt.state &= ~QStyle::State_Selected;+ −
opt.rect.setRect(reverse ? position : i + position, y, width - i, height);+ −
style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);+ −
opt.state = oldState;+ −
+ −
drawBranches(painter, branches, index);+ −
if (setClipRect)+ −
painter->restore();+ −
} else {+ −
QStyle::State oldState = opt.state;+ −
opt.state &= ~QStyle::State_Selected;+ −
opt.rect.setRect(position, y, width, height);+ −
style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);+ −
opt.state = oldState;+ −
}+ −
+ −
if (const QWidget *widget = d->editorForIndex(modelIndex).editor) {+ −
painter->save();+ −
painter->setClipRect(widget->geometry());+ −
d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);+ −
painter->restore();+ −
} else {+ −
d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);+ −
}+ −
}+ −
+ −
if (currentRowHasFocus) {+ −
QStyleOptionFocusRect o;+ −
o.QStyleOption::operator=(option);+ −
o.state |= QStyle::State_KeyboardFocusChange;+ −
QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)+ −
? QPalette::Normal : QPalette::Disabled;+ −
o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)+ −
? QPalette::Highlight : QPalette::Background);+ −
int x = 0;+ −
if (!option.showDecorationSelected)+ −
x = header->sectionPosition(0) + d->indentationForItem(d->current);+ −
QRect focusRect(x - header->offset(), y, header->length() - x, height);+ −
o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), focusRect);+ −
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);+ −
// if we show focus on all columns and the first section is moved,+ −
// we have to split the focus rect into two rects+ −
if (allColumnsShowFocus && !option.showDecorationSelected+ −
&& header->sectionsMoved() && (header->visualIndex(0) != 0)) {+ −
QRect sectionRect(0, y, header->sectionPosition(0), height); + −
o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), sectionRect);+ −
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);+ −
}+ −
}+ −
}+ −
+ −
/*!+ −
Draws the branches in the tree view on the same row as the model item+ −
\a index, using the \a painter given. The branches are drawn in the+ −
rectangle specified by \a rect.+ −
*/+ −
void QTreeView::drawBranches(QPainter *painter, const QRect &rect,+ −
const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
const bool reverse = isRightToLeft();+ −
const int indent = d->indent;+ −
const int outer = d->rootDecoration ? 0 : 1;+ −
const int item = d->current;+ −
const QTreeViewItem &viewItem = d->viewItems.at(item);+ −
int level = viewItem.level;+ −
QRect primitive(reverse ? rect.left() : rect.right() + 1, rect.top(), indent, rect.height());+ −
+ −
QModelIndex parent = index.parent();+ −
QModelIndex current = parent;+ −
QModelIndex ancestor = current.parent();+ −
+ −
QStyleOptionViewItemV2 opt = viewOptions();+ −
QStyle::State extraFlags = QStyle::State_None;+ −
if (isEnabled())+ −
extraFlags |= QStyle::State_Enabled;+ −
if (window()->isActiveWindow())+ −
extraFlags |= QStyle::State_Active;+ −
QPoint oldBO = painter->brushOrigin();+ −
if (verticalScrollMode() == QAbstractItemView::ScrollPerPixel)+ −
painter->setBrushOrigin(QPoint(0, verticalOffset()));+ −
+ −
if (d->alternatingColors) {+ −
if (d->current & 1) {+ −
opt.features |= QStyleOptionViewItemV2::Alternate;+ −
} else {+ −
opt.features &= ~QStyleOptionViewItemV2::Alternate;+ −
}+ −
}+ −
+ −
// When hovering over a row, pass State_Hover for painting the branch+ −
// indicators if it has the decoration (aka branch) selected.+ −
bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows+ −
&& opt.showDecorationSelected+ −
&& index.parent() == d->hover.parent()+ −
&& index.row() == d->hover.row();+ −
+ −
if (d->selectionModel->isSelected(index))+ −
extraFlags |= QStyle::State_Selected;+ −
+ −
if (level >= outer) {+ −
// start with the innermost branch+ −
primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);+ −
opt.rect = primitive;+ −
+ −
const bool expanded = viewItem.expanded;+ −
const bool children = (((expanded && viewItem.total > 0)) // already laid out and has children+ −
|| d->hasVisibleChildren(index)); // not laid out yet, so we don't know+ −
bool moreSiblings = false;+ −
if (d->hiddenIndexes.isEmpty())+ −
moreSiblings = (d->model->rowCount(parent) - 1 > index.row());+ −
else+ −
moreSiblings = ((d->viewItems.size() > item +1)+ −
&& (d->viewItems.at(item + 1).index.parent() == parent));+ −
+ −
opt.state = QStyle::State_Item | extraFlags+ −
| (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)+ −
| (children ? QStyle::State_Children : QStyle::State_None)+ −
| (expanded ? QStyle::State_Open : QStyle::State_None);+ −
if (hoverRow || item == d->hoverBranch)+ −
opt.state |= QStyle::State_MouseOver;+ −
else+ −
opt.state &= ~QStyle::State_MouseOver;+ −
style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);+ −
}+ −
// then go out level by level+ −
for (--level; level >= outer; --level) { // we have already drawn the innermost branch+ −
primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);+ −
opt.rect = primitive;+ −
opt.state = extraFlags;+ −
bool moreSiblings = false;+ −
if (d->hiddenIndexes.isEmpty()) {+ −
moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());+ −
} else {+ −
int successor = item + viewItem.total + 1;+ −
while (successor < d->viewItems.size()+ −
&& d->viewItems.at(successor).level >= uint(level)) {+ −
const QTreeViewItem &successorItem = d->viewItems.at(successor);+ −
if (successorItem.level == uint(level)) {+ −
moreSiblings = true;+ −
break;+ −
}+ −
successor += successorItem.total + 1;+ −
}+ −
}+ −
if (moreSiblings)+ −
opt.state |= QStyle::State_Sibling;+ −
if (hoverRow || item == d->hoverBranch)+ −
opt.state |= QStyle::State_MouseOver;+ −
else+ −
opt.state &= ~QStyle::State_MouseOver;+ −
style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);+ −
current = ancestor;+ −
ancestor = current.parent();+ −
}+ −
painter->setBrushOrigin(oldBO);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::mousePressEvent(QMouseEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
bool handled = false;+ −
if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonPress)+ −
handled = d->expandOrCollapseItemAtPos(event->pos());+ −
if (!handled && d->itemDecorationAt(event->pos()) == -1)+ −
QAbstractItemView::mousePressEvent(event);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::mouseReleaseEvent(QMouseEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
if (d->itemDecorationAt(event->pos()) == -1) {+ −
QAbstractItemView::mouseReleaseEvent(event);+ −
} else {+ −
if (state() == QAbstractItemView::DragSelectingState)+ −
setState(QAbstractItemView::NoState);+ −
if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonRelease)+ −
d->expandOrCollapseItemAtPos(event->pos());+ −
}+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
if (state() != NoState || !d->viewport->rect().contains(event->pos()))+ −
return;+ −
+ −
int i = d->itemDecorationAt(event->pos());+ −
if (i == -1) {+ −
i = d->itemAtCoordinate(event->y());+ −
if (i == -1)+ −
return; // user clicked outside the items+ −
+ −
const QPersistentModelIndex firstColumnIndex = d->viewItems.at(i).index;+ −
+ −
int column = d->header->logicalIndexAt(event->x());+ −
QPersistentModelIndex persistent = firstColumnIndex.sibling(firstColumnIndex.row(), column);+ −
+ −
if (d->pressedIndex != persistent) {+ −
mousePressEvent(event);+ −
return;+ −
}+ −
+ −
// signal handlers may change the model+ −
emit doubleClicked(persistent);+ −
+ −
if (!persistent.isValid())+ −
return;+ −
+ −
if (edit(persistent, DoubleClicked, event) || state() != NoState)+ −
return; // the double click triggered editing+ −
+ −
if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))+ −
emit activated(persistent);+ −
+ −
d->executePostedLayout(); // we need to make sure viewItems is updated+ −
if (d->itemsExpandable+ −
&& d->expandsOnDoubleClick+ −
&& d->hasVisibleChildren(persistent)) {+ −
if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == firstColumnIndex))) {+ −
// find the new index of the item+ −
for (i = 0; i < d->viewItems.count(); ++i) {+ −
if (d->viewItems.at(i).index == firstColumnIndex)+ −
break;+ −
}+ −
if (i == d->viewItems.count())+ −
return;+ −
}+ −
if (d->viewItems.at(i).expanded)+ −
d->collapse(i, true);+ −
else+ −
d->expand(i, true);+ −
updateGeometries();+ −
viewport()->update();+ −
}+ −
}+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::mouseMoveEvent(QMouseEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
if (d->itemDecorationAt(event->pos()) == -1) // ### what about expanding/collapsing state ?+ −
QAbstractItemView::mouseMoveEvent(event);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::keyPressEvent(QKeyEvent *event)+ −
{+ −
Q_D(QTreeView);+ −
QModelIndex current = currentIndex();+ −
//this is the management of the expansion+ −
if (d->isIndexValid(current) && d->model && d->itemsExpandable) {+ −
switch (event->key()) {+ −
case Qt::Key_Asterisk: {+ −
QStack<QModelIndex> parents;+ −
parents.push(current);+ −
while (!parents.isEmpty()) {+ −
QModelIndex parent = parents.pop();+ −
for (int row = 0; row < d->model->rowCount(parent); ++row) {+ −
QModelIndex child = d->model->index(row, 0, parent);+ −
if (!d->isIndexValid(child))+ −
break;+ −
parents.push(child);+ −
expand(child);+ −
}+ −
}+ −
expand(current);+ −
break; }+ −
case Qt::Key_Plus:+ −
expand(current);+ −
break;+ −
case Qt::Key_Minus:+ −
collapse(current);+ −
break;+ −
}+ −
}+ −
+ −
QAbstractItemView::keyPressEvent(event);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
QModelIndex QTreeView::indexAt(const QPoint &point) const+ −
{+ −
Q_D(const QTreeView);+ −
d->executePostedLayout();+ −
+ −
int visualIndex = d->itemAtCoordinate(point.y());+ −
QModelIndex idx = d->modelIndex(visualIndex);+ −
if (!idx.isValid())+ −
return QModelIndex();+ −
+ −
if (d->viewItems.at(visualIndex).spanning)+ −
return idx;+ −
+ −
int column = d->columnAt(point.x());+ −
if (column == idx.column())+ −
return idx;+ −
if (column < 0)+ −
return QModelIndex();+ −
return idx.sibling(idx.row(), column);+ −
}+ −
+ −
/*!+ −
Returns the model index of the item above \a index.+ −
*/+ −
QModelIndex QTreeView::indexAbove(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
if (!d->isIndexValid(index))+ −
return QModelIndex();+ −
d->executePostedLayout();+ −
int i = d->viewIndex(index);+ −
if (--i < 0)+ −
return QModelIndex();+ −
return d->viewItems.at(i).index;+ −
}+ −
+ −
/*!+ −
Returns the model index of the item below \a index.+ −
*/+ −
QModelIndex QTreeView::indexBelow(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
if (!d->isIndexValid(index))+ −
return QModelIndex();+ −
d->executePostedLayout();+ −
int i = d->viewIndex(index);+ −
if (++i >= d->viewItems.count())+ −
return QModelIndex();+ −
return d->viewItems.at(i).index;+ −
}+ −
+ −
/*!+ −
\internal+ −
+ −
Lays out the items in the tree view.+ −
*/+ −
void QTreeView::doItemsLayout()+ −
{+ −
Q_D(QTreeView);+ −
d->viewItems.clear(); // prepare for new layout+ −
QModelIndex parent = d->root;+ −
if (d->model->hasChildren(parent)) {+ −
d->layout(-1);+ −
}+ −
QAbstractItemView::doItemsLayout();+ −
d->header->doItemsLayout();+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::reset()+ −
{+ −
Q_D(QTreeView);+ −
d->expandedIndexes.clear();+ −
d->hiddenIndexes.clear();+ −
d->spanningIndexes.clear();+ −
d->viewItems.clear();+ −
QAbstractItemView::reset();+ −
}+ −
+ −
/*!+ −
Returns the horizontal offset of the items in the treeview.+ −
+ −
Note that the tree view uses the horizontal header section+ −
positions to determine the positions of columns in the view.+ −
+ −
\sa verticalOffset()+ −
*/+ −
int QTreeView::horizontalOffset() const+ −
{+ −
Q_D(const QTreeView);+ −
return d->header->offset();+ −
}+ −
+ −
/*!+ −
Returns the vertical offset of the items in the tree view.+ −
+ −
\sa horizontalOffset()+ −
*/+ −
int QTreeView::verticalOffset() const+ −
{+ −
Q_D(const QTreeView);+ −
if (d->verticalScrollMode == QAbstractItemView::ScrollPerItem) {+ −
if (d->uniformRowHeights)+ −
return verticalScrollBar()->value() * d->defaultItemHeight;+ −
// If we are scrolling per item and have non-uniform row heights,+ −
// finding the vertical offset in pixels is going to be relatively slow.+ −
// ### find a faster way to do this+ −
d->executePostedLayout();+ −
int offset = 0;+ −
for (int i = 0; i < d->viewItems.count(); ++i) {+ −
if (i == verticalScrollBar()->value())+ −
return offset;+ −
offset += d->itemHeight(i);+ −
}+ −
return 0;+ −
}+ −
// scroll per pixel+ −
return verticalScrollBar()->value();+ −
}+ −
+ −
/*!+ −
Move the cursor in the way described by \a cursorAction, using the+ −
information provided by the button \a modifiers.+ −
*/+ −
QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)+ −
{+ −
Q_D(QTreeView);+ −
Q_UNUSED(modifiers);+ −
+ −
d->executePostedLayout();+ −
+ −
QModelIndex current = currentIndex();+ −
if (!current.isValid()) {+ −
int i = d->below(-1);+ −
int c = 0;+ −
while (c < d->header->count() && d->header->isSectionHidden(c))+ −
++c;+ −
if (i < d->viewItems.count() && c < d->header->count()) {+ −
return d->modelIndex(i, c);+ −
}+ −
return QModelIndex();+ −
}+ −
int vi = -1;+ −
#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)+ −
// Selection behavior is slightly different on the Mac.+ −
if (d->selectionMode == QAbstractItemView::ExtendedSelection+ −
&& d->selectionModel+ −
&& d->selectionModel->hasSelection()) {+ −
+ −
const bool moveUpDown = (cursorAction == MoveUp || cursorAction == MoveDown);+ −
const bool moveNextPrev = (cursorAction == MoveNext || cursorAction == MovePrevious);+ −
const bool contiguousSelection = moveUpDown && (modifiers & Qt::ShiftModifier);+ −
+ −
// Use the outermost index in the selection as the current index+ −
if (!contiguousSelection && (moveUpDown || moveNextPrev)) {+ −
+ −
// Find outermost index.+ −
const bool useTopIndex = (cursorAction == MoveUp || cursorAction == MovePrevious);+ −
int index = useTopIndex ? INT_MAX : INT_MIN;+ −
const QItemSelection selection = d->selectionModel->selection();+ −
for (int i = 0; i < selection.count(); ++i) {+ −
const QItemSelectionRange &range = selection.at(i);+ −
int candidate = d->viewIndex(useTopIndex ? range.topLeft() : range.bottomRight());+ −
if (candidate >= 0)+ −
index = useTopIndex ? qMin(index, candidate) : qMax(index, candidate);+ −
}+ −
+ −
if (index >= 0 && index < INT_MAX)+ −
vi = index;+ −
}+ −
}+ −
#endif+ −
if (vi < 0)+ −
vi = qMax(0, d->viewIndex(current));+ −
+ −
switch (cursorAction) {+ −
case MoveNext:+ −
case MoveDown:+ −
#ifdef QT_KEYPAD_NAVIGATION+ −
if (vi == d->viewItems.count()-1 && QApplication::keypadNavigationEnabled())+ −
return d->model->index(0, current.column(), d->root);+ −
#endif+ −
return d->modelIndex(d->below(vi), current.column());+ −
case MovePrevious:+ −
case MoveUp:+ −
#ifdef QT_KEYPAD_NAVIGATION+ −
if (vi == 0 && QApplication::keypadNavigationEnabled())+ −
return d->modelIndex(d->viewItems.count() - 1, current.column());+ −
#endif+ −
return d->modelIndex(d->above(vi), current.column());+ −
case MoveLeft: {+ −
QScrollBar *sb = horizontalScrollBar();+ −
if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum())+ −
d->collapse(vi, true);+ −
else {+ −
bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);+ −
if (descend) {+ −
QModelIndex par = current.parent();+ −
if (par.isValid() && par != rootIndex())+ −
return par;+ −
else+ −
descend = false;+ −
}+ −
if (!descend) {+ −
if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {+ −
int visualColumn = d->header->visualIndex(current.column()) - 1;+ −
while (visualColumn >= 0 && isColumnHidden(d->header->logicalIndex(visualColumn)))+ −
visualColumn--;+ −
int newColumn = d->header->logicalIndex(visualColumn);+ −
QModelIndex next = current.sibling(current.row(), newColumn);+ −
if (next.isValid())+ −
return next;+ −
}+ −
+ −
sb->setValue(sb->value() - sb->singleStep());+ −
}+ −
+ −
}+ −
updateGeometries();+ −
viewport()->update();+ −
break;+ −
}+ −
case MoveRight:+ −
if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable+ −
&& d->hasVisibleChildren(d->viewItems.at(vi).index)) {+ −
d->expand(vi, true);+ −
} else {+ −
bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);+ −
if (descend) {+ −
QModelIndex idx = d->modelIndex(d->below(vi));+ −
if (idx.parent() == current)+ −
return idx;+ −
else+ −
descend = false;+ −
}+ −
if (!descend) {+ −
if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {+ −
int visualColumn = d->header->visualIndex(current.column()) + 1;+ −
while (visualColumn < d->model->columnCount(current.parent()) && isColumnHidden(d->header->logicalIndex(visualColumn)))+ −
visualColumn++;+ −
+ −
QModelIndex next = current.sibling(current.row(), visualColumn);+ −
if (next.isValid())+ −
return next;+ −
}+ −
+ −
//last restort: we change the scrollbar value+ −
QScrollBar *sb = horizontalScrollBar();+ −
sb->setValue(sb->value() + sb->singleStep());+ −
}+ −
}+ −
updateGeometries();+ −
viewport()->update();+ −
break;+ −
case MovePageUp:+ −
return d->modelIndex(d->pageUp(vi), current.column());+ −
case MovePageDown:+ −
return d->modelIndex(d->pageDown(vi), current.column());+ −
case MoveHome:+ −
return d->model->index(0, current.column(), d->root);+ −
case MoveEnd:+ −
return d->modelIndex(d->viewItems.count() - 1, current.column());+ −
}+ −
return current;+ −
}+ −
+ −
/*!+ −
Applies the selection \a command to the items in or touched by the+ −
rectangle, \a rect.+ −
+ −
\sa selectionCommand()+ −
*/+ −
void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)+ −
{+ −
Q_D(QTreeView);+ −
if (!selectionModel() || rect.isNull())+ −
return;+ −
+ −
d->executePostedLayout();+ −
QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())+ −
: qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));+ −
QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :+ −
qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));+ −
QModelIndex topLeft = indexAt(tl);+ −
QModelIndex bottomRight = indexAt(br);+ −
if (!topLeft.isValid() && !bottomRight.isValid()) {+ −
if (command & QItemSelectionModel::Clear)+ −
selectionModel()->clear();+ −
return;+ −
}+ −
if (!topLeft.isValid() && !d->viewItems.isEmpty())+ −
topLeft = d->viewItems.first().index;+ −
if (!bottomRight.isValid() && !d->viewItems.isEmpty()) {+ −
const int column = d->header->logicalIndex(d->header->count() - 1);+ −
const QModelIndex index = d->viewItems.last().index;+ −
bottomRight = index.sibling(index.row(), column);+ −
}+ −
+ −
if (!d->isIndexEnabled(topLeft) || !d->isIndexEnabled(bottomRight))+ −
return;+ −
+ −
d->select(topLeft, bottomRight, command);+ −
}+ −
+ −
/*!+ −
Returns the rectangle from the viewport of the items in the given+ −
\a selection.+ −
*/+ −
QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const+ −
{+ −
Q_D(const QTreeView);+ −
if (selection.isEmpty())+ −
return QRegion();+ −
+ −
QRegion selectionRegion;+ −
for (int i = 0; i < selection.count(); ++i) {+ −
QItemSelectionRange range = selection.at(i);+ −
if (!range.isValid())+ −
continue;+ −
QModelIndex parent = range.parent();+ −
QModelIndex leftIndex = range.topLeft();+ −
int columnCount = d->model->columnCount(parent);+ −
while (leftIndex.isValid() && isIndexHidden(leftIndex)) {+ −
if (leftIndex.column() + 1 < columnCount)+ −
leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);+ −
else+ −
leftIndex = QModelIndex();+ −
}+ −
if (!leftIndex.isValid())+ −
continue;+ −
const QRect leftRect = visualRect(leftIndex);+ −
int top = leftRect.top();+ −
QModelIndex rightIndex = range.bottomRight();+ −
while (rightIndex.isValid() && isIndexHidden(rightIndex)) {+ −
if (rightIndex.column() - 1 >= 0)+ −
rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);+ −
else+ −
rightIndex = QModelIndex();+ −
}+ −
if (!rightIndex.isValid())+ −
continue;+ −
const QRect rightRect = visualRect(rightIndex);+ −
int bottom = rightRect.bottom();+ −
if (top > bottom)+ −
qSwap<int>(top, bottom);+ −
int height = bottom - top + 1;+ −
if (d->header->sectionsMoved()) {+ −
for (int c = range.left(); c <= range.right(); ++c)+ −
selectionRegion += QRegion(QRect(columnViewportPosition(c), top,+ −
columnWidth(c), height));+ −
} else {+ −
QRect combined = leftRect|rightRect;+ −
combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left()));+ −
selectionRegion += combined;+ −
}+ −
}+ −
return selectionRegion;+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
QModelIndexList QTreeView::selectedIndexes() const+ −
{+ −
QModelIndexList viewSelected;+ −
QModelIndexList modelSelected;+ −
if (selectionModel())+ −
modelSelected = selectionModel()->selectedIndexes();+ −
for (int i = 0; i < modelSelected.count(); ++i) {+ −
// check that neither the parents nor the index is hidden before we add+ −
QModelIndex index = modelSelected.at(i);+ −
while (index.isValid() && !isIndexHidden(index))+ −
index = index.parent();+ −
if (index.isValid())+ −
continue;+ −
viewSelected.append(modelSelected.at(i));+ −
}+ −
return viewSelected;+ −
}+ −
+ −
/*!+ −
Scrolls the contents of the tree view by (\a dx, \a dy).+ −
*/+ −
void QTreeView::scrollContentsBy(int dx, int dy)+ −
{+ −
Q_D(QTreeView);+ −
+ −
d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling+ −
+ −
dx = isRightToLeft() ? -dx : dx;+ −
if (dx) {+ −
if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {+ −
int oldOffset = d->header->offset();+ −
if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())+ −
d->header->setOffsetToLastSection();+ −
else+ −
d->header->setOffsetToSectionPosition(horizontalScrollBar()->value());+ −
int newOffset = d->header->offset();+ −
dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;+ −
} else {+ −
d->header->setOffset(horizontalScrollBar()->value());+ −
}+ −
}+ −
+ −
const int itemHeight = d->defaultItemHeight <= 0 ? sizeHintForRow(0) : d->defaultItemHeight;+ −
if (d->viewItems.isEmpty() || itemHeight == 0)+ −
return;+ −
+ −
// guestimate the number of items in the viewport+ −
int viewCount = d->viewport->height() / itemHeight;+ −
int maxDeltaY = qMin(d->viewItems.count(), viewCount);+ −
// no need to do a lot of work if we are going to redraw the whole thing anyway+ −
if (qAbs(dy) > qAbs(maxDeltaY) && d->editors.isEmpty()) {+ −
verticalScrollBar()->update();+ −
d->viewport->update();+ −
return;+ −
}+ −
+ −
if (dy && verticalScrollMode() == QAbstractItemView::ScrollPerItem) {+ −
int currentScrollbarValue = verticalScrollBar()->value();+ −
int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)+ −
int currentViewIndex = currentScrollbarValue; // the first visible item+ −
int previousViewIndex = previousScrollbarValue;+ −
const QVector<QTreeViewItem> viewItems = d->viewItems;+ −
dy = 0;+ −
if (previousViewIndex < currentViewIndex) { // scrolling down+ −
for (int i = previousViewIndex; i < currentViewIndex; ++i) {+ −
if (i < d->viewItems.count())+ −
dy -= d->itemHeight(i);+ −
}+ −
} else if (previousViewIndex > currentViewIndex) { // scrolling up+ −
for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {+ −
if (i < d->viewItems.count())+ −
dy += d->itemHeight(i);+ −
}+ −
}+ −
}+ −
+ −
d->scrollContentsBy(dx, dy);+ −
}+ −
+ −
/*!+ −
This slot is called whenever a column has been moved.+ −
*/+ −
void QTreeView::columnMoved()+ −
{+ −
Q_D(QTreeView);+ −
updateEditorGeometries();+ −
d->viewport->update();+ −
}+ −
+ −
/*!+ −
\internal+ −
*/+ −
void QTreeView::reexpand()+ −
{+ −
// do nothing+ −
}+ −
+ −
/*!+ −
\internal+ −
*/+ −
static bool treeViewItemLessThan(const QTreeViewItem &left,+ −
const QTreeViewItem &right)+ −
{+ −
if (left.level != right.level) {+ −
Q_ASSERT(left.level > right.level);+ −
QModelIndex leftParent = left.index.parent();+ −
QModelIndex rightParent = right.index.parent();+ −
// computer parent, don't get+ −
while (leftParent.isValid() && leftParent.parent() != rightParent)+ −
leftParent = leftParent.parent();+ −
return (leftParent.row() < right.index.row());+ −
}+ −
return (left.index.row() < right.index.row());+ −
}+ −
+ −
/*!+ −
Informs the view that the rows from the \a start row to the \a end row+ −
inclusive have been inserted into the \a parent model item.+ −
*/+ −
void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end)+ −
{+ −
Q_D(QTreeView);+ −
// if we are going to do a complete relayout anyway, there is no need to update+ −
if (d->delayedPendingLayout) {+ −
QAbstractItemView::rowsInserted(parent, start, end);+ −
return;+ −
}+ −
+ −
//don't add a hierarchy on a column != 0+ −
if (parent.column() != 0 && parent.isValid()) {+ −
QAbstractItemView::rowsInserted(parent, start, end);+ −
return;+ −
}+ −
+ −
if (parent != d->root && !d->isIndexExpanded(parent) && d->model->rowCount(parent) > (end - start) + 1) {+ −
QAbstractItemView::rowsInserted(parent, start, end);+ −
return;+ −
}+ −
+ −
const int parentItem = d->viewIndex(parent);+ −
if (((parentItem != -1) && d->viewItems.at(parentItem).expanded && updatesEnabled())+ −
|| (parent == d->root)) {+ −
const uint childLevel = (parentItem == -1)+ −
? uint(0) : d->viewItems.at(parentItem).level + 1;+ −
const int firstChildItem = parentItem + 1;+ −
const int lastChildItem = firstChildItem + ((parentItem == -1)+ −
? d->viewItems.count()+ −
: d->viewItems.at(parentItem).total) - 1;+ −
+ −
const int delta = end - start + 1;+ −
QVector<QTreeViewItem> insertedItems(delta);+ −
for (int i = 0; i < delta; ++i) {+ −
insertedItems[i].index = d->model->index(i + start, 0, parent);+ −
insertedItems[i].level = childLevel;+ −
}+ −
if (d->viewItems.isEmpty())+ −
d->defaultItemHeight = indexRowSizeHint(insertedItems[0].index);+ −
+ −
int insertPos;+ −
if (lastChildItem < firstChildItem) { // no children+ −
insertPos = firstChildItem;+ −
} else {+ −
// do a binary search to figure out where to insert+ −
QVector<QTreeViewItem>::iterator it;+ −
it = qLowerBound(d->viewItems.begin() + firstChildItem,+ −
d->viewItems.begin() + lastChildItem + 1,+ −
insertedItems.at(0), treeViewItemLessThan);+ −
insertPos = it - d->viewItems.begin();+ −
+ −
// update stale model indexes of siblings+ −
for (int item = insertPos; item <= lastChildItem; ) {+ −
Q_ASSERT(d->viewItems.at(item).level == childLevel);+ −
const QModelIndex modelIndex = d->viewItems.at(item).index;+ −
//Q_ASSERT(modelIndex.parent() == parent);+ −
d->viewItems[item].index = d->model->index(+ −
modelIndex.row() + delta, modelIndex.column(), parent);+ −
+ −
if (!d->viewItems[item].index.isValid()) {+ −
// Something really bad is happening, a bad model is+ −
// often the cause. We can't optimize in this case :(+ −
qWarning() << "QTreeView::rowsInserted internal representation of the model has been corrupted, resetting.";+ −
doItemsLayout();+ −
return;+ −
}+ −
+ −
item += d->viewItems.at(item).total + 1;+ −
}+ −
}+ −
+ −
d->viewItems.insert(insertPos, delta, insertedItems.at(0));+ −
if (delta > 1) {+ −
qCopy(insertedItems.begin() + 1, insertedItems.end(),+ −
d->viewItems.begin() + insertPos + 1);+ −
}+ −
+ −
d->updateChildCount(parentItem, delta);+ −
updateGeometries();+ −
viewport()->update();+ −
} else if ((parentItem != -1) && d->viewItems.at(parentItem).expanded) {+ −
d->doDelayedItemsLayout();+ −
} else if (parentItem != -1 && (d->model->rowCount(parent) == end - start + 1)) {+ −
// the parent just went from 0 children to having some update to re-paint the decoration+ −
viewport()->update();+ −
}+ −
QAbstractItemView::rowsInserted(parent, start, end);+ −
}+ −
+ −
/*!+ −
Informs the view that the rows from the \a start row to the \a end row+ −
inclusive are about to removed from the given \a parent model item.+ −
*/+ −
void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)+ −
{+ −
Q_D(QTreeView);+ −
d->rowsRemoved(parent, start, end, false);+ −
QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);+ −
}+ −
+ −
/*!+ −
\since 4.1+ −
+ −
Informs the view that the rows from the \a start row to the \a end row+ −
inclusive have been removed from the given \a parent model item.+ −
*/+ −
void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end)+ −
{+ −
Q_D(QTreeView);+ −
d->rowsRemoved(parent, start, end, true);+ −
}+ −
+ −
/*!+ −
Informs the tree view that the number of columns in the tree view has+ −
changed from \a oldCount to \a newCount.+ −
*/+ −
void QTreeView::columnCountChanged(int oldCount, int newCount)+ −
{+ −
Q_D(QTreeView);+ −
if (oldCount == 0 && newCount > 0) {+ −
//if the first column has just been added we need to relayout.+ −
d->doDelayedItemsLayout();+ −
}+ −
+ −
if (isVisible())+ −
updateGeometries();+ −
viewport()->update();+ −
}+ −
+ −
/*!+ −
Resizes the \a column given to the size of its contents.+ −
+ −
\sa columnWidth(), setColumnWidth()+ −
*/+ −
void QTreeView::resizeColumnToContents(int column)+ −
{+ −
Q_D(QTreeView);+ −
d->executePostedLayout();+ −
if (column < 0 || column >= d->header->count())+ −
return;+ −
int contents = sizeHintForColumn(column);+ −
int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);+ −
d->header->resizeSection(column, qMax(contents, header));+ −
}+ −
+ −
/*!+ −
\obsolete+ −
\overload+ −
+ −
Sorts the model by the values in the given \a column.+ −
*/+ −
void QTreeView::sortByColumn(int column)+ −
{+ −
Q_D(QTreeView);+ −
sortByColumn(column, d->header->sortIndicatorOrder());+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
+ −
Sets the model up for sorting by the values in the given \a column and \a order.+ −
+ −
\a column may be -1, in which case no sort indicator will be shown+ −
and the model will return to its natural, unsorted order. Note that not+ −
all models support this and may even crash in this case.+ −
+ −
\sa sortingEnabled+ −
*/+ −
void QTreeView::sortByColumn(int column, Qt::SortOrder order)+ −
{+ −
Q_D(QTreeView);+ −
+ −
//If sorting is enabled will emit a signal connected to _q_sortIndicatorChanged, which then actually sorts+ −
d->header->setSortIndicator(column, order);+ −
//If sorting is not enabled, force to sort now.+ −
if (!d->sortingEnabled)+ −
d->model->sort(column, order);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::selectAll()+ −
{+ −
Q_D(QTreeView);+ −
if (!selectionModel())+ −
return;+ −
SelectionMode mode = d->selectionMode;+ −
d->executePostedLayout(); //make sure we lay out the items+ −
if (mode != SingleSelection && !d->viewItems.isEmpty())+ −
d->select(d->viewItems.first().index, d->viewItems.last().index,+ −
QItemSelectionModel::ClearAndSelect+ −
|QItemSelectionModel::Rows);+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
Expands all expandable items.+ −
+ −
Warning: if the model contains a large number of items,+ −
this function will take some time to execute.+ −
+ −
\sa collapseAll() expand() collapse() setExpanded()+ −
*/+ −
void QTreeView::expandAll()+ −
{+ −
Q_D(QTreeView);+ −
d->viewItems.clear();+ −
d->expandedIndexes.clear();+ −
d->interruptDelayedItemsLayout();+ −
d->layout(-1);+ −
for (int i = 0; i < d->viewItems.count(); ++i) {+ −
if (d->viewItems[i].expanded)+ −
continue;+ −
d->viewItems[i].expanded = true;+ −
d->layout(i);+ −
QModelIndex idx = d->viewItems.at(i).index;+ −
d->expandedIndexes.insert(idx);+ −
}+ −
updateGeometries();+ −
d->viewport->update();+ −
}+ −
+ −
/*!+ −
\since 4.2+ −
+ −
Collapses all expanded items.+ −
+ −
\sa expandAll() expand() collapse() setExpanded()+ −
*/+ −
void QTreeView::collapseAll()+ −
{+ −
Q_D(QTreeView);+ −
d->expandedIndexes.clear();+ −
doItemsLayout();+ −
}+ −
+ −
/*!+ −
\since 4.3+ −
Expands all expandable items to the given \a depth.+ −
+ −
\sa expandAll() collapseAll() expand() collapse() setExpanded()+ −
*/+ −
void QTreeView::expandToDepth(int depth)+ −
{+ −
Q_D(QTreeView);+ −
d->viewItems.clear();+ −
d->expandedIndexes.clear();+ −
d->interruptDelayedItemsLayout();+ −
d->layout(-1);+ −
for (int i = 0; i < d->viewItems.count(); ++i) {+ −
if (d->viewItems.at(i).level <= (uint)depth) {+ −
d->viewItems[i].expanded = true;+ −
d->layout(i);+ −
d->storeExpanded(d->viewItems.at(i).index);+ −
}+ −
}+ −
updateGeometries();+ −
d->viewport->update();+ −
}+ −
+ −
/*!+ −
This function is called whenever \a{column}'s size is changed in+ −
the header. \a oldSize and \a newSize give the previous size and+ −
the new size in pixels.+ −
+ −
\sa setColumnWidth()+ −
*/+ −
void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)+ −
{+ −
Q_D(QTreeView);+ −
d->columnsToUpdate.append(column);+ −
if (d->columnResizeTimerID == 0)+ −
d->columnResizeTimerID = startTimer(0);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::updateGeometries()+ −
{+ −
Q_D(QTreeView);+ −
if (d->header) {+ −
if (d->geometryRecursionBlock)+ −
return;+ −
d->geometryRecursionBlock = true;+ −
QSize hint = d->header->isHidden() ? QSize(0, 0) : d->header->sizeHint();+ −
setViewportMargins(0, hint.height(), 0, 0);+ −
QRect vg = d->viewport->geometry();+ −
QRect geometryRect(vg.left(), vg.top() - hint.height(), vg.width(), hint.height());+ −
d->header->setGeometry(geometryRect);+ −
//d->header->setOffset(horizontalScrollBar()->value()); // ### bug ???+ −
QMetaObject::invokeMethod(d->header, "updateGeometries");+ −
d->updateScrollBars();+ −
d->geometryRecursionBlock = false;+ −
}+ −
QAbstractItemView::updateGeometries();+ −
}+ −
+ −
/*!+ −
Returns the size hint for the \a column's width or -1 if there is no+ −
model.+ −
+ −
If you need to set the width of a given column to a fixed value, call+ −
QHeaderView::resizeSection() on the view's header.+ −
+ −
If you reimplement this function in a subclass, note that the value you+ −
return is only used when resizeColumnToContents() is called. In that case,+ −
if a larger column width is required by either the view's header or+ −
the item delegate, that width will be used instead.+ −
+ −
\sa QWidget::sizeHint, header()+ −
*/+ −
int QTreeView::sizeHintForColumn(int column) const+ −
{+ −
Q_D(const QTreeView);+ −
d->executePostedLayout();+ −
if (d->viewItems.isEmpty())+ −
return -1;+ −
int w = 0;+ −
QStyleOptionViewItemV4 option = d->viewOptionsV4();+ −
const QVector<QTreeViewItem> viewItems = d->viewItems;+ −
+ −
int start = 0;+ −
int end = viewItems.count();+ −
if(end > 1000) { //if we have too many item this function would be too slow.+ −
//we get a good approximation by only iterate over 1000 items.+ −
start = qMax(0, d->firstVisibleItem() - 100);+ −
end = qMin(end, start + 900);+ −
}+ −
+ −
for (int i = start; i < end; ++i) {+ −
if (viewItems.at(i).spanning)+ −
continue; // we have no good size hint+ −
QModelIndex index = viewItems.at(i).index;+ −
index = index.sibling(index.row(), column);+ −
QWidget *editor = d->editorForIndex(index).editor;+ −
if (editor && d->persistent.contains(editor)) {+ −
w = qMax(w, editor->sizeHint().width());+ −
int min = editor->minimumSize().width();+ −
int max = editor->maximumSize().width();+ −
w = qBound(min, w, max);+ −
}+ −
int hint = d->delegateForIndex(index)->sizeHint(option, index).width();+ −
w = qMax(w, hint + (column == 0 ? d->indentationForItem(i) : 0));+ −
}+ −
return w;+ −
}+ −
+ −
/*!+ −
Returns the size hint for the row indicated by \a index.+ −
+ −
\sa sizeHintForColumn(), uniformRowHeights()+ −
*/+ −
int QTreeView::indexRowSizeHint(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
if (!d->isIndexValid(index) || !d->itemDelegate)+ −
return 0;+ −
+ −
int start = -1;+ −
int end = -1;+ −
int count = d->header->count();+ −
bool emptyHeader = (count == 0);+ −
QModelIndex parent = index.parent();+ −
+ −
if (count && isVisible()) {+ −
// If the sections have moved, we end up checking too many or too few+ −
start = d->header->visualIndexAt(0);+ −
} else {+ −
// If the header has not been laid out yet, we use the model directly+ −
count = d->model->columnCount(parent);+ −
}+ −
+ −
if (isRightToLeft()) {+ −
start = (start == -1 ? count - 1 : start);+ −
end = 0;+ −
} else {+ −
start = (start == -1 ? 0 : start);+ −
end = count - 1;+ −
}+ −
+ −
if (end < start)+ −
qSwap(end, start);+ −
+ −
int height = -1;+ −
QStyleOptionViewItemV4 option = d->viewOptionsV4();+ −
// ### If we want word wrapping in the items,+ −
// ### we need to go through all the columns+ −
// ### and set the width of the column+ −
+ −
// Hack to speed up the function+ −
option.rect.setWidth(-1);+ −
+ −
for (int column = start; column <= end; ++column) {+ −
int logicalColumn = emptyHeader ? column : d->header->logicalIndex(column);+ −
if (d->header->isSectionHidden(logicalColumn))+ −
continue;+ −
QModelIndex idx = d->model->index(index.row(), logicalColumn, parent);+ −
if (idx.isValid()) {+ −
QWidget *editor = d->editorForIndex(idx).editor;+ −
if (editor && d->persistent.contains(editor)) {+ −
height = qMax(height, editor->sizeHint().height());+ −
int min = editor->minimumSize().height();+ −
int max = editor->maximumSize().height();+ −
height = qBound(min, height, max);+ −
}+ −
int hint = d->delegateForIndex(idx)->sizeHint(option, idx).height();+ −
height = qMax(height, hint);+ −
}+ −
}+ −
+ −
return height;+ −
}+ −
+ −
/*!+ −
\since 4.3+ −
Returns the height of the row indicated by the given \a index.+ −
\sa indexRowSizeHint()+ −
*/+ −
int QTreeView::rowHeight(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
d->executePostedLayout();+ −
int i = d->viewIndex(index);+ −
if (i == -1)+ −
return 0;+ −
return d->itemHeight(i);+ −
}+ −
+ −
/*!+ −
\internal+ −
*/+ −
void QTreeView::horizontalScrollbarAction(int action)+ −
{+ −
QAbstractItemView::horizontalScrollbarAction(action);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
bool QTreeView::isIndexHidden(const QModelIndex &index) const+ −
{+ −
return (isColumnHidden(index.column()) || isRowHidden(index.row(), index.parent()));+ −
}+ −
+ −
/*+ −
private implementation+ −
*/+ −
void QTreeViewPrivate::initialize()+ −
{+ −
Q_Q(QTreeView);+ −
updateStyledFrameWidths();+ −
q->setSelectionBehavior(QAbstractItemView::SelectRows);+ −
q->setSelectionMode(QAbstractItemView::SingleSelection);+ −
q->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);+ −
q->setAttribute(Qt::WA_MacShowFocusRect);+ −
+ −
QHeaderView *header = new QHeaderView(Qt::Horizontal, q);+ −
header->setMovable(true);+ −
header->setStretchLastSection(true);+ −
header->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter);+ −
q->setHeader(header);+ −
#ifndef QT_NO_ANIMATION+ −
QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()));+ −
#endif //QT_NO_ANIMATION+ −
}+ −
+ −
void QTreeViewPrivate::expand(int item, bool emitSignal)+ −
{+ −
Q_Q(QTreeView);+ −
+ −
if (item == -1 || viewItems.at(item).expanded)+ −
return;+ −
+ −
#ifndef QT_NO_ANIMATION+ −
if (emitSignal && animationsEnabled)+ −
prepareAnimatedOperation(item, QVariantAnimation::Forward);+ −
#endif //QT_NO_ANIMATION+ −
QAbstractItemView::State oldState = state;+ −
q->setState(QAbstractItemView::ExpandingState);+ −
const QModelIndex index = viewItems.at(item).index;+ −
storeExpanded(index);+ −
viewItems[item].expanded = true;+ −
layout(item);+ −
q->setState(oldState);+ −
+ −
if (model->canFetchMore(index))+ −
model->fetchMore(index);+ −
if (emitSignal) {+ −
emit q->expanded(index);+ −
#ifndef QT_NO_ANIMATION+ −
if (animationsEnabled)+ −
beginAnimatedOperation();+ −
#endif //QT_NO_ANIMATION+ −
}+ −
}+ −
+ −
void QTreeViewPrivate::collapse(int item, bool emitSignal)+ −
{+ −
Q_Q(QTreeView);+ −
+ −
if (item == -1 || expandedIndexes.isEmpty())+ −
return;+ −
+ −
//if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll+ −
delayedAutoScroll.stop();+ −
+ −
int total = viewItems.at(item).total;+ −
const QModelIndex &modelIndex = viewItems.at(item).index;+ −
if (!isPersistent(modelIndex))+ −
return; // if the index is not persistent, no chances it is expanded+ −
QSet<QPersistentModelIndex>::iterator it = expandedIndexes.find(modelIndex);+ −
if (it == expandedIndexes.end() || viewItems.at(item).expanded == false)+ −
return; // nothing to do+ −
+ −
#ifndef QT_NO_ANIMATION+ −
if (emitSignal && animationsEnabled)+ −
prepareAnimatedOperation(item, QVariantAnimation::Backward);+ −
#endif //QT_NO_ANIMATION+ −
+ −
QAbstractItemView::State oldState = state;+ −
q->setState(QAbstractItemView::CollapsingState);+ −
expandedIndexes.erase(it);+ −
viewItems[item].expanded = false;+ −
int index = item;+ −
QModelIndex parent = modelIndex;+ −
while (parent.isValid() && parent != root) {+ −
Q_ASSERT(index > -1);+ −
viewItems[index].total -= total;+ −
parent = parent.parent();+ −
index = viewIndex(parent);+ −
}+ −
viewItems.remove(item + 1, total); // collapse+ −
q->setState(oldState);+ −
+ −
if (emitSignal) {+ −
emit q->collapsed(modelIndex);+ −
#ifndef QT_NO_ANIMATION+ −
if (animationsEnabled)+ −
beginAnimatedOperation();+ −
#endif //QT_NO_ANIMATION+ −
}+ −
}+ −
+ −
#ifndef QT_NO_ANIMATION+ −
void QTreeViewPrivate::prepareAnimatedOperation(int item, QVariantAnimation::Direction direction)+ −
{+ −
animatedOperation.item = item;+ −
animatedOperation.viewport = viewport;+ −
animatedOperation.setDirection(direction);+ −
+ −
int top = coordinateForItem(item) + itemHeight(item);+ −
QRect rect = viewport->rect();+ −
rect.setTop(top);+ −
if (direction == QVariantAnimation::Backward) {+ −
const int limit = rect.height() * 2;+ −
int h = 0;+ −
int c = item + viewItems.at(item).total + 1;+ −
for (int i = item + 1; i < c && h < limit; ++i)+ −
h += itemHeight(i);+ −
rect.setHeight(h);+ −
animatedOperation.setEndValue(top + h);+ −
}+ −
animatedOperation.setStartValue(top);+ −
animatedOperation.before = renderTreeToPixmapForAnimation(rect);+ −
}+ −
+ −
void QTreeViewPrivate::beginAnimatedOperation()+ −
{+ −
Q_Q(QTreeView);+ −
+ −
QRect rect = viewport->rect();+ −
rect.setTop(animatedOperation.top());+ −
if (animatedOperation.direction() == QVariantAnimation::Forward) {+ −
const int limit = rect.height() * 2;+ −
int h = 0;+ −
int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1;+ −
for (int i = animatedOperation.item + 1; i < c && h < limit; ++i)+ −
h += itemHeight(i);+ −
rect.setHeight(h);+ −
animatedOperation.setEndValue(animatedOperation.top() + h);+ −
}+ −
+ −
if (!rect.isEmpty()) {+ −
animatedOperation.after = renderTreeToPixmapForAnimation(rect);+ −
+ −
q->setState(QAbstractItemView::AnimatingState);+ −
animatedOperation.start(); //let's start the animation+ −
}+ −
}+ −
+ −
void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const+ −
{+ −
const int start = animatedOperation.startValue().toInt(),+ −
end = animatedOperation.endValue().toInt(),+ −
current = animatedOperation.currentValue().toInt();+ −
bool collapsing = animatedOperation.direction() == QVariantAnimation::Backward;+ −
const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after;+ −
painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height());+ −
const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before;+ −
painter->drawPixmap(0, current, bottom);+ −
}+ −
+ −
QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) const+ −
{+ −
Q_Q(const QTreeView);+ −
QPixmap pixmap(rect.size());+ −
pixmap.fill(Qt::transparent); //the base might not be opaque, and we don't want uninitialized pixels.+ −
QPainter painter(&pixmap);+ −
painter.fillRect(QRect(QPoint(0,0), rect.size()), q->palette().base());+ −
painter.translate(0, -rect.top());+ −
q->drawTree(&painter, QRegion(rect));+ −
painter.end();+ −
+ −
//and now let's render the editors the editors+ −
QStyleOptionViewItemV4 option = viewOptionsV4();+ −
for (QList<QEditorInfo>::const_iterator it = editors.constBegin(); it != editors.constEnd(); ++it) {+ −
QWidget *editor = it->editor;+ −
QModelIndex index = it->index;+ −
option.rect = q->visualRect(index);+ −
if (option.rect.isValid()) {+ −
+ −
if (QAbstractItemDelegate *delegate = delegateForIndex(index))+ −
delegate->updateEditorGeometry(editor, option, index);+ −
+ −
const QPoint pos = editor->pos();+ −
if (rect.contains(pos)) {+ −
editor->render(&pixmap, pos - rect.topLeft());+ −
//the animation uses pixmap to display the treeview's content+ −
//the editor is rendered on this pixmap and thus can (should) be hidden+ −
editor->hide();+ −
}+ −
}+ −
}+ −
+ −
+ −
return pixmap;+ −
}+ −
+ −
void QTreeViewPrivate::_q_endAnimatedOperation()+ −
{+ −
Q_Q(QTreeView);+ −
q->setState(QAbstractItemView::NoState);+ −
q->updateGeometries();+ −
viewport->update();+ −
}+ −
#endif //QT_NO_ANIMATION+ −
+ −
void QTreeViewPrivate::_q_modelAboutToBeReset()+ −
{+ −
viewItems.clear();+ −
}+ −
+ −
void QTreeViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)+ −
{+ −
if (start <= 0 && 0 <= end)+ −
viewItems.clear();+ −
QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(parent, start, end);+ −
}+ −
+ −
void QTreeViewPrivate::_q_columnsRemoved(const QModelIndex &parent, int start, int end)+ −
{+ −
if (start <= 0 && 0 <= end)+ −
doDelayedItemsLayout();+ −
QAbstractItemViewPrivate::_q_columnsRemoved(parent, start, end);+ −
}+ −
+ −
void QTreeViewPrivate::layout(int i)+ −
{+ −
Q_Q(QTreeView);+ −
QModelIndex current;+ −
QModelIndex parent = (i < 0) ? (QModelIndex)root : modelIndex(i);+ −
+ −
if (i>=0 && !parent.isValid()) {+ −
//modelIndex() should never return something invalid for the real items.+ −
//This can happen if columncount has been set to 0.+ −
//To avoid infinite loop we stop here.+ −
return;+ −
}+ −
+ −
int count = 0;+ −
if (model->hasChildren(parent)) {+ −
if (model->canFetchMore(parent))+ −
model->fetchMore(parent);+ −
count = model->rowCount(parent);+ −
}+ −
+ −
bool expanding = true;+ −
if (i == -1) {+ −
if (uniformRowHeights) {+ −
QModelIndex index = model->index(0, 0, parent);+ −
defaultItemHeight = q->indexRowSizeHint(index);+ −
}+ −
viewItems.resize(count);+ −
} else if (viewItems[i].total != (uint)count) {+ −
viewItems.insert(i + 1, count, QTreeViewItem()); // expand+ −
} else {+ −
expanding = false;+ −
}+ −
+ −
int first = i + 1;+ −
int level = (i >= 0 ? viewItems.at(i).level + 1 : 0);+ −
int hidden = 0;+ −
int last = 0;+ −
int children = 0;+ −
+ −
for (int j = first; j < first + count; ++j) {+ −
current = model->index(j - first, 0, parent);+ −
if (isRowHidden(current)) {+ −
++hidden;+ −
last = j - hidden + children;+ −
} else {+ −
last = j - hidden + children;+ −
viewItems[last].index = current;+ −
viewItems[last].level = level;+ −
viewItems[last].height = 0;+ −
viewItems[last].spanning = q->isFirstColumnSpanned(current.row(), parent);+ −
viewItems[last].expanded = false;+ −
viewItems[last].total = 0;+ −
if (isIndexExpanded(current)) {+ −
viewItems[last].expanded = true;+ −
layout(last);+ −
children += viewItems[last].total;+ −
last = j - hidden + children;+ −
}+ −
}+ −
}+ −
+ −
// remove hidden items+ −
if (hidden > 0)+ −
viewItems.remove(last + 1, hidden); // collapse+ −
+ −
if (!expanding)+ −
return; // nothing changed+ −
+ −
while (parent != root) {+ −
Q_ASSERT(i > -1);+ −
viewItems[i].total += count - hidden;+ −
parent = parent.parent();+ −
i = viewIndex(parent);+ −
}+ −
}+ −
+ −
int QTreeViewPrivate::pageUp(int i) const+ −
{+ −
int index = itemAtCoordinate(coordinateForItem(i) - viewport->height());+ −
return index == -1 ? 0 : index;+ −
}+ −
+ −
int QTreeViewPrivate::pageDown(int i) const+ −
{+ −
int index = itemAtCoordinate(coordinateForItem(i) + viewport->height());+ −
return index == -1 ? viewItems.count() - 1 : index;+ −
}+ −
+ −
int QTreeViewPrivate::indentationForItem(int item) const+ −
{+ −
if (item < 0 || item >= viewItems.count())+ −
return 0;+ −
int level = viewItems.at(item).level;+ −
if (rootDecoration)+ −
++level;+ −
return level * indent;+ −
}+ −
+ −
int QTreeViewPrivate::itemHeight(int item) const+ −
{+ −
if (uniformRowHeights)+ −
return defaultItemHeight;+ −
if (viewItems.isEmpty())+ −
return 0;+ −
const QModelIndex &index = viewItems.at(item).index;+ −
int height = viewItems.at(item).height;+ −
if (height <= 0 && index.isValid()) {+ −
height = q_func()->indexRowSizeHint(index);+ −
viewItems[item].height = height;+ −
}+ −
if (!index.isValid() || height < 0)+ −
return 0;+ −
return height;+ −
}+ −
+ −
+ −
/*!+ −
\internal+ −
Returns the viewport y coordinate for \a item.+ −
*/+ −
int QTreeViewPrivate::coordinateForItem(int item) const+ −
{+ −
if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {+ −
if (uniformRowHeights)+ −
return (item * defaultItemHeight) - vbar->value();+ −
// ### optimize (spans or caching)+ −
int y = 0;+ −
for (int i = 0; i < viewItems.count(); ++i) {+ −
if (i == item)+ −
return y - vbar->value();+ −
y += itemHeight(i);+ −
}+ −
} else { // ScrollPerItem+ −
int topViewItemIndex = vbar->value();+ −
if (uniformRowHeights)+ −
return defaultItemHeight * (item - topViewItemIndex);+ −
if (item >= topViewItemIndex) {+ −
// search in the visible area first and continue down+ −
// ### slow if the item is not visible+ −
int viewItemCoordinate = 0;+ −
int viewItemIndex = topViewItemIndex;+ −
while (viewItemIndex < viewItems.count()) {+ −
if (viewItemIndex == item)+ −
return viewItemCoordinate;+ −
viewItemCoordinate += itemHeight(viewItemIndex);+ −
++viewItemIndex;+ −
}+ −
// below the last item in the view+ −
Q_ASSERT(false);+ −
return viewItemCoordinate;+ −
} else {+ −
// search the area above the viewport (used for editor widgets)+ −
int viewItemCoordinate = 0;+ −
for (int viewItemIndex = topViewItemIndex; viewItemIndex > 0; --viewItemIndex) {+ −
if (viewItemIndex == item)+ −
return viewItemCoordinate;+ −
viewItemCoordinate -= itemHeight(viewItemIndex - 1);+ −
}+ −
return viewItemCoordinate;+ −
}+ −
}+ −
return 0;+ −
}+ −
+ −
/*!+ −
\internal+ −
Returns the index of the view item at the+ −
given viewport \a coordinate.+ −
+ −
\sa modelIndex()+ −
*/+ −
int QTreeViewPrivate::itemAtCoordinate(int coordinate) const+ −
{+ −
const int itemCount = viewItems.count();+ −
if (itemCount == 0)+ −
return -1;+ −
if (uniformRowHeights && defaultItemHeight <= 0)+ −
return -1;+ −
if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {+ −
if (uniformRowHeights) {+ −
const int viewItemIndex = (coordinate + vbar->value()) / defaultItemHeight;+ −
return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);+ −
}+ −
// ### optimize+ −
int viewItemCoordinate = 0;+ −
const int contentsCoordinate = coordinate + vbar->value();+ −
for (int viewItemIndex = 0; viewItemIndex < viewItems.count(); ++viewItemIndex) {+ −
viewItemCoordinate += itemHeight(viewItemIndex);+ −
if (viewItemCoordinate >= contentsCoordinate)+ −
return (viewItemIndex >= itemCount ? -1 : viewItemIndex);+ −
}+ −
} else { // ScrollPerItem+ −
int topViewItemIndex = vbar->value();+ −
if (uniformRowHeights) {+ −
if (coordinate < 0)+ −
coordinate -= defaultItemHeight - 1;+ −
const int viewItemIndex = topViewItemIndex + (coordinate / defaultItemHeight);+ −
return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);+ −
}+ −
if (coordinate >= 0) {+ −
// the coordinate is in or below the viewport+ −
int viewItemCoordinate = 0;+ −
for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.count(); ++viewItemIndex) {+ −
viewItemCoordinate += itemHeight(viewItemIndex);+ −
if (viewItemCoordinate > coordinate)+ −
return (viewItemIndex >= itemCount ? -1 : viewItemIndex);+ −
}+ −
} else {+ −
// the coordinate is above the viewport+ −
int viewItemCoordinate = 0;+ −
for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {+ −
if (viewItemCoordinate <= coordinate)+ −
return (viewItemIndex >= itemCount ? -1 : viewItemIndex);+ −
viewItemCoordinate -= itemHeight(viewItemIndex);+ −
}+ −
}+ −
}+ −
return -1;+ −
}+ −
+ −
int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const+ −
{+ −
if (!_index.isValid() || viewItems.isEmpty())+ −
return -1;+ −
+ −
const int totalCount = viewItems.count();+ −
const QModelIndex index = _index.sibling(_index.row(), 0);+ −
+ −
+ −
// A quick check near the last item to see if we are just incrementing+ −
const int start = lastViewedItem > 2 ? lastViewedItem - 2 : 0;+ −
const int end = lastViewedItem < totalCount - 2 ? lastViewedItem + 2 : totalCount;+ −
int row = index.row();+ −
for (int i = start; i < end; ++i) {+ −
const QModelIndex &idx = viewItems.at(i).index;+ −
if (idx.row() == row) {+ −
if (idx.internalId() == index.internalId()) {+ −
lastViewedItem = i;+ −
return i;+ −
}+ −
}+ −
}+ −
+ −
// NOTE: this function is slow if the item is outside the visible area+ −
// search in visible items first and below+ −
int t = firstVisibleItem();+ −
t = t > 100 ? t - 100 : 0; // start 100 items above the visible area+ −
+ −
for (int i = t; i < totalCount; ++i) {+ −
const QModelIndex &idx = viewItems.at(i).index;+ −
if (idx.row() == row) {+ −
if (idx.internalId() == index.internalId()) {+ −
lastViewedItem = i;+ −
return i;+ −
}+ −
}+ −
}+ −
// search from top to first visible+ −
for (int j = 0; j < t; ++j) {+ −
const QModelIndex &idx = viewItems.at(j).index;+ −
if (idx.row() == row) {+ −
if (idx.internalId() == index.internalId()) {+ −
lastViewedItem = j;+ −
return j;+ −
}+ −
}+ −
}+ −
// nothing found+ −
return -1;+ −
}+ −
+ −
QModelIndex QTreeViewPrivate::modelIndex(int i, int column) const+ −
{+ −
if (i < 0 || i >= viewItems.count())+ −
return QModelIndex();+ −
+ −
QModelIndex ret = viewItems.at(i).index;+ −
if (column)+ −
ret = ret.sibling(ret.row(), column);+ −
return ret;+ −
}+ −
+ −
int QTreeViewPrivate::firstVisibleItem(int *offset) const+ −
{+ −
const int value = vbar->value();+ −
if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {+ −
if (offset)+ −
*offset = 0;+ −
return (value < 0 || value >= viewItems.count()) ? -1 : value;+ −
}+ −
// ScrollMode == ScrollPerPixel+ −
if (uniformRowHeights) {+ −
if (!defaultItemHeight)+ −
return -1;+ −
+ −
if (offset)+ −
*offset = -(value % defaultItemHeight);+ −
return value / defaultItemHeight;+ −
}+ −
int y = 0; // ### optimize (use spans ?)+ −
for (int i = 0; i < viewItems.count(); ++i) {+ −
y += itemHeight(i); // the height value is cached+ −
if (y > value) {+ −
if (offset)+ −
*offset = y - value - itemHeight(i);+ −
return i;+ −
}+ −
}+ −
return -1;+ −
}+ −
+ −
int QTreeViewPrivate::columnAt(int x) const+ −
{+ −
return header->logicalIndexAt(x);+ −
}+ −
+ −
void QTreeViewPrivate::relayout(const QModelIndex &parent)+ −
{+ −
Q_Q(QTreeView);+ −
// do a local relayout of the items+ −
if (parent.isValid()) {+ −
int parentViewIndex = viewIndex(parent);+ −
if (parentViewIndex > -1 && viewItems.at(parentViewIndex).expanded) {+ −
collapse(parentViewIndex, false); // remove the current layout+ −
expand(parentViewIndex, false); // do the relayout+ −
q->updateGeometries();+ −
viewport->update();+ −
}+ −
} else {+ −
viewItems.clear();+ −
q->doItemsLayout();+ −
}+ −
}+ −
+ −
+ −
void QTreeViewPrivate::updateScrollBars()+ −
{+ −
Q_Q(QTreeView);+ −
QSize viewportSize = viewport->size();+ −
if (!viewportSize.isValid())+ −
viewportSize = QSize(0, 0);+ −
+ −
int itemsInViewport = 0;+ −
if (uniformRowHeights) {+ −
if (defaultItemHeight <= 0)+ −
itemsInViewport = viewItems.count();+ −
else+ −
itemsInViewport = viewportSize.height() / defaultItemHeight;+ −
} else {+ −
const int itemsCount = viewItems.count();+ −
const int viewportHeight = viewportSize.height();+ −
for (int height = 0, item = itemsCount - 1; item >= 0; --item) {+ −
height += itemHeight(item);+ −
if (height > viewportHeight)+ −
break;+ −
++itemsInViewport;+ −
}+ −
}+ −
if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {+ −
if (!viewItems.isEmpty())+ −
itemsInViewport = qMax(1, itemsInViewport);+ −
vbar->setRange(0, viewItems.count() - itemsInViewport);+ −
vbar->setPageStep(itemsInViewport);+ −
vbar->setSingleStep(1);+ −
} else { // scroll per pixel+ −
int contentsHeight = 0;+ −
if (uniformRowHeights) {+ −
contentsHeight = defaultItemHeight * viewItems.count();+ −
} else { // ### optimize (spans or caching)+ −
for (int i = 0; i < viewItems.count(); ++i)+ −
contentsHeight += itemHeight(i);+ −
}+ −
vbar->setRange(0, contentsHeight - viewportSize.height());+ −
vbar->setPageStep(viewportSize.height());+ −
vbar->setSingleStep(qMax(viewportSize.height() / (itemsInViewport + 1), 2));+ −
}+ −
+ −
const int columnCount = header->count();+ −
const int viewportWidth = viewportSize.width();+ −
int columnsInViewport = 0;+ −
for (int width = 0, column = columnCount - 1; column >= 0; --column) {+ −
int logical = header->logicalIndex(column);+ −
width += header->sectionSize(logical);+ −
if (width > viewportWidth)+ −
break;+ −
++columnsInViewport;+ −
}+ −
if (columnCount > 0)+ −
columnsInViewport = qMax(1, columnsInViewport);+ −
if (horizontalScrollMode == QAbstractItemView::ScrollPerItem) {+ −
hbar->setRange(0, columnCount - columnsInViewport);+ −
hbar->setPageStep(columnsInViewport);+ −
hbar->setSingleStep(1);+ −
} else { // scroll per pixel+ −
const int horizontalLength = header->length();+ −
const QSize maxSize = q->maximumViewportSize();+ −
if (maxSize.width() >= horizontalLength && vbar->maximum() <= 0)+ −
viewportSize = maxSize;+ −
hbar->setPageStep(viewportSize.width());+ −
hbar->setRange(0, qMax(horizontalLength - viewportSize.width(), 0));+ −
hbar->setSingleStep(qMax(viewportSize.width() / (columnsInViewport + 1), 2));+ −
}+ −
}+ −
+ −
int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const+ −
{+ −
executePostedLayout();+ −
int x = pos.x();+ −
int column = header->logicalIndexAt(x);+ −
if (column != 0)+ −
return -1; // no logical index at x+ −
+ −
int viewItemIndex = itemAtCoordinate(pos.y());+ −
QRect returning = itemDecorationRect(modelIndex(viewItemIndex));+ −
if (!returning.contains(pos))+ −
return -1;+ −
+ −
return viewItemIndex;+ −
}+ −
+ −
QRect QTreeViewPrivate::itemDecorationRect(const QModelIndex &index) const+ −
{+ −
Q_Q(const QTreeView);+ −
if (!rootDecoration && index.parent() == root)+ −
return QRect(); // no decoration at root+ −
+ −
int viewItemIndex = viewIndex(index);+ −
if (viewItemIndex < 0 || !hasVisibleChildren(viewItems.at(viewItemIndex).index))+ −
return QRect();+ −
+ −
int itemIndentation = indentationForItem(viewItemIndex);+ −
int position = header->sectionViewportPosition(0);+ −
int size = header->sectionSize(0);+ −
+ −
QRect rect;+ −
if (q->isRightToLeft())+ −
rect = QRect(position + size - itemIndentation, coordinateForItem(viewItemIndex),+ −
indent, itemHeight(viewItemIndex));+ −
else+ −
rect = QRect(position + itemIndentation - indent, coordinateForItem(viewItemIndex),+ −
indent, itemHeight(viewItemIndex));+ −
QStyleOption opt;+ −
opt.initFrom(q);+ −
opt.rect = rect;+ −
return q->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, q);+ −
}+ −
+ −
QList<QPair<int, int> > QTreeViewPrivate::columnRanges(const QModelIndex &topIndex,+ −
const QModelIndex &bottomIndex) const+ −
{+ −
const int topVisual = header->visualIndex(topIndex.column()),+ −
bottomVisual = header->visualIndex(bottomIndex.column());+ −
+ −
const int start = qMin(topVisual, bottomVisual);+ −
const int end = qMax(topVisual, bottomVisual);+ −
+ −
QList<int> logicalIndexes;+ −
+ −
//we iterate over the visual indexes to get the logical indexes+ −
for (int c = start; c <= end; c++) {+ −
const int logical = header->logicalIndex(c);+ −
if (!header->isSectionHidden(logical)) {+ −
logicalIndexes << logical;+ −
}+ −
}+ −
//let's sort the list+ −
qSort(logicalIndexes.begin(), logicalIndexes.end());+ −
+ −
QList<QPair<int, int> > ret;+ −
QPair<int, int> current;+ −
current.first = -2; // -1 is not enough because -1+1 = 0+ −
current.second = -2;+ −
for(int i = 0; i < logicalIndexes.count(); ++i) {+ −
const int logicalColumn = logicalIndexes.at(i);+ −
if (current.second + 1 != logicalColumn) {+ −
if (current.first != -2) {+ −
//let's save the current one+ −
ret += current;+ −
}+ −
//let's start a new one+ −
current.first = current.second = logicalColumn;+ −
} else {+ −
current.second++;+ −
}+ −
}+ −
+ −
//let's get the last range+ −
if (current.first != -2) {+ −
ret += current;+ −
}+ −
+ −
return ret;+ −
}+ −
+ −
void QTreeViewPrivate::select(const QModelIndex &topIndex, const QModelIndex &bottomIndex,+ −
QItemSelectionModel::SelectionFlags command)+ −
{+ −
Q_Q(QTreeView);+ −
QItemSelection selection;+ −
const int top = viewIndex(topIndex),+ −
bottom = viewIndex(bottomIndex);+ −
+ −
const QList< QPair<int, int> > colRanges = columnRanges(topIndex, bottomIndex);+ −
QList< QPair<int, int> >::const_iterator it;+ −
for (it = colRanges.begin(); it != colRanges.end(); ++it) {+ −
const int left = (*it).first,+ −
right = (*it).second;+ −
+ −
QModelIndex previous;+ −
QItemSelectionRange currentRange;+ −
QStack<QItemSelectionRange> rangeStack;+ −
for (int i = top; i <= bottom; ++i) {+ −
QModelIndex index = modelIndex(i);+ −
QModelIndex parent = index.parent();+ −
QModelIndex previousParent = previous.parent();+ −
if (previous.isValid() && parent == previousParent) {+ −
// same parent+ −
if (qAbs(previous.row() - index.row()) > 1) {+ −
//a hole (hidden index inside a range) has been detected+ −
if (currentRange.isValid()) {+ −
selection.append(currentRange);+ −
}+ −
//let's start a new range+ −
currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));+ −
} else {+ −
QModelIndex tl = model->index(currentRange.top(), currentRange.left(),+ −
currentRange.parent());+ −
currentRange = QItemSelectionRange(tl, index.sibling(index.row(), right));+ −
}+ −
} else if (previous.isValid() && parent == model->index(previous.row(), 0, previousParent)) {+ −
// item is child of previous+ −
rangeStack.push(currentRange);+ −
currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));+ −
} else {+ −
if (currentRange.isValid())+ −
selection.append(currentRange);+ −
if (rangeStack.isEmpty()) {+ −
currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));+ −
} else {+ −
currentRange = rangeStack.pop();+ −
index = currentRange.bottomRight(); //let's resume the range+ −
--i; //we process again the current item+ −
}+ −
}+ −
previous = index;+ −
}+ −
if (currentRange.isValid())+ −
selection.append(currentRange);+ −
for (int i = 0; i < rangeStack.count(); ++i)+ −
selection.append(rangeStack.at(i));+ −
}+ −
q->selectionModel()->select(selection, command);+ −
}+ −
+ −
QPair<int,int> QTreeViewPrivate::startAndEndColumns(const QRect &rect) const+ −
{+ −
Q_Q(const QTreeView);+ −
int start = header->visualIndexAt(rect.left());+ −
int end = header->visualIndexAt(rect.right());+ −
if (q->isRightToLeft()) {+ −
start = (start == -1 ? header->count() - 1 : start);+ −
end = (end == -1 ? 0 : end);+ −
} else {+ −
start = (start == -1 ? 0 : start);+ −
end = (end == -1 ? header->count() - 1 : end);+ −
}+ −
return qMakePair<int,int>(qMin(start, end), qMax(start, end));+ −
}+ −
+ −
bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const+ −
{+ −
Q_Q(const QTreeView);+ −
if (model->hasChildren(parent)) {+ −
if (hiddenIndexes.isEmpty())+ −
return true;+ −
if (q->isIndexHidden(parent))+ −
return false;+ −
int rowCount = model->rowCount(parent);+ −
for (int i = 0; i < rowCount; ++i) {+ −
if (!q->isRowHidden(i, parent))+ −
return true;+ −
}+ −
if (rowCount == 0)+ −
return true;+ −
}+ −
return false;+ −
}+ −
+ −
void QTreeViewPrivate::rowsRemoved(const QModelIndex &parent,+ −
int start, int end, bool after)+ −
{+ −
Q_Q(QTreeView);+ −
// if we are going to do a complete relayout anyway, there is no need to update+ −
if (delayedPendingLayout) {+ −
_q_rowsRemoved(parent, start, end);+ −
return;+ −
}+ −
+ −
const int parentItem = viewIndex(parent);+ −
if ((parentItem != -1) || (parent == root)) {+ −
+ −
const uint childLevel = (parentItem == -1)+ −
? uint(0) : viewItems.at(parentItem).level + 1;+ −
Q_UNUSED(childLevel); // unused in release mode, used in assert below+ −
+ −
const int firstChildItem = parentItem + 1;+ −
int lastChildItem = firstChildItem + ((parentItem == -1)+ −
? viewItems.count()+ −
: viewItems.at(parentItem).total) - 1;+ −
+ −
const int delta = end - start + 1;+ −
+ −
int removedCount = 0;+ −
for (int item = firstChildItem; item <= lastChildItem; ) {+ −
Q_ASSERT(viewItems.at(item).level == childLevel);+ −
const QModelIndex modelIndex = viewItems.at(item).index;+ −
//Q_ASSERT(modelIndex.parent() == parent);+ −
const int count = viewItems.at(item).total + 1;+ −
if (modelIndex.row() < start) {+ −
// not affected by the removal+ −
item += count;+ −
} else if (modelIndex.row() <= end) {+ −
// removed+ −
viewItems.remove(item, count);+ −
removedCount += count;+ −
lastChildItem -= count;+ −
} else {+ −
if (after) {+ −
// moved; update the model index+ −
viewItems[item].index = model->index(+ −
modelIndex.row() - delta, modelIndex.column(), parent);+ −
}+ −
item += count;+ −
}+ −
}+ −
+ −
updateChildCount(parentItem, -removedCount);+ −
if (after) {+ −
q->updateGeometries();+ −
viewport->update();+ −
} else {+ −
//we have removed items: we should at least update the scroll bar values.+ −
// They are used to determine the item geometry.+ −
updateScrollBars();+ −
}+ −
} else {+ −
// If an ancestor of root is removed then relayout+ −
QModelIndex idx = root;+ −
while (idx.isValid()) {+ −
idx = idx.parent();+ −
if (idx == parent) {+ −
doDelayedItemsLayout();+ −
break;+ −
}+ −
}+ −
}+ −
_q_rowsRemoved(parent, start, end);+ −
+ −
QSet<QPersistentModelIndex>::iterator it = expandedIndexes.begin();+ −
while (it != expandedIndexes.constEnd()) {+ −
if (!it->isValid())+ −
it = expandedIndexes.erase(it);+ −
else+ −
++it;+ −
}+ −
it = hiddenIndexes.begin();+ −
while (it != hiddenIndexes.constEnd()) {+ −
if (!it->isValid())+ −
it = hiddenIndexes.erase(it);+ −
else+ −
++it;+ −
}+ −
}+ −
+ −
void QTreeViewPrivate::updateChildCount(const int parentItem, const int delta)+ −
{+ −
if ((parentItem != -1) && delta) {+ −
int level = viewItems.at(parentItem).level;+ −
int item = parentItem;+ −
do {+ −
Q_ASSERT(item >= 0);+ −
for ( ; int(viewItems.at(item).level) != level; --item) ;+ −
viewItems[item].total += delta;+ −
--level;+ −
} while (level >= 0);+ −
}+ −
}+ −
+ −
+ −
void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order)+ −
{+ −
model->sort(column, order);+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)+ −
{+ −
#ifndef QT_NO_ACCESSIBILITY+ −
if (QAccessible::isActive()) {+ −
int entry = visualIndex(current) + 1;+ −
if (header())+ −
++entry;+ −
QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);+ −
}+ −
#endif+ −
QAbstractItemView::currentChanged(current, previous);+ −
+ −
if (allColumnsShowFocus()) {+ −
if (previous.isValid()) {+ −
QRect previousRect = visualRect(previous);+ −
previousRect.setX(0);+ −
previousRect.setWidth(viewport()->width());+ −
viewport()->update(previousRect);+ −
}+ −
if (current.isValid()) {+ −
QRect currentRect = visualRect(current);+ −
currentRect.setX(0);+ −
currentRect.setWidth(viewport()->width());+ −
viewport()->update(currentRect);+ −
}+ −
}+ −
}+ −
+ −
/*!+ −
\reimp+ −
*/+ −
void QTreeView::selectionChanged(const QItemSelection &selected,+ −
const QItemSelection &deselected)+ −
{+ −
#ifndef QT_NO_ACCESSIBILITY+ −
if (QAccessible::isActive()) {+ −
// ### does not work properly for selection ranges.+ −
QModelIndex sel = selected.indexes().value(0);+ −
if (sel.isValid()) {+ −
int entry = visualIndex(sel) + 1;+ −
if (header())+ −
++entry;+ −
QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);+ −
}+ −
QModelIndex desel = deselected.indexes().value(0);+ −
if (desel.isValid()) {+ −
int entry = visualIndex(desel) + 1;+ −
if (header())+ −
++entry;+ −
QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);+ −
}+ −
}+ −
#endif+ −
QAbstractItemView::selectionChanged(selected, deselected);+ −
}+ −
+ −
int QTreeView::visualIndex(const QModelIndex &index) const+ −
{+ −
Q_D(const QTreeView);+ −
d->executePostedLayout();+ −
return d->viewIndex(index);+ −
}+ −
+ −
QT_END_NAMESPACE+ −
+ −
#include "moc_qtreeview.cpp"+ −
+ −
#endif // QT_NO_TREEVIEW+ −